¿Cómo están teniendo en cuenta las tablas hash O (1) la velocidad de hash?

8

Se dice que las tablas hash se amortizan usando, por ejemplo, un simple encadenamiento y duplicación a cierta capacidad.Θ(1)

Sin embargo, esto supone que las longitudes de los elementos son constantes. Calcular el hash de un elemento requiere pasar por el elemento, tomar tiempo donde es la longitud.Θ(l)l

Pero para discriminar entre elementos, necesitamos que los elementos tengan una longitud de al menos bits; de lo contrario por principio de casillero no serán distintos. La función hash que atraviesa bits de elemento llevará tiempo .nlgnlgnΘ(lgn)

Entonces, ¿podemos decir que la velocidad de una tabla hash, teniendo en cuenta una función hash razonable que utiliza todas las partes de la entrada, es en realidad ? ¿Por qué, entonces, las tablas hash en la práctica son eficientes para almacenar elementos de longitud variable, como cadenas y enteros grandes?Θ(lgn)

ithisa
fuente
44
La respuesta es que no lo son . Este tipo de análisis de hashing no tiene en cuenta la dimensión (o el número de bits) de los elementos, sino solo su multitud.
Nikos M.
Pero si una búsqueda de mapa hash que sería sin considerar leer y escribir los bits como se describe, es , entonces bajo el mismo criterio, una búsqueda binaria o cualquier otro proceso normalmente considera que realidad sería ¿no? Θ(1)Θ(lg n)Θlg nΘ(lg2 n)
@tAllan Una búsqueda binaria regular sería pero si mantiene los elementos ordenados de acuerdo con las secuencias de bits de sus claves y realiza una búsqueda binaria que compara "un bit a la vez" (se omiten detalles difíciles) ser capaz de alcanzar . Θ(log2n)Θ(logn)
Vuelva a instalar Mónica

Respuestas:

3

La historia de que las tablas hash se amortizan Θ(1)Es una mentira una simplificación excesiva.

Esto solo es cierto si:
- La cantidad de datos a hash por elemento es trivial en comparación con el número de K eys y la velocidad de hash a K ey es rápida -k.
- El número de C ollisions es pequeña -c.
- Nosotros no tomamos en cuenta el tiempo necesario para R edimensionar la tabla hash -r.

Grandes cadenas de hash
Si la primera suposición es falsa, el tiempo de ejecución aumentará aΘ(k).
Esto es definitivamente cierto para cadenas grandes, pero para cadenas grandes una comparación simple también tendría un tiempo de ejecución deΘ(k). Entonces, un hash no es asintóticamente más lento, aunque el hash siempre será más lento que una simple comparación, porque la comparación tiene una ergo de exclusión tempranaO(1), Ω(k) y el hashing siempre tiene que hacer hash con la cadena completa O(k), Ω(k).

Tenga en cuenta que los enteros crecen muy lentamente. 8 bytes pueden almacenar valores de hasta1018; 8 bytes es una cantidad trivial de hash.
Si desea almacenar bigints, entonces piense en ellas como cadenas.

Algoritmo de hash lento
Si la cantidad de hashing de gasto no es trivial en comparación con el almacenamiento de los datos, entonces obviamente elΘ(1)la suposición se vuelve insostenible.
A menos que se use un hash criptográfico, esto no debería ser un problema.

Lo que importa es que n >> k. Mientras eso se mantengaΘ(1) Es una declaración justa.

Muchas colisiones
Si la función de hash es deficiente, o la tabla de hash es pequeña, o el tamaño de la tabla de hash es incómoda, las colisiones serán frecuentes y el tiempo de ejecución será deO(log(n)).
La función de hashing debe elegirse de modo que las colisiones sean raras y sigan siendo lo más rápido posible, en caso de duda, opte por menos colisiones a expensas de un hashing más lento.
Una regla de oro es que la tabla de hashing siempre debe tener menos del 75% de su capacidad.
Y el tamaño de la tabla hash no debería tener ninguna correlación con la función hashing.
A menudo, el tamaño de la tabla de hash es (relativamente) primo.

Cambiar el tamaño de la tabla hash
Debido a que una tabla hash casi completa generará demasiadas colisiones y una tabla hash grande (vacía) es un desperdicio de espacio, muchas implementaciones permiten que la tabla hash crezca (y se reduzca) según sea necesario.
El crecimiento de una tabla puede implicar una copia completa de todos los elementos (y posiblemente una reorganización), porque el almacenamiento debe ser continuo por razones de rendimiento.
Solo en casos patológicos será un problema cambiar el tamaño de la tabla hash, por lo que los cambios de tamaño (costosos pero raros) se amortizan en muchas llamadas.

Tiempo de ejecución
Entonces, el tiempo de ejecución real de una tabla hash esΘ(kcr).
Cada uno dek, c, r en promedio se supone que es una constante (pequeña) en el tiempo de ejecución amortizado y, por lo tanto, decimos que Θ(1) Es una declaración justa.

Para volver a sus preguntas
Por favor, discúlpeme por parafrasear, he intentado extraer diferentes conjuntos de significados, siéntase libre de comentar si me he perdido algo

Parece que le preocupa la longitud de la salida de la función hash. Llamemos estom (n generalmente se considera que es el número de elementos que se van a codificar). m estarán log(n)porque m necesita identificar de forma exclusiva una entrada en la tabla hash.
Esto significa que m crece muy lentamente. A 64 bits, el número de entradas de la tabla hash ocupará una porción considerable de RAM disponible en todo el mundo. Con 128 bits, superará con creces el almacenamiento en disco disponible en el planeta tierra.
Producir un hash de 128 bits no es mucho más difícil que un hash de 32 bits, así que no , el momento de crear un hash no esO(m) (o O(log(n)) Si tu quieres).

La función hash pasando por log(n)los bits de elemento llevarán tiempo . Θ(log(n))

Pero la función hash no pasa por bits de elementos . Por un elemento (!!) solo va a través de los datos . Además, la longitud de la entrada (k) no tiene relación con el número de elementos. Esto es importante, porque algunos algoritmos no hash tienen que examinar muchos elementos en la colección para encontrar un elemento (no) coincidente. La tabla hash solo hace 1 o 2 comparaciones por elemento en consideración en promedio antes de llegar a una conclusión. log(n)
O(k)

¿Por qué las tablas hash son eficientes para almacenar elementos de longitud variable?

Debido a que, independientemente de la longitud de la entrada ( ), la longitud de la salida ( ) es siempre la misma, las colisiones son raras y el tiempo de búsqueda es constante. Sin embargo, cuando la longitud de la clave aumenta en comparación con el número de elementos en la tabla hash ( ), la historia cambia ...km
kn

¿Por qué las tablas hash son eficientes para almacenar cadenas grandes?

Las tablas hash no son muy eficientes para cadenas muy grandes.

Si (es decir, el tamaño de la entrada es bastante grande en comparación con el número de elementos en la tabla hash), entonces ya no podemos decir que el hash tiene un tiempo de ejecución constante, pero debe cambiar a un tiempo de ejecución de especialmente porque no hay salida anticipada. Usted tiene a hash de la clave completa. Si solo está almacenando un número limitado de artículos, entonces puede que sea mucho mejor usar un almacenamiento ordenado, porque al comparar puede optar por salir tan pronto como se vea una diferencia. not n>>kΘ(k)k1 k2

Sin embargo, si conoce sus datos, puede optar por no usar la clave completa, sino solo la parte volátil (conocida o supuesta) de la misma, restaurando la propiedad mientras mantiene las colisiones bajo control. Θ(1)

Constantes ocultas
Como todos deberían saber simplemente significa que el tiempo por elemento procesado es una constante. Esta constante es bastante más grande para el hash que para una simple comparación. Para tablas pequeñas, una búsqueda binaria será más rápida que una búsqueda hash, porque, por ejemplo, 10 comparaciones binarias podrían ser más rápidas que un solo hash. Para conjuntos de datos pequeños, se deben considerar alternativas a las tablas hash. Es en grandes conjuntos de datos que las tablas hash realmente brillan.Θ(1)


Johan
fuente
No entiendo tu definición de . No es cierto que el cambio de tamaño aumente el tiempo de ejecución amortizado. Mientras realice el cambio de tamaño adecuadamente, el costo de la copia puede amortizarse y no aumenta el tiempo de ejecución amortizado. No creo que la velocidad del hash sea un problema (incluso los hash criptográficos son muy rápidos; y, en cualquier caso, se ejecutan en tiempo constante, si la longitud de la entrada está limitada por una constante). Las reclamaciones de tiempo de ejecución siempre dependen del uso de una buena función hash (por lo que las colisiones serán pocas). k,c,rO(1)
DW
1
Entonces, de los problemas que mencionó, creo que solo la longitud de la entrada es realmente un problema grave. Además, esto realmente no responde la pregunta que se hizo. La pregunta habla sobre la longitud de las salidas y esa longitud de las salidas debería considerarse mejor como bits en lugar de bits. Eso es correcto, pero lo que pasa por alto es el modelo computacional utilizado para calcular el tiempo de ejecución . Esta respuesta no parece entrar en nada de eso, por lo que no estoy seguro de que esto esté llegando al problema planteado en la pregunta. Ω(lgn)O(1)O(1)
DW
Quería estar completo con todos los elementos del tiempo de ejecución. Estamos de acuerdo en que solo la longitud de la clave es realmente una preocupación al hacer hash. Arregle el problema de log (n) que el OP planteó. Leí mal eso, porque no es un problema cuando hashing IMO.
Johan
Espero que la respuesta esté más en sintonía con la pregunta del OP ahora.
Johan
3

Comencemos con una pregunta más simple. Considere cuál es quizás la estructura de datos más simple que existe, una matriz . Para concretar, imaginemos una serie de enteros. ¿Cuánto tiempo lleva la operación ? La respuesta depende del modelo de cálculo. Aquí son relevantes dos modelos: el modelo RAM (que es más común) y el modelo de bits (que es más sencillo de explicar).A[i]=A[j]

En el modelo de bits , una operación básica que implica bits de cuesta . Entonces, si los enteros tienen un ancho de bits, la operación costará alrededor de .NNwA[i]=A[j]2w

En el modelo RAM , la unidad básica de datos no es un bit sino una palabra (a veces conocida como una palabra de máquina ). Una palabra es un entero de ancho , donde es el tamaño de las entradas (en bits). Una operación básica que implica palabras cuesta . En la mayoría de los casos, si tiene una matriz de enteros, los enteros que necesita tienen un ancho , por lo que la operación cuesta .lognnNNO(logn)A[i]=A[j]O(1)

Como dije anteriormente, generalmente analizamos algoritmos usando el modelo RAM. La única excepción común es la aritmética de enteros, especialmente la multiplicación de enteros, que a menudo se analiza con respecto al número de operaciones de bits.

¿Por qué usamos el modelo RAM? Dado que tiene más poder predictivo (frente a la realidad). La suposición de que el tamaño de entrada es como máximo exponencial en el tamaño de una palabra de máquina generalmente está justificada, especialmente para los procesadores modernos de 64 bits, y las operaciones en palabras de máquina toman tiempo constante en las CPU reales.


Las tablas hash son estructuras de datos más complicadas, y realmente involucran tres tipos: el tipo clave, el tipo hash y el tipo de valor. Desde el punto de vista del tipo de valor , una tabla hash es solo una matriz glorificada, así que ignoremos ese aspecto. El tipo de hash siempre se puede suponer que consistir en un pequeño número de palabras de la máquina. El tipo de clave satisface una propiedad especial: es hashable , lo que significa que tiene una operación hash que (como mínimo) es una función determinista (una función que siempre devuelve el mismo valor).

Ahora podemos abordar su pregunta: ¿cuánto tiempo lleva hacer hash una clave? La respuesta depende del modelo de cálculo. Esta vez tenemos tres modelos comunes: los dos anteriores y el modelo oráculo.

En el modelo de oráculo , suponemos que la función hash nos la proporciona un "oráculo" que puede calcular el hash de una clave arbitraria en tiempo constante.

En el modelo RAM y el modelo de bits , la función hash es una función real, y la complejidad temporal de la tabla hash depende de la complejidad temporal de la función hash. Las funciones hash utilizadas para la tabla hash (en lugar de para fines criptográficos) suelen ser muy rápidas y requieren un tiempo lineal en la entrada. Eso significa que si el tipo de clave tiene una longitud de bits (en el modelo de bits) o palabras (en el modelo de RAM), la función hash toma tiempo . Cuando es una constante, la función hash toma tiempo constante.NNO(N)N

Cuando analizamos el tiempo de ejecución de los algoritmos de tabla hash, usualmente utilizamos implícitamente el modelo oracle. Esto a menudo se expresa en un idioma diferente: simplemente decimos que contamos el número de invocaciones de la función hash. Esto tiene sentido, ya que generalmente las aplicaciones de la función hash es el término dominante en el tiempo de ejecución de los algoritmos de la tabla hash, por lo que para analizar la complejidad del tiempo real, todo lo que tiene que hacer es multiplicar el número de invocaciones hash por el tiempo de ejecución de la función hash.

Cuando analizamos el tiempo de ejecución de un algoritmo utilizando una tabla hash como estructura de datos, a menudo nos interesa el tiempo de ejecución real, generalmente en el modelo de RAM. Una opción aquí es hacer lo que se sugirió en el párrafo anterior, es decir, multiplicar el tiempo de ejecución de las operaciones de la tabla hash (dado en términos de número de invocaciones de funciones hash) por el tiempo de ejecución de la función hash.

Sin embargo, esto no es lo suficientemente bueno si las teclas tienen longitudes variables. Por ejemplo, imagine que tenemos claves de tamaño , y calculamos el hash de cada una de ellas. La complejidad de tiempo real es , pero el cálculo anterior solo da . Si este es el caso en alguna aplicación, podemos tenerlo en cuenta ad hoc, utilizando un análisis refinado de la complejidad de la tabla hash subyacente.1,2,4,,2mO(2m)O(m2m)

Yuval Filmus
fuente