Existen varias técnicas que garantizan que las búsquedas siempre requerirán operaciones O (1), incluso en el peor de los casos.
¿Cómo puedo determinar si una tabla hash tiene la posibilidad de tener operaciones O (1) y posiblemente qué técnicas usar en mi función hash?
El peor de los casos ocurre cuando un atacante malintencionado (Mallory) le proporciona deliberadamente datos que Mallory ha seleccionado específicamente para hacer que el sistema funcione lentamente.
Una vez que haya elegido alguna función hash en particular, probablemente sea demasiado optimista suponer que Mallory nunca descubrirá qué función hash eligió. Una vez que Mallory descubre qué función hash seleccionó, si permite que Mallory le proporcione una gran cantidad de datos para insertar en su tabla hash utilizando esa función hash, entonces está condenado: Mallory puede generar internamente rápidamente miles de millones de elementos de datos, hágalos con su Función hash para encontrar qué elementos de datos pueden colisionar, y luego alimentarlo con millones de uno en mil elementos de datos que probablemente colisionen, lo que lleva a búsquedas que se ejecutan mucho más lentamente que O (1).
Todas las técnicas que garantizan "búsquedas de O (1) incluso en el peor de los casos" evitan este problema haciendo un poco de trabajo adicional en cada inserción para garantizar que, en el futuro, cada búsqueda posible pueda tener éxito en el tiempo de O (1) . En particular, suponemos (en el peor de los casos) que Mallory tarde o temprano descubrirá qué función hash estamos utilizando; pero solo tiene la oportunidad de insertar algunos elementos de datos antes de elegir una función hash diferente ( hashing de tabulación u otro hashing universal ) que seleccionamos especialmente para que todos los datos que tenemos hasta ahora se puedan buscar en 2 o 3 sondas, es decir, O (1). Debido a que seleccionamos al azar esta función, podemos estar bastante seguros de que Mallory no sabrá qué función elegimos por un tiempo. Incluso si MalloryInmediatamente nos da datos que, incluso con esta nueva función hash, colisiona con datos anteriores, luego podemos elegir otra función hash nueva y nueva, de modo que, después de volver a escribir, todos los datos anteriores que él y todos los demás nos hayan alimentado ahora se puedan ver arriba en 2 o 3 sondas en el peor de los casos, es decir, búsquedas de O (1) en el peor de los casos.
Es bastante fácil seleccionar aleatoriamente una nueva función hash y volver a mostrar la tabla completa con la frecuencia suficiente para garantizar que cada búsqueda sea siempre O (1). Si bien esto garantiza que cada búsqueda sea siempre O (1), estas técnicas, al insertar el enésimo elemento en una tabla hash que ya contiene elementos N-1, ocasionalmente pueden requerir tiempo O (N) para ese inserto. Sin embargo, es posible diseñar el sistema de manera que, incluso cuando Mallory le brinde deliberadamente nuevos datos que, utilizando la nueva función hash, colisionen con datos anteriores, el sistema puede aceptar muchos elementos de Mallory y otros antes de que necesite hacer un reconstrucción completa de O (N). Las técnicas de tabla hash que seleccionan una nueva función y repiten para garantizar las búsquedas de O (1), incluso en el peor de los casos, incluyen:
- cuckoo hashing garantiza que cada búsqueda de clave tenga éxito con un máximo de 2 cálculos de hash y 2 búsquedas de tabla.
- El hash de rayuela garantiza que cada búsqueda de clave tenga éxito después de inspeccionar un número pequeño de entradas consecutivas H (quizás H = 32) en la tabla.
- hashing dinámico perfecto : el artículo de 1994 de Dietzfelbinger es el primero que he leído que señala que, a pesar de que se repite "con frecuencia" para garantizar que cada búsqueda clave siempre tenga éxito con 2 cálculos hash y 2 búsquedas, es posible hacer una repetición completa tan raramente que, a pesar de que cada repetición completa utiliza el tiempo O (n), el costo promedio esperado de las inserciones y eliminaciones se amortiza O (1).
Estructuras de datos / Tablas hash