Hashing características Hashing (Funciones de Dispersión) Mauricio Solar Lorna Figueroa No necesita almacenamiento adicional (índice). Facilita la inserción y eliminación rápida de registros. Encuentra registros con muy pocos accesos en promedio, al disco. La idea general hashing es colocar, en la forma menos relacionada posible, la información entregada por los elementos, de manera tal que su acceso sea eficiente y exista una adecuada repartición de los mismos a lo largo de todo el espacio de almacenamiento. Desventajas: No se pueden usar registros de longitud variable. No puede haber orden físico de datos. No permite llaves duplicadas. Hashing La tabla hash Se necesita un mecanismo de acceso a registros sólo con una lectura. Visto hasta ahora: Secuencia: n/ accesos en promedio Ordenado: log n Arboles: o accesos La representación de conjuntos o diccionarios con listas o arrays tiene un tiempo de O(n), para Insertar, Suprimir y Pertenece. Con vectores de bits el tiempo es O(cte), pero tiene muchas limitaciones. Cómo aprovechar lo mejor de uno y otro tipo? Idea: Usar un array con B posiciones (,..., B-). Dada una clave x calcular una posición mediante una función: h(x) = posición en (.. B-) h: Función de hashing El elemento (x, v) se inserta en esa posición..... B- Hashing algunas definiciones Técnica para generar una dirección base para una llave dada. Técnica que convierte la llave registro en un número aleatorio, el que sirve después para determinar dónde se almacena el registro. Técnica de almacenamiento y recuperación que usa una función de hash para mapear registros en una dirección de almacenamiento. La tabla hash Para buscar el elemento con clave x, aplicar la función h(x) y buscar en la posición correspondiente. Ejemplo: Si x de tipo entero h(x) = x módulo B Si x de tipo cadena h(x) = (suma de códigos ascii (x)) mod B
La tabla hash Hashing abierto En promedio se necesita un tiempo constante O(cte): aplicar h(x) y acceder a esa posición. M - Tabla de encabezados de celdas o cubetas Listas de elementos de cada celda La tabla hash El tipo de x no está restringido. Si es un registro, se puede tomar uno de sus campos como clave, y aplicar la función sobre él. Función de hashing: h: Clave [,..., B-] Tamaño de la tabla B: aproximadamente mayor que el número de elementos normalmente, B << rango de claves Hashing abierto typedef Lista_asociacion TablaHash[B-]; La tabla hashing está formada por B cubetas. Dentro de cada cubeta se encuentran los elementos sinónimos (con igual de h). El conjunto de sinónimos es llamado clase. La tabla hash Qué ocurre si para dos elementos distintos x, y, h(x) = h(y)? Definición: Si (x y) (h(x) = h(y)) entonces se dice que x, y son sinónimos. Los distintos métodos de hashing difieren en el tratamiento de los sinónimos. Existen dos tipos de hashing, que se distinguen por las formas de almacenar los elementos:. Abierto o externo: permite manejar almacenar los conjuntos en un espacio potencialmente ilimitado (tamaños arbitrarios). Cerrado o interno: usa un espacio fijo para almacenamiento, limitando así el tamaño de los conjuntos. Hashing cerrado En esta clase de hashing sólo existe la estructura de las celdas o la tabla, donde deben almacenarse todos los elementos conjunto o diccionario, en vez de usarse como encabezados de listas. y h celda ocupada Celdas alternativas h(x) Situación de colisión en hashing cerrado o interno x
Hashing cerrado typedef AsociacionTablaHash[B-] Si al insertar un elemento nuevo x, ya está ocupado h(x), se dice que ocurre una colisión. En este caso será necesario hacer rehashing: buscar una nueva posición para almacenar el elemento. Hashing cerrado Qué ocurre con la eliminación de elementos? Se necesita una marca especial suprimido, que indique la posición de un elemento borrado. En la función Miembro, se sigue la búsqueda hasta llegar a un suprimido. En Inserta el espacio de ese elemento se puede utilizar (como si fuera vacío). Problema rehashing lineal: Si se llenan varias cubetas consecutivas y hay una colisión, se debe consultar todo el grupo. Aumenta el tamaño de este grupo, haciendo que las inserciones y búsquedas sean más lentas. Hashing cerrado estrategias de reubicación (rehashing) Consiste en explorar posiciones alternativas: h (y), h (y), h (y), h (y),... Dichas posiciones se buscan en orden hasta hallar un espacio vacío donde almacenar el elemento (o bien hasta agotar el espacio disponible, si la tabla está llena). Rehashing: Si falla h(x), aplicar h (x), h (x),... hasta encontrar una posición libre. Rehashing: h i (x)= (h(x) + i) mod B La secuencia de posiciones recorridas para un elemento se suele denominar cadena o secuencia de búsqueda. Funciones Hashing Propiedades de una buena función de hashing La función debe minimizar el número de colisiones: debe ser lo más aleatoria posible y repartir los elementos en la tabla de manera uniforme. La función debe ser fácil de calcular (se busca eficiencia). Ojo: h(x) es función de x, devuelve siempre el mismo para un mismo de x. Hashing cerrado Miembro (x, D): Examinar la posición h(x). Si x está en la posición h(x) entonces devolver Verdad (ó Tabla[h(x)].). Si está vacía entonces no es miembro, devolver Falso. En otro caso, la posición está ocupada pero por otro elemento. Se deben examinar las posiciones h (x), h (x),... y así sucesivamente hasta encontrar x, vacío ó examinar toda la tabla. hash(tipodato llave, int #max) { int sum =; int j = ; while (j < #elementos de llave) { sum = sum + * llave[j] + llave[j+]; j = j + ; } return sum mod #max; }
Multiplicación. Dada una clave x entera: h(x) = (x * C) mod B; para algún C, con C y B primos entre sí Dígitos centrales: h(x) = x / mod B Escoger un C, tal que BC K, para x en el intervalo (,..., K). h(x)= (x / C) mod B Ejemplo: K = ; B = ; C =; h() = Otro posible método: h(x) = ( C*x / C + x*c) mod B Plegado o folding: Con cadenas. Agrupar caracteres de n en n: Cada carácter es un número en el intervalo.. Se suman los es de los grupos. Ejemplo: x = abcdefgh h(x)=( a+ b +c + d + e + f + g+h) mod B Con reales. Agrupar dígitos decimales de n en n: Ejemplo: h(x) = ((x * ) + (x * ) + (x * ) +... ) mod B Otro ejemplo con reales: h(x) = Frac (x) * B Centros cuadrados: la llave se multiplica por sí misma, tomando los dígitos centrales al cuadrado; posteriormente se ajusta al espacio disponible. División: la clave se divide por un número aproximadamente igual al número de direcciones (número primo, pues tiende a distribuir residuos en forma más eficiente). Desplazamiento: los dígitos externos de ambos extremos se corren hacia adentro, se suman y se ajusta al espacio disponible. Análisis de dígitos: analizan las claves para eliminar posibles repeticiones en la misma. Cuál usar? tomar algunas claves o llaves problema, simular el comportamiento con algunos métodos y se toma el que mejor se comporta. En general: Método de la división es mejor Método plegado para claves muy largas Conversión de raíz: la base número se modifica y en la serie de dígitos resultante, se suprimen los dígitos de orden mayor. División polinómica: cada dígito clave se toma como coeficiente de polinomio, se divide por un polinomio fijo, coeficiente resto se toma como dirección. Con cadenas. Suma de códigos ASCII: suma = ; para i = hasta longitud(x) hacer suma = suma + ord(x[i]); devolver (suma mod B); Tratamiento de Colisiones Es posible reducir el % de overflow, pero el problema se mantiene dado que no se llega al %. Existen dos métodos principales de rehashing:. prueba lineal. doble hashing
Tratamiento de Colisiones Tratamiento de Colisiones Prueba lineal o secuencial Si la función de hashing de un elemento apunta al índice i, pero en esa posición ya existe otro elemento, se trata de verificar las posiciones inmediatamente siguientes: i+, i+, i+,..., hasta encontrar una posición vacía (en secuencia). Si se llegó al final de la tabla continuar buscando al principio (posición ): H(x,) = h(x) H(x,p+) = H(x,p) + mod M Doble hashing x y z h(z) h(y) h(x) celda ocupada H(x,) + h (y) + h (z) H(x,) H(y,) H(x,) H(z,) H(y,) Situación de colisiones múltiples resuelta con doble hashing Tratamiento de Colisiones Obviamente el número máximo de pruebas es M-, de lo contrario se empieza a repetir la búsqueda ( p < M). Esta técnica es muy fácil de implementar y tiene un comportamiento satisfactorio cuando la tabla no está muy llena. x h(x) H(x,) H(x,) H(x,)... celda ocupada Celdas alternativas Problema: agrupamiento primario: una vez que un bloque de posiciones consecutivas aparece ocupada en la tabla se transforma en un blanco para colisiones subsecuentes. Eso va produciendo bloques ocupados cada vez más grandes y más fáciles de hacer blanco de colisiones. Funciones re-hashing Estrategias de rehashing (para hashing cerrado): Intervalos constantes mayores que : h i (x) = (h(x) + C*i) mod B; para algún C > Ejemplo: B =, C =, h(x) =, se buscaría en:,,,,,,, B y C deben ser primos entre sí, para buscar en todas las posiciones. No se soluciona el problema, ya que se crean grupos a saltos de C. Tratamiento de Colisiones Doble hashing Consiste en usar una segunda función de hashing h cuyos es son independientes hashing primario. Este nuevo se agrega a la posición hallada en caso de colisión, y así sucesivamente hasta resolver el problema de posicionamiento: H(x,) = h(x) H(x,p+) = (H(x,p) + h (x)) mod M Funciones re-hashing Permutaciones aleatorias: h i (x) = (h(x) + D i ) mod B; para D i = (D, D,...) una permutación de,,..., B- La permutación se fija de antemano. Se soluciona el problema para determinadas permutaciones D i.
Funciones re-hashing Método para obtener una permutación: Sea B una potencia de. Se toma una constante K, en el intervalo (,..., B-). Empezamos con un cualquiera D= D i D i+ = *D i Si D i+ > B Resta = D i+ -B; D i+ = Resta K; D i = (,,,,,,,,...) {,,,,,,,,, } Hash lineal: h(x) = x mod + i; i =,,... Número Insertado : Función hash: h() + = ==> no hay conflicto Funciones re-hashing Ejemplo: B = ; K = = b ; D = = b {,,,,,,,,, } Hash lineal: h(x) = x mod + i; i =,,... Di* Resta Resta D D D D D D D D Número Insertado: Función hash: h() + = ==> hay conflicto Función hash: h() + = ==> no hay conflicto Insertar en una tabla hash de tamaño, las claves:,,,,,,,,,, con los métodos : hash lineal, hash cuadrático, hash encadenado h(x) = x mod la función hash usa el último dígito en la representación decimal de la llave. Contar el número de comparaciones necesarias para cada operación. {,,,,,,,,, } Hash lineal: h(x) = x mod + i; i =,,... Número Insertado: Función hash: h() + = ==> hay conflicto Función hash: h() + = ==> no hay conflicto Tabla inicial:
{,,,,,,,,, } Hash lineal: h(x) = x mod + i; i =,,... {,,,,,,,,, } Hash lineal: h(x) = x mod + i; i =,,... Número Insertado: Función hash: h() + = ==> no hay conflicto Número Insertado: Función hash: h() + = ==> hay conflicto Función hash: h() + = ==> no hay conflicto {,,,,,,,,, } Hash lineal: h(x) = x mod + i; i =,,... Número Insertado: Función hash: h() + = ==> hay conflicto Función hash: h() + = (vuelve a ) ==> no hay conflicto {,,,,,,,,, } Hash lineal: h(x) = x mod + i; i =,,... Número Insertado: Función hash: h() + = ==> hay conflicto Función hash: h() + = () ==> hay conflicto Función hash: h() + = () ==> hay conflicto Función hash: h() + = () ==> hay conflicto Función hash: h() + = () ==> no hay conflicto {,,,,,,,,, } Hash lineal: h(x) = x mod + i; i =,,... Número Insertado: Función hash: h() + = ==> hay conflicto Función hash: h() + = ==> no hay conflicto {,,,,,,,,, } Hash lineal: h(x) = x mod + i; i =,,... Número Insertado: Función hash: h() + = ==> conflicto Función hash: h() + = ==> conflicto Función hash: h() + = () ==> conflicto Función hash: h() + = () ==> conflicto Función hash: h() + = () ==> conflicto Función hash: h() + = () ==> conflicto Función hash: h() + = () ==> conflicto Función hash: h() + = () ==> no hay conflicto
{,,,,,,,,, } Hash lineal: h(x) = x mod + i; i =,,... Número Insertado: Función hash: h() + = ==> conflicto Función hash: h() + = ==> conflicto Función hash: h() + = ==> conflicto Función hash: h() + = ==> conflicto Función hash: h() + = ==> no conflicto {,,,,,,,,, } Método cuadrático: h(x) = x mod + i; i =,,,... Número Insertado: Función hash: h() + = ==> hay conflicto Función hash: h() + = ==> no hay conflicto Total Número de Pruebas: {,,,,,,,,, } Método cuadrático: h(x) = x mod + i; i =,,,... Número Insertado : Función hash: h() + = ==> no hay conflicto {,,,,,,,,, } Método cuadrático: h(x) = x mod + i; i =,,,... Número Insertado: Función hash: h() + = ==> no hay conflicto {,,,,,,,,, } Método cuadrático: h(x) = x mod + i; i =,,,... Número Insertado: Función hash: h() + = ==> hay conflicto Función hash: h() + = ==> no hay conflicto {,,,,,,,,, } Método cuadrático: h(x) = x mod + i; i =,,,... Número Insertado: Función hash: h() + = ==> hay conflicto Función hash: h() + = (vuelve a ) ==> no hay conflicto
{,,,,,,,,, } Método cuadrático: h(x) = x mod + i; i =,,,... Número Insertado: Función hash: h() + = ==> hay conflicto Función hash: h() + = ==> no hay conflicto {,,,,,,,,, } Método cuadrático: h(x) = x mod + i; i =,,,... Número Insertado: Función hash: h() + = ==> conflicto Función hash: h() + = ==> conflicto Función hash: h() + = () ==> conflicto Función hash: h() + = () ==> conflicto Función hash: h() + = () ==> conflicto Función hash: h() + = () ==> conflicto Función hash: h() + = () ==> conflicto Función hash: h() + = () ==> conflicto Función hash: h() + = () ==> conflicto Función hash: h() + = () ==> conflicto Función hash: h() + = () ==> conflicto No importa cuánto tiempo se busque, no se encontrará un lugar! {,,,,,,,,, } Método cuadrático: h(x) = x mod + i; i =,,,... Número Insertado: Función hash: h() + = ==> hay conflicto Función hash: h() + = ==> no hay conflicto {,,,,,,,,, } Método cuadrático: h(x) = x mod + i; i =,,,... Número Insertado: Función hash: h() + = ==> conflicto Función hash: h() + = ==> conflicto Función hash: h() + = ==> no hay conflicto Total Número de Pruebas: (excluyendo la búsqueda fallada para el ) Cuando se insertó el, nunca se examinaron las dos celdas disponibles, y. Nota: para que toda la tabla sea investigada el factor de carga tiene que ser mayor que, y el tamaño de la tabla tiene que ser primero. {,,,,,,,,, } Método cuadrático: h(x) = x mod + i; i =,,,... Número Insertado: Función hash: h() + = ==> hay conflicto Función hash: h() + = () ==> hay conflicto Función hash: h() + = () ==> no hay conflicto {,,,,,,,,, } Hash encadenado separado: La tabla de encadenamiento separada agrega a una lista enlazada cuando ocurre una colisión. El resultado final se parece a : -> -> ->
El número de búsqueda en una lista enlazada es: Valor Insertado Compariciones Total : Eliminar las claves,, y desde la tabla hash: Hash lineal: Eliminar : h() + = ==> no se encuentra h() + = () ==> encontrado Total Número de pruebas: Eliminar las claves,, y desde la tabla hash: Eliminar las claves,, y desde la tabla hash: Hash lineal: Eliminar : h() + = ==> Eliminado ==> continuar la búsqueda h() + = () ==> Eliminado h() + = () ==> No encontrado h() + = () ==> No encontrado h() + = () ==> Encontrado Total Número de pruebas: Eliminar las claves,, y desde la tabla hash: Eliminar las claves,, y desde la tabla hash: Hash lineal: Eliminar : h() + = ==> no se encuentra h() + = ==> encontrado Total Número de pruebas: Método Cuadrático : Eliminar : h() + = ==> No encontrado h() + = ==> Encontrado Total Número de Pruebas :
Eliminar las claves,, y desde la tabla hash: Eliminar las claves,, y desde la tabla hash: -> -> -> Método Cuadrático : Eliminar : h() + = ==> eliminado h() + = () ==> encontrado Total Número de Pruebas : Método encadenado separado: Eliminar : Valor : Comparaciones : -> Eliminar las claves,, y desde la tabla hash: Eliminar las claves,, y desde la tabla hash: -> -> -> Método Cuadrático : Eliminar : h() + = ==> Eliminado h() + = () ==> Eliminado h() + = ( ) ==> Encontrado Total Número de Pruebas : Método encadenado separado: Eliminar : Valor : Comparaciones : -> Eliminar las claves,, y desde la tabla hash: Método encadenado separado: Eliminar : Valor : Comparaciones : -> -> -> Referencias bibliográficas Estructura de Datos y Algoritmos. Aho, Hopcroft, Ullman. Addison-Wesley Iberoamericana. García Mateos, A.A.E.D. Apuntes de la UNLP Facultad de Informática. -> ->