Estoy buscando implementar una tabla hash rápida y bien distribuida en C #. Tengo problemas para elegir mi función de restricción de hash que toma un código de hash arbitrario y lo "restringe" para que pueda usarse para indexar los cubos. Hay dos opciones que veo hasta ahora:
Por un lado, puede asegurarse de que sus cubos siempre tengan un número primo de elementos, y para restringir el hash, simplemente modúlelo por el número de cubos. Esto es, de hecho, lo que hace el Diccionario de .NET . El problema con este enfoque es que usar% es extremadamente lento en comparación con otras operaciones; Si observa las tablas de instrucciones de Agner Fog ,
idiv
(que es el código de ensamblado que se genera para%) tiene una latencia de instrucción de ~ 25 ciclos para los procesadores Intel más nuevos. Compare esto con alrededor de 3mul
, o 1 para operaciones bit a bit comoand
,or
oxor
.Por otro lado, puede hacer que el número de cubos sea siempre una potencia de 2. Todavía tendrá que calcular el módulo del hash para que no intente indexar fuera de la matriz, pero esta vez será menos costoso. . Dado que para potencias de 2
% N
es justo& (N - 1)
, la restricción se reduce a una operación de enmascaramiento que solo toma 1-2 ciclos. Esto lo hace sparsehash de Google . La desventaja de esto es que contamos con los usuarios para proporcionar buenos hashes; enmascarar el hash esencialmente corta parte del hash, por lo que ya no tomamos en cuenta todos los bits del hash. Si el hash del usuario está distribuido de manera desigual, por ejemplo, solo se completan los bits más altos o los bits más bajos son consistentemente iguales, entonces este enfoque tiene una tasa de colisiones mucho más alta.
Estoy buscando un algoritmo que pueda usar que tenga lo mejor de ambos mundos: toma en cuenta todos los bits del hash y también es más rápido que usar%. No necesariamente tiene que ser un módulo, solo algo que se garantiza que esté en el rango 0..N-1
(donde N es la longitud de los cubos) y tiene una distribución uniforme para todas las ranuras. ¿Existe tal algoritmo?
Gracias por ayudar.
fuente
(2^N +/- 1)
, consulte stackoverflow.com/questions/763137/…Respuestas:
Las implementaciones de tablas hash modernas no utilizan la función de módulo. A menudo usan la potencia de tablas de dos tamaños y cortan bits innecesarios. Una función hash ideal permitiría esto. El uso de módulos combinados con tamaños de tablas de números primos surgió en los días en que las funciones hash eran generalmente deficientes, ya que a menudo se encuentran en el desarrollo .net. Recomiendo leer sobre SipHash , una función hash moderna, luego leer sobre otras funciones modernas, como xxHash .
Debo explicar por qué las funciones hash de .net son a menudo deficientes En .net, los programadores a menudo se ven obligados a implementar funciones hash anulando GetHashcode. Pero .net no proporciona las herramientas necesarias para garantizar que las funciones creadas por el programador sean de alta calidad, a saber:
Para obtener más información sobre el uso de un resultado de función hash como índice de tabla hash, consulte las definiciones de formas universales de hashing en este documento: Hash universal más rápido de 64 bits usando multiplicaciones sin acarreo
fuente
Para usar AND mientras mantiene todos los bits, use XOR también.
Por ejemplo
temp = (hash & 0xFFFF) ^ ( hash >> 16); index = (temp & 0xFF) ^ (temp >> 8);
,.Para este ejemplo, no hay módulo y los 32 bits de
hash
efecto son de 8 bitsindex
. Sin embargo, si es o no más rápido que el DIV es algo que depende de demasiados factores, y puede ser más lento que el DIV en algunos casos (por ejemplo, hash grande e índice pequeño).fuente
index
, estará en el rango[0..255]
. Necesito algo en el rango[0..n-1]
, donden
está el número de cubos.Puede aprovechar el hecho de que muchos enteros primos tienen un inverso multiplicativo modular. Ver este artículo . Ha satisfecho una de las restricciones al hacer que su índice de cubeta sea primo y el módulo 2 ^ n, que son intrínsecamente primos.
El artículo describe el algoritmo para encontrar un número tal que multiplicar por ese número e ignorar el desbordamiento producirá el mismo resultado que si se hubiera dividido por el tamaño del índice del depósito.
fuente