Solucion de Práctico 8 - Facultad de Ingeniería

Anuncio
P2-2016
Solución Práctico 8 - Colecciones: TADs tabla y tabla de dispersión
Objetivos
Introducir la estructura de datos tabla de dispersión o hash table, y analizar que TADs pueden implementarse con ésta.
Trabajar con el TAD tabla, desarrollando y analizando implementaciones.
Usar el TAD tabla en la resolución de problemas simples y complejos.
Ejercicio 1
Suponga que se tiene una tabla de dispersión abierta de 10 cubetas (buckets), con la función de
dispersión h(i) = i % 10 Muestre la tabla de dispersión resultante de insertar 4371, 1323, 6173, 4199, 4344,
9679, 1989.
(a) Muestre la tabla de dispersión resultante de insertar 4371, 1323, 6173, 4199, 4344, 9679, 1989.
(b) Repita la parte anterior para una tabla de dispersión cerrada, usando resolución lineal de colisiones.
(c) ¿Cómo se comporta ante la secuencia de inserciones de la parte a) una tabla de 7 cubetas con la
función de dispersión h(i) = i % 7?
Solución propuesta para el ejercicio 1
Parte a
Calculemos h(k) = k(10) para:
h(4371) = 1
h(1323)=h(6173) = 3
h(4344) = 4
h(4199)=h(9679)=h(1989)=9
Parte b
Esta parte trata sobre dispersión cerrada. A diferencia de la anterior, si ocurre una colisión, se intenta
buscar celdas alternativas hasta encontrar una vacía. Formalmente se puede expresar de la siguiente
manera:
di (k) = (h(k) + f (i ))mod N
donde h es la función de dispersión, f es una función que determina la estrategia de resolución de las
colisiones e i se va incrementando hasta encontrar un lugar libre con f (i ) = i.
Para este ejercicio es importante el orden de la inserción de los elementos:
d0 (4371) = 1. La celda está vacía, por lo que se puede insertar.
d0 (1323) = 3. La celda está vacía, por lo que se puede insertar.
d0 (6173) = 3. La celda está ocupada por lo que se tiene una colisión. Por resolución lineal, d1 (6173) =
4 y está libre, entonces se inserta en dicha celda.
d0 (4199) = 9. La celda está vacía, por lo que se puede insertar.
d0 (4344) = 4. La celda está ocupada por lo que se tiene una colisión. Por resolución lineal, d1 (4344) =
5, se inserta en la celda 5.
d0 (9679) = 9. La celda está ocupada por lo que se tiene una colisión. Por resolución lineal y teniendo
en cuenta que es cíclico, se inserta en la celda 0.
Instituto de Computación - Facultad de Ingeniería - UdelaR
Página 1 de 7
d0 (1989) = 9. La celda está ocupada por lo que se tiene una colisión. Por resolución lineal, se intenta
insertar en la celda 0 pero también está ocupada, ergo se sigue buscando en la siguiente posición
libre. La celda 1 también está ocupada, por lo que se inserto en la celda 2.
Finalmente, la tabla de dispersión queda de la siguiente forma:
d(4371) → 1
d(1323) → 3
d(6173) → 4
d(4199) → 9
d(4344) → 5
d(9679) → 0
d(1989) → 2
Parte c
Aquí se pide trabajar con la función de dispersión abierta módulo 7. Primeramente, se calcula la
función h:
h(4371) = 3
h(1323) = 0
h(6173) = h(4199) = 6
h(4344) = 4
h(9679) = 5
h(1989) = 1
Nótese que con esta función de dispersión se obtiene una sola colisión, mientrás que en la parte (a) se
obtuvieron tres colisiones. Esto sucede porque en este ejemplo se eligió un módulo primo.
Instituto de Computación - Facultad de Ingeniería - UdelaR
Página 2 de 7
Ejercicio 4
Se quiere implementar una tabla no acotada, cuyo dominio son las cadenas de caracteres y su recorrido
los enteros, utilizando una tabla de dispersión abierta, donde la resolución de colisiones usa separate
chaining (resolución de colisiones usando listas).
(a) ¿Qué necesita para estimar el tamaño de la tabla de dispersión? ¿Cómo hace esta estimación?
(b) Analice diferentes opciones para la función de hash a utilizar, analizando pros y contras.
(c) Implemente
Solución propuesta para el ejercicio 4
Parte a
Para estimar el tamaño de la tabla, se requiere conocer el problema concreto en que se va a usar el
hash. Por ejemplo, si se busca guardar las palabras de un diccionario, se se debe de obtener la cantidad
de palabras.
Parte b
Aquí se centra la discusión en la elección de la función de hash donde la entrada son caracteres y la
salida enteros positivos.
Primer opción
Dado que los caracteres se representan como enteros en la tabla ASCII, la opción más intuitiva y trivial
es sumar estos valores.
1
2
3
4
5
6
7
unsigned long hash_texto ( const char * t , unsigned int N ) {
unsigned long res = 0;
for ( unsigned int i = 0; i < strlen ( t ); i ++)
res = res + t [ i ]; // aqui se suma el ordinal en la tabla ASCII del caracter
return res % N ;
}
Esta función no logra distribuir de forma equitativa las claves. El ordinal de los caracteres está acotado
por 127 y si suponemos que las claves están acotadas por una longitud m, la función adoptará valores
entre 0 y 127 ∗ M. De esta forma, si la tabla de dispersión es muy grande los valores de las claves no
quedarán distribuidos de forma equitativa.
Segunda opción
Tomar una cantidad acotada de caracteres de la entrada (se rellena con blancos si es menor la longitud)
y en base a eso calcular el entero de forma polinomial. Por ejemplo, tomemos los 3 primeros caracteres. El
idioma español tiene 27 letras mas el blanco, utilizamos el coeficiente 28.
De esta forma podemos escribir el siguiente polinomio donde x, y, z son tres caracteres:
p( x, y, z) = ord( x ) + 28 ∗ ord(y) + 282 ∗ ord(z)
y en código (t ya viene de longitud al menos 3):
1
2
3
4
5
6
unsigned long hash_texto ( const char * t , unsigned int N ) {
unsigned long res = 0;
res = t [0] + t [1]*28 + t [2]*784;
return res % N ;
}
Supongamos que N es lo suficientemente grande (por ejemplo 10007), puede observarse que, intuitivamente, para caracteres aleatorios y uniformes, queda razoblemente homogénea la distribución en la tabla
de dispersión. Sin embargo, no cualquier trinomio tiene la misma probabilidad de ocurrencia. Aunque hay
273 = 19683 posibles combinaciones de tres letras, se puede estimar que solo alrededor de 3 mil ocurren
(esto aplica para inglés). Ergo, si el tamaño de la tabla es 10007 solo la tercera parte estará utilizada.
Instituto de Computación - Facultad de Ingeniería - UdelaR
Página 3 de 7
Tercer opción
En consecuencia, generalicemos la noción anterior para utilizar todas las letras de la cadena de caracteres. En vez de utilizar 28, por simplicidad se utilizará 32, dado que multiplicar por 32 se convierte en
desplazar 5 bits (32 = 25 ). Entonces:
p(t) = sumiN=−0 1 t[ N − i ] ∗ 32i
Aplicando la regla de Horner:
1
2
3
4
5
6
7
8
unsigned long hash_texto ( const char * t , unsigned int N ) {
unsigned long res = 0;
for ( unsigned int i = 0; i < strlen ( t ); i ++)
// aqui se suma el ordinal en la tabla ASCII del caracter
res = ( res << 5) + res + t [ i ];
// res << 5 desplaza ‘res ‘ 5 bits hacia la derecha lo cual es una forma
// eficiente de calcular res * 2^5
9
return res % N ;
10
11
}
Esta función se puede suponer razonablemente distribuida y con bajo costo computacional para calcular.
Instituto de Computación - Facultad de Ingeniería - UdelaR
Página 4 de 7
Ejercicio 7
Se desea utilizar una Tabla para guardar información de contactos telefónicos en forma de correspondencias entre Nombres y Teléfonos, donde ambos se pueden representar como cadenas de caracteres.
(a) Especifique un TAD TablaContactos con operaciones constructoras, selectoras/destructoras y predicados, que permita guardar una cantidad no conocida a priori de Nombres y Teléfonos. Además de
las operaciones habituales de Tabla se necesita contar con la operación ListarTodosOrdenados que
recibe una TablaContactos e imprime todos los contactos en orden alfabético por Nombre, en forma
de líneas que contienen cada una Nombre y Teléfono.
(b) Implemente el TAD TablaContactos teniendo en cuenta que las operaciones de inserción deben ejecutarse en orden log2 (n) en caso promedio, siendo n la cantidad de elementos que contiene la TablaContactos. Provea una representación para este TAD y el código de las operaciones constructoras y
de ListarTodosOrdenados. Omita el código del resto de las operaciones del TAD. Se permite utilizar
las operaciones <, >, = y <>para comparar cadenas de caracteres.
(c) Considere que se quiere modificar el TAD TablaContactos, agregando la funcionalidad de discado
rápido que permita almacenar hasta K referencias a contactos. Esta funcionalidad se implementa
mediante las siguientes operaciones:
1
2
/* Inicializa el discado rapido en la TablaC ontact os T */
void I n i c i a l i z a r L D R a p i d o ( Tabl aConta ctos & T );
3
4
5
6
/* Inserta en la posicion j <= K del discado rapido de T una referencia al contacto
ya existente en T con nombre = nomContacto */
void I n s e r ta r L D R a p i d o ( Tab laCont actos &T , char * nomContacto , unsigned int j );
7
8
9
10
/* Imprime el telefono y nombre del contacto almacenado en la
posicion j <= K del discado rapido de T */
void BusquedaRapi da ( TablaC ontact os &T , unsigned int j );
Solución propuesta para el ejercicio 7
Parte a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Tab laCont actos CreoTabla ();
/* retorna la tabla vac í a */
void Insertar ( char * d , char * r , Tab laCont actos & T );
/* Define T ( d ) = r , i n d e p e n d i e n t e m e n t e de que T ( d ) estuviera ya definido */
bool EsVacia ( Tabl aConta ctos T );
/* retorna TRUE sii T es vac í a */
bool EstaDefinida ( Tab laCont actos T , char * d );
/* retorna TRUE sii T est á definida para el TELEFONO d */
char * Recuperar ( char * d , Tabla Contac tos & T );
/* retorna r si EstaDefinida ( d )== TRUE y r = T ( d ) */
void Borrar ( char * d , Tabla Contac tos & T );
/* elimina la co rr e sp on d en c ua que vincula al TELEFONO d
en T . Luego de borrar , EstaDefinida (T , d ) es FALSE */
void L i s t a r T o d o s O r d e n a d o s ( Tab laCont actos & T );
/* imprime todos los contactos en orden alfab é tico por
Nombre , en forma de l í neas que contienen
cada una : Nombre , Tel é fono . */
Instituto de Computación - Facultad de Ingeniería - UdelaR
Página 5 de 7
Parte b
1
2
3
4
struct TCNodo {
char * nom , * tel ;
TCNodo * izq , * der ;
};
5
6
typedef TCNodo * T ablaCo ntacto s ;
7
8
9
10
11
Tab laCont actos CreoTabla ()
{
return NULL ;
};
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/* Define T ( d ) = r , i n d e p e n d i e n t e m e n t e de que T ( d ) estuviera ya definido */
void Insertar ( char * d , char * r , Tab laCont actos & T )
{
if ( T == NULL ) {
T = new TCNodo ;
T - > nom = d ;
T - > tel = r ;
T - > izq = NULL ;
T - > der = NULL ;
} else if ( strcmp (T - > nom , d )==0) {
T - > tel = r ;
} else if ( strcmp (T - > nom , d ) > 0)
Insertar (d , r , T - > izq );
else
Insertar (d , r , T - > der );
29
30
};
31
32
33
34
35
36
37
38
39
40
41
42
/* Imprime todos los contactos en orden alfab é tico por Nombre , en forma de
l í neas que contienen cada una : Nombre , Tel é fono . */
void L i s t a r T o d o s O r d e n a d o s ( Tab laCont actos T )
{
if ( T != NULL ) {
L i s t a r T o d o s O r d e n a d o s (T - > izq );
printf ( " %s , %s \ n " , T - > nom ,T - > tel );
L i s t a r T o d o s O r d e n a d o s (T - > der );
}
};
Parte c
Se crea una estructura, que sea un cabezal, que tenga el arbol anterior y ademas un arreglo de largo K de
punteros a nodos del árbol. De esta forma dada una posición j con 1<=j<=k se puede obtener en O(1) los
datos del registro en esa posición.
Instituto de Computación - Facultad de Ingeniería - UdelaR
Página 6 de 7
1
2
3
4
struct TCNodo {
char * nom , * tel ;
TCNodo * izq , * der ;
};
5
6
7
8
9
struct TablaConta ctos {
TCNodo * tablaC ;
TCNodo * busRap [K -1];
};
10
11
typedef TCNodo * PTC ;
12
13
14
15
16
17
18
19
/* Pre : T es una T ablaCo ntacto s y T ( n ). Pos : inserta una referencia a NIL para todos
los lugares de la lista de discado r á pido i n d e p e n d i e n t e m e n t e de lo que hubiera antes . */
void I n i c i a l i z a r L D R a p i d o ( Tabl aConta ctos T )
{
for ( int j =0; j < K ; j ++)
T . busRap [ j ]= NULL ;
};
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/* Pre : j <= K , T es una Tab laCont actos y T ( n ) esta definido en tc . Pos : inserta una
referencia a n y T ( n ) en el lugar j de la lista de discado r á pido i n d e p e n d i e n t e m e n t e de
lo que hubiera antes . */
void I n s e r t a r L D R a p i d o ( Tab laCont actos T , char * n , int j )
{
PTC tc ;
tc = T . tablaC ;
while (! strcmp ( tc - > nom , n )) {
if ( strcmp ( tc - > nom , n ) >0)
tc = tc - > der ;
else
tc = tc - > izq ;
}
T . busRap [ j ]= tc ;
};
37
38
39
40
41
42
43
44
45
/* Pre : j <= K y T es una Tab laCont actos . Pos : imprime el Nombre y Tel é fono de la
persona ubicada en el lugar j de la lista de discado r á pido o NIL en caso de que j
no est é definido en la lista de discado r á pido . */
void Busque daRapi da ( TablaC ontact os T , int j )
{
printf ( " %s , %s \ n " , T . busRap [ j ] - > nom , T . busRap [ j ] - > tel );
};
Instituto de Computación - Facultad de Ingeniería - UdelaR
Página 7 de 7
Descargar