¿Qué algoritmo de hash es mejor para la unicidad y la velocidad? Los ejemplos (buenos) usos incluyen diccionarios hash.
Sé que hay cosas como SHA-256 y similares, pero estos algoritmos están diseñados para ser seguros , lo que generalmente significa que son más lentos que los algoritmos que son menos únicos . Quiero un algoritmo hash diseñado para ser rápido, pero que siga siendo bastante único para evitar colisiones.
algorithms
hashing
Earlz
fuente
fuente
Respuestas:
Probé algunos algoritmos diferentes, midiendo la velocidad y el número de colisiones.
Usé tres conjuntos de teclas diferentes:
"1"
para"216553"
(piense en los códigos postales y cómo un hash pobre eliminó msn.com )Para cada corpus, se registró el número de colisiones y el tiempo promedio empleado en el hashing.
Probé:
xor
lugar de+
)Resultados
Cada resultado contiene el tiempo promedio de hash y el número de colisiones
Notas :
¿Las colisiones suceden realmente?
Si. Comencé a escribir mi programa de prueba para ver si realmente ocurren colisiones de hash , y no son solo una construcción teórica. De hecho suceden:
Colisiones FNV-1
creamwove
choca conquists
Colisiones FNV-1a
costarring
choca conliquid
declinate
choca conmacallums
altarage
choca conzinke
altarages
choca conzinkes
Murmurio2 colisiones
cataract
choca conperiti
roquette
choca conskivie
shawl
choca constormbound
dowlases
choca contramontane
cricketings
choca contwanger
longans
choca conwhigs
Colisiones DJB2
hetairas
choca conmentioner
heliotropes
choca conneurospora
depravement
choca conserafins
stylist
choca consubgenera
joyful
choca consynaphea
redescribed
choca conurites
dram
choca convivency
DJB2a colisiones
haggadot
choca conloathsomenesses
adorablenesses
choca conrentability
playwright
choca consnush
playwrighting
choca consnushing
treponematoses
choca conwaterbeds
Colisiones CRC32
codding
choca congnu
exhibiters
choca conschlager
Colisiones SuperFastHash
dahabiah
choca condrapability
encharm
choca conenclave
grahams
choca congramary
night
choca convigil
nights
choca convigils
finks
choca convinic
Aleatorización
La otra medida subjetiva es la distribución aleatoria de los hashes. La asignación de las HashTables resultantes muestra cuán uniformemente se distribuyen los datos. Todas las funciones hash muestran una buena distribución al mapear la tabla linealmente:
O como un mapa de Hilbert ( XKCD siempre es relevante ):
Excepto cuando hash cadenas de números (
"1"
,"2"
, ...,"216553"
) (por ejemplo, códigos postales ), donde los patrones comienzan a surgir en la mayoría de los algoritmos de hash:SDBM :
DJB2a :
FNV-1 :
Todos excepto FNV-1a , que todavía me parecen bastante aleatorios:
De hecho, Murmur2 parece tener una aleatoriedad aún mejor con
Numbers
queFNV-1a
:El extra
*
en la tabla denota cuán mala es la aleatoriedad. ConFNV-1a
ser el mejor yDJB2x
ser el peor:Originalmente escribí este programa para decidir si incluso tenía que preocuparme por colisiones: lo hago.
Y luego se convirtió en asegurarse de que las funciones hash fueran lo suficientemente aleatorias.
Algoritmo FNV-1a
El hash FNV1 viene en variantes que devuelven hashes de 32, 64, 128, 256, 512 y 1024 bits.
El algoritmo FNV-1a es:
Donde las constantes
FNV_offset_basis
yFNV_prime
dependen del tamaño de hash de retorno que desee:Vea la página principal de FNV para más detalles.
Todos mis resultados son con la variante de 32 bits.
FNV-1 mejor que FNV-1a?
No. FNV-1a es mucho mejor. Hubo más colisiones con FNV-1a al usar la palabra inglesa corpus:
Ahora compare minúsculas y mayúsculas:
En este caso, FNV-1a no es "400%" peor que FN-1, solo 20% peor.
Creo que lo más importante es que hay dos clases de algoritmos cuando se trata de colisiones:
Y luego está la distribución uniforme de los hashes:
Actualizar
¿Murmullo? Seguro Por qué no
Actualizar
@whatshisname se preguntó cómo funcionaría un CRC32 , agregó números a la tabla.
CRC32 es bastante bueno . Pocas colisiones, pero más lentas, y la sobrecarga de una tabla de búsqueda de 1k.
Recorte todas las cosas erróneas sobre la distribución de CRC - my bad
Hasta hoy iba a usar FNV-1a como mi algoritmo de hash de tabla hash de facto . Pero ahora me estoy cambiando a Murmur2:
Y realmente, realmente espero que haya algo mal con el
SuperFastHash
algoritmo que encontré ; Es una pena ser tan popular como es.Actualización: desde la página de inicio de MurmurHash3 en Google :
Así que supongo que no soy solo yo.
Actualización: me di cuenta de por qué
Murmur
es más rápido que los demás. MurmurHash2 opera en cuatro bytes a la vez. La mayoría de los algoritmos son byte a byte :Esto significa que a medida que las teclas se alargan, Murmur tiene la oportunidad de brillar.
Actualizar
Los GUID están diseñados para ser únicos, no aleatorios
Una publicación oportuna de Raymond Chen reitera el hecho de que los GUID "aleatorios" no deben usarse para su aleatoriedad. Ellos, o un subconjunto de ellos, no son adecuados como una clave hash:
Aleatoriedad no es lo mismo que evitar colisiones; por eso sería un error intentar inventar su propio algoritmo de "hashing" tomando algún subconjunto de un guid "aleatorio":
Nota : Nuevamente, pongo "GUID aleatorio" entre comillas, porque es la variante "aleatoria" de GUID. Una descripción más precisa sería
Type 4 UUID
. Pero nadie sabe qué son los tipos 4 o 1, 3 y 5. Por lo tanto, es más fácil llamarlos GUID "aleatorios".Todas las palabras inglesas reflejan
fuente
Si desea crear un mapa hash a partir de un diccionario que no cambia, puede considerar el hashing perfecto https://en.wikipedia.org/wiki/Perfect_hash_function : durante la construcción de la función hash y la tabla hash, puede garantizar, para un conjunto de datos dado, que no habrá colisiones.
fuente
Aquí hay una lista de funciones hash, pero la versión corta es:
fuente
CityHash by Google es el algoritmo que estás buscando. No es bueno para la criptografía, pero es bueno para generar hashes únicos.
Lea el blog para más detalles y el código está disponible aquí .
CityHash está escrito en C ++. También hay un puerto C simple .
Acerca del soporte de 32 bits:
fuente
plain C port
el enlace está rotoHe trazado una comparación de velocidad corta de diferentes algoritmos de hash cuando hashing archivos.
Las parcelas individuales solo difieren ligeramente en el método de lectura y pueden ignorarse aquí, ya que todos los archivos se almacenaron en un archivo tmpfs. Por lo tanto, el punto de referencia no estaba sujeto a IO si se está preguntando.
Algoritmos incluyen:
SpookyHash, CityHash, Murmur3, MD5, SHA{1,256,512}
.Conclusiones:
CRC
instrucción SSE 4.2 , que mi CPU no tiene. SpookyHash estuvo en mi caso siempre un poquito antes de CityHash.La fuente utilizada para las parcelas:
fuente
Los algoritmos SHA (incluido SHA-256) están diseñados para ser rápidos .
De hecho, su velocidad puede ser un problema a veces. En particular, una técnica común para almacenar un token derivado de contraseña es ejecutar un algoritmo de hash rápido estándar 10.000 veces (almacenar el hash del hash del hash del hash de la ... contraseña).
Salida:
fuente
bcrypt
. Usa las herramientas adecuadas..rodata
costos de instalación, desmontaje y / o estado. Cuando desea un algoritmo para una tabla hash, generalmente tiene claves muy cortas y muchas de ellas, pero no necesita las garantías adicionales de una cuenta criptográfica. Yo uso un Jenkins modificado uno por uno.La suposición de que las funciones hash criptográficas son más únicas es errónea, y de hecho se puede demostrar que a menudo es al revés en la práctica. En verdad:
Lo que significa que una función hash no criptográfica puede tener menos colisiones que una criptográfica para un "buen" conjunto de datos: conjuntos de datos para los que fue diseñada.
De hecho, podemos demostrar esto con los datos en la respuesta de Ian Boyd y un poco de matemática: el problema del cumpleaños . La fórmula para el número esperado de pares de colisión si selecciona
n
enteros al azar del conjunto[1, d]
es la siguiente (tomada de Wikipedia):Plugging
n
= 216,553 yd
= 2 ^ 32 obtenemos aproximadamente 5.5 colisiones esperadas . Las pruebas de Ian muestran principalmente resultados en ese vecindario, pero con una excepción dramática: la mayoría de las funciones obtuvieron cero colisiones en las pruebas de números consecutivos. La probabilidad de elegir al azar 216,553 números de 32 bits y obtener colisiones cero es de aproximadamente 0,43%. Y eso es solo para una función: ¡aquí tenemos cinco familias distintas de funciones hash con cero colisiones!Entonces, lo que estamos viendo aquí es que los hash que Ian probó están interactuando favorablemente con el conjunto de datos de números consecutivos, es decir, están dispersando entradas mínimamente diferentes más ampliamente de lo que lo haría una función hash criptográfica ideal. (Nota al margen: esto significa que la evaluación gráfica de Ian de que FNV-1a y MurmurHash2 "le parecen aleatorios" en el conjunto de datos de números se puede refutar a partir de sus propios datos. Cero colisiones en un conjunto de datos de ese tamaño, para ambas funciones hash, es sorprendentemente no aleatorio!)
Esto no es una sorpresa porque es un comportamiento deseable para muchos usos de las funciones hash. Por ejemplo, las claves de tabla hash son a menudo muy similares; La respuesta de Ian menciona un problema que MSN tuvo una vez con las tablas hash de código postal . Este es un uso donde la prevención de colisiones en entradas probables gana sobre el comportamiento aleatorio.
Otra comparación instructiva aquí es el contraste en los objetivos de diseño entre CRC y las funciones hash criptográficas:
Entonces, para CRC, nuevamente es bueno tener menos colisiones que aleatorias en entradas mínimamente diferentes. Con cripto hashes, este es un no-no!
fuente
Usa SipHash . Tiene muchas propiedades deseables:
Rápido. Una implementación optimizada toma alrededor de 1 ciclo por byte.
Seguro. SipHash es un fuerte PRF (función pseudoaleatoria). Esto significa que no se puede distinguir de una función aleatoria (a menos que conozca la clave secreta de 128 bits). Por lo tanto:
No es necesario preocuparse de que las sondas de la tabla hash se conviertan en tiempo lineal debido a colisiones. Con SipHash, sabe que obtendrá un rendimiento promedio de caso en promedio, independientemente de las entradas.
Inmunidad a los ataques de denegación de servicio basados en hash.
Puede usar SipHash (especialmente la versión con una salida de 128 bits) como MAC (Código de autenticación de mensaje). Si recibe un mensaje y una etiqueta SipHash, y la etiqueta es la misma que la de ejecutar SipHash con su clave secreta, entonces sabe que quien creó el hash también estaba en posesión de su clave secreta, y que ni el mensaje ni el hash ha sido alterado desde entonces.
fuente
Depende de los datos que esté procesando. Algunos hash funcionan mejor con datos específicos como texto. Algunos algoritmos de hash se diseñaron específicamente para ser buenos para datos específicos.
Paul Hsieh una vez hizo hash rápido . Enumera el código fuente y las explicaciones. Pero ya estaba vencido. :)
fuente
Java utiliza este algoritmo simple de multiplicar y agregar:
Probablemente hay muchos mejores, pero esto está bastante extendido y parece ser una buena compensación entre velocidad y singularidad.
fuente
En primer lugar, ¿por qué necesita implementar su propio hash? Para la mayoría de las tareas, debe obtener buenos resultados con las estructuras de datos de una biblioteca estándar, suponiendo que haya una implementación disponible (a menos que solo lo haga para su propia educación).
En cuanto a los algoritmos de hash reales, mi favorito personal es FNV. 1
Aquí hay un ejemplo de implementación de la versión de 32 bits en C:
fuente
*
y^
:h = (h * 16777619) ^ p[i]
==>h = (h ^ p[i]) * 16777619