Professional Documents
Culture Documents
HASHING
Índice
1. Hashing 2
1.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2. Resolviendo colisiones de hasheo por direccionamiento abierto . . . 3
1.3. Borrando elementos desde una tabla de hasheo . . . . . . . . . . . . 6
1.4. Tablas de hasheo encadenadas . . . . . . . . . . . . . . . . . . . . . 6
1.5. Eficiencia en los métodos de rehasheo . . . . . . . . . . . . . . . . . 7
1.6. Reordenamiento de la tabla de hasheo . . . . . . . . . . . . . . . . 9
1.7. Método de Brent . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.8. Árboles binarios de hasheo . . . . . . . . . . . . . . . . . . . . . . . 10
3. Bibliografı́a 14
1
1 Hashing
1. Hashing
Hash, x. There is no definition for this word - nobody knows what hash is”
AMBROSE BIERCE (The Devil’s Dictionaty, 1906)
1.1. Introducción
Supongamos que tenemos un registro que se encuentra guardado en una tabla
con una correspondiente clave. Es necesario para operar sobre ese dato, tener que
analizar cierto número de estos registros antes de obtener el que estamos buscan-
do. Para mejorar nuestro rendimiento y llevarlo a un nivel óptimo nos convendrı́a
analizar que distribución en la tabla y que método de búsqueda podrı́amos usar
para no realizar comparaciones innecesarias.
Si queremos que cada clave sea devuelta en un solo acceso, entonces el lugar
de cada registro en la tabla solo puede depender de una sola clave y no depender
de la localización de las otras claves como ocurre en un árbol. El método mas
eficiente de organizar tal tabla, es un arreglo donde cada registro es almacenado a
una distancia especifica desde la base de este.
Supongamos ahora que una empresa que tiene 1000 partes diferentes en su
stock y cada parte tiene un registro diferente. Entonces un arreglo indexado del 0
al 1000 es suficiente para poder guardar todo el archivo de stock. Los últimos tres
2
1.2 Resolviendo colisiones de hasheo por direccionamiento abierto
números del número de registro de las partes nos va a servir como ı́ndice de los los
registros en el arreglo.
El método anterior tiene una falla. Supongamos que dos claves, c1 y c2, son
aquellas tal que h(c1) y h(c2) son iguales. Claramente si c1 esta en la tabla, cuan-
do se queramos incluir c2 se intentara acceder al el registro donde se encuentra c1.
Dos registros no pueden ocupar la misma posición. Cuando esto ocurre se llama
colisión de hasheo (hash collision) o hash clash.
Hay dos formas básicas de resolver esto. La primera es llamada rehasheo (re-
hashing), que incluye usar una función de hasheo secundaria en la clave de hasheo
del registro. La segunda, llamada encadenamiento (chaining), construye una lista
enlazada de todos los elementos cuyas claves hashean en el mismo ı́ndice.
Mas allá de esto, cabe decir que una buena función de hasheo es aquella que
minimizan las colisiones y distribuye de forma uniforme los registros. Dejar es-
pacios en blanco en un arreglo es ineficiente en termino de espacio, pero reduce
sensiblemente la necesidad de resolver los hash clashes y, por lo tanto, gana en ve-
locidad. Más adelante mostraremos las diferencias de rendimiento entre una tabla
medianamente llena y una tabla llena; este es un tema no menor que requiere bas-
tante análisis.
Vale también destacar que el hecho de que hashing permita el acceso directo a
los elementos de la tabla posee una falla muy seria. Los elementos de la tabla de
hasheo no son guardados en forma secuencial por claves y no hay métodos prácti-
cos para obtener las claves en alguna secuencia dada.
3
1.2 Resolviendo colisiones de hasheo por direccionamiento abierto
Notemos que puede ocurrir que en el rehasheo nunca se encuentre una posición
disponible por lo que se seguirı́a intentando calcular sin ningún resultado infini-
tamente. Esto puede pasar por dos motivos. Primero que la tabla este completa,
lo cual es fácilmente salvable contando las veces que se aplica la función y com-
parando contra el total de elementos de la tabla. Segundo existen posiciones libres
en la tabla pero la función de rehasheo nunca las toca. Consideremos la situación
donde las los impares están llenos, los pares vacı́os, y la función de rehasheo solo
toca los impares.
De hecho las funciones que dependen exclusivamente de los ı́ndices ha ser re-
hasheados causan agrupamiento primario.
4
1.2 Resolviendo colisiones de hasheo por direccionamiento abierto
5
1.3 Borrando elementos desde una tabla de hasheo
Una solución posible a este problema es que marquemos a este registro como
borrado y no como vacı́o. Entonces la búsqueda solo se detendrá si encuentra un
registro vacı́o y no uno borrado. Pero esta solución solo será viable si nos encon-
tramos con pocos registros borrados, ya que si este no fuera el caso, las búsquedas
tocarı́an muchas posiciones marcadas como borradas antes de concluir.
6
1.5 Eficiencia en los métodos de rehasheo
2∗tamtabla
2∗tamtabla−n+2
Para una búsqueda no exitosa 0,5 / (1 - fc) 2 + 0,5 para tamaños de tablas
grandes. Cuando la tabla esta llena el número máximo de comprobaciones a re-
alizar es (tamtabla + 1) / 2.
Para tablas pequeñas el número es razonable pero para tablas grandes este
puede mejorarse eliminando el agrupamiento primario seteando rh(i,clave) a rh(i
+ hkey) % tamtabla como definimos antes o usando rehasheo cuadrático. Esto
deja el número de comparaciones en aproximadamente 1 - log (1 - fc) - fc/2
para búsquedas no exitosas. Para tablas completas las búsquedas exitosas son en
(tamtabla + 1) y las no exitosas se mantienen en orden (tamtabla + 1) / 2.
7
1.5 Eficiencia en los métodos de rehasheo
Estos datos indican que el hasheo lineal debe ser evitado para tablas que están
a más del 75 % de su capacidad, especialmente si las búsquedas son frecuentes,
ya que el agrupamiento primario tiene un significativo impacto en los tiempos
de búsquedas. En cambio el agrupamiento secundario sólo adiciona un 0,5 com-
probaciones al número promedio requerido. Dado el hecho de que el doble hasheo
requiere cálculos adicionales para determinar h2(clave), tal vez sea preferible acep-
tar la media comprobaciones extra y usar rh(i,clave) = (i + hclave) % tamtabla.
Otra técnica que puede mejorar el método de rehasheo lineal es rehasheo lin-
eal de secuencias divididas. Este método se basa en que cuando encontramos que
h(clave) está ocupado, comparamos clave con la clave de kh encontrada en la
posición h(clave). Si kh < h(clave), usamos la función de rehasheo i + c1 ; si
kh > h(clave), usamos i + c2, como función de rehasheo. Esta técnica reduce
el número de comprobaciones en búsquedas exitosas en más de un 50 % y en
búsquedas no exitosas en mas de 80 %. De cualquier forma las formas de rehasheo
no lineales son todavı́a mejores. Las tablas anteriores también demuestran que el
gran gasto que conlleva tener tablas casi llenas para una búsqueda no exitosa.
Las inserciones también requieren el mismo número de comparaciones que las
búsquedas no exitosas. Cuando la tabla esta casi llena las inserciones se aproxi-
man a una búsqueda secuencial y es peor que una la inserción en un árbol.
8
1.6 Reordenamiento de la tabla de hasheo
Respecto de las tablas resueltas mediante chaining hay que decir que hay un
detalle importante para comenzar; en las tablas de direccionamiento, fc es siempre
menor a uno, dado que surge de la división del número de claves por el tamaño de
la tabla. En el método de encadenamiento fc puede ser mayor que uno dado que
no existen restricciones al número de claves a almacenar.
Es evidente que las tablas encadenadas tienen buen rendimiento aún cuando el
factor de carga es grande, cosa que no ocurre en las tablas de hashing de dirección
abierta.
Hay que decir algo muy importante acerca de la técnica de hashing, y esto es
que en ningún momento el tiempo de búsqueda depende de la cantidad de claves
sino que depende exclusivamente del fc; es decir, por más que en una tabla hayan
millones de entradas, si el tamaño de la tabla es suficientemente grande y la función
de hasheo es la adecuada, el tiempo de búsqueda estará siempre acotado y será el
mismo que para una tabla con sólo 10 entradas y con un tamaño proporcional al
anterior. Esto quiere decir que una tabla de hasheo permite realizar de búsquedas
en un orden O(1) dado que no depende del tamaño de la entrada.
El primer método descubierto por Amble y Knuth, dice que una serie de ele-
mentos que hashean en un mismo elemento se mantienen en orden descendiente
de claves. Cuando buscamos por un elemento no es necesario rehashear repetida-
mente hasta obtener un elemento vacı́o sino que en cuanto obtenemos un elemento
cuya clave es menor a la clave de búsqueda entonces sabemos que el elemento no
se encuentra en la tabla de hasheo. Cuando insertamos un elemento en la tabla, si
accedemos a una clave que es menor a nuestra clave, entonces remplazamos nuestra
clave, la clave a insertar, por la menor, que se encuentra en la tabla, y continuamos
el proceso de inserción con la clave menor. Una tabla ordenada de esta forma se
9
1.7 Método de Brent
10
1.8 Árboles binarios de hasheo
Una ves que el árbol a sido construido, las claves de los caminos desde la raı́z
hasta los últimos nodos son reordenados en la tabla de hasheo. emphi es inicializa-
do en la posición del ultimo nodo del árbol. Luego si adj(i) no es cero, clave(adj(i))
y su registro asociado es movido desde la tabla[(ı́ndice(adj(i)))] a tabla[ı́ndice(i)]
y i es reseteado a adj(i). Este proceso es repetido hasta que adj(i) es -1(menos
uno), en cuyo caso clave y registro son insertado en tabla[ı́ndice(i)] y la inserción
esta completa.
Vemos que todo el algoritmo depende de la función que encuentra el adj(i). Es-
ta puede derivar del siguiente método para que se realice rápidamente: Encontrar
la representación binaria de i + 1. Borrar cualquier TRAILING de 0(cero) bits y
1 bits precediéndolos. Restar 1(uno) del resultado del número binario a obtener su
adj(i). Por ejemplo : la representación de 11(once) + 1(uno) es 1100. removiendo el
TRAILING de 100 se llega a 1; entonces adj(11) = 0, adj(17) = 3, adj(14) = 6, etc.
Gonnet y Munro obtienen resultados que se son más cercanos al óptimo que el
algoritmo de Brent. De cualquier forma no son óptimos, ya que los elementos de
11
2 El Contenedor Asociativo de Hasheo de la Biblioteca Estándar de Plantillas
(STL)
tabla de hasheo solo pueden ser reordenados moviéndolos a posiciones mas altas
de la tabla y nunca a las posiciones más bajas. Cuando la tabla esta cargada en
factor 0,9, el árbol binario requiere 1,75 comprobaciones por obtención (en Brent
1.80), con un factor de 0,95 requiere 1.88 (en Brent 1,97). Para una √ tabla llena
requiere un promedio de 2,13 y Brent 2,5. Mientras que Brent es O( 2 n) el Árbol
Binario de Hasheo es de O(log n).
2.2. Notación
X es un Tipo que es un modelo de Contenedor Asociativo de Hasheo.
12
2.3 Definiciones
2.3. Definiciones
Los elementos del Contenedor Asociativo de Hasheo son organizados en bucket
(buckets). El contenedor utiliza el valor de la función de Hasheo para determinar
a cual bucket se le asignará.
13
2.6 Modelos en el contenedor :
hash map: Asocia objetos del tipo Key con objetos del tipo Data. Pertenece
también al Contenedor de Pares Asociados lo que significa que su tipo de valor es
pair¡const Key, Data¿.También es un Contenedor Asociativo Único.
hash multimap: Posee las mismas funciones que el hash map pero es un Con-
tenedor Asociativo Múltiple.
3. Bibliografı́a
Andrew S. Tanenbaum - ”Structured Computer Organization, 4th ed.”
http://www.cs.vu.nl/ ast/
14
3 Bibliografı́a
Kruse - ”Data Structures And Program Design In C++” - 1st Edition - Prentice-
Hall, Inc. - 2000
15