¿Qué es una buena función hash?

130

¿Qué es una buena función Hash? Vi muchas funciones de hash y aplicaciones en mis cursos de estructuras de datos en la universidad, pero principalmente entendí que es bastante difícil hacer una buena función de hash. Como regla general para evitar colisiones, mi profesor dijo que:

function Hash(key)
  return key mod PrimeNumber
end

(mod es el operador% en C y lenguajes similares)

con el número primo como el tamaño de la tabla hash. Entiendo que es una función algo buena para evitar colisiones y una rápida, pero ¿cómo puedo hacer una mejor? ¿Hay mejores funciones hash para las teclas de cadena frente a las teclas numéricas?

Hoffmann
fuente
34
¿Ha considerado usar una o más de las siguientes funciones hash de propósito general: partow.net/programming/hashfunctions/index.html
En fnv_func, el tipo de p [i] es char, ¿qué pasará con h después de la primera iteración? ¿Se hizo a propósito?
55
@martinatime dijo: Hay una gran cantidad de información sobre las funciones hash en wikipedia en.wikipedia.org/wiki/Hash_function y al final de este artículo partow.net/programming/hashfunctions/index.html tiene algoritmos implementados en varios idiomas.
2501

Respuestas:

33

Para hacer búsquedas de tablas hash "normales" en básicamente cualquier tipo de datos, esta de Paul Hsieh es la mejor que he usado.

http://www.azillionmonkeys.com/qed/hash.html

Si te importa la seguridad criptográfica o cualquier otra cosa más avanzada, entonces YMMV. Si solo desea una función hash de propósito general para una búsqueda de tabla hash, entonces esto es lo que está buscando.

Chris Harris
fuente
Gracias por el enlace informativo! Conozco algunos análisis de Bob Jenkins y otros que apuntan a funciones hash universalmente aceptables bastante buenas, pero aún no he encontrado este.
Konrad Rudolph el
Había leído en el sitio de Jenkins que SFH es uno de los mejores entonces, pero creo que Murmur podría hacerlo mejor, vea esta excelente respuesta: programmers.stackexchange.com/questions/49550/…
nawfal
2
¿Qué significa YMMV?
cobarzan
3
@cobarzan Su millaje puede variar
ProgramadorDan
2
La función hash de Hsieh es horrible, con un orden de magnitud de más colisiones de las que queremos. En particular, las cadenas que difieren solo en los últimos 4 bytes pueden colisionar fácilmente. Si tiene una cadena de 30 caracteres, que difieren en los últimos 4 bytes, después de haber procesado 28 bytes, los hashes difieren solo en los últimos 2 bytes. Eso significa que está GARANTIZADO una colisión para uno de los valores restantes de dos bytes. (Sí, es rápido. Y qué.)
Andrew Lazarus
51

No existe una "buena función hash" para los hash universales (ed. Sí, sé que existe el "hash universal", pero eso no es lo que quise decir). Dependiendo del contexto, diferentes criterios determinan la calidad de un hash. Dos personas ya mencionaron SHA. Este es un hash criptográfico y no es del todo bueno para las tablas hash, lo que probablemente quieras decir.

Las tablas hash tienen requisitos muy diferentes. Pero aún así, encontrar una buena función hash universalmente es difícil porque los diferentes tipos de datos exponen información diferente que puede ser hash. Como regla general, es bueno considerar toda la información que un tipo contiene por igual. Esto no siempre es fácil o incluso posible. Por razones de estadísticas (y por lo tanto colisión), también es importante generar una buena distribución en el espacio del problema, es decir, todos los objetos posibles. Esto significa que cuando los números hash entre 100 y 1050 no es bueno dejar que el dígito más significativo juegue un papel importante en el hash porque para ~ 90% de los objetos, este dígito será 0. Es mucho más importante dejar que los últimos tres los dígitos determinan el hash.

Del mismo modo, cuando se combinan cadenas, es importante tener en cuenta todos los caracteres, excepto cuando se sabe de antemano que los primeros tres caracteres de todas las cadenas serán los mismos; considerando esto, entonces es un desperdicio.

Este es en realidad uno de los casos en los que aconsejo leer lo que Knuth tiene que decir en The Art of Computer Programming , vol. 3. Otra buena lectura es The Art of Hashing de Julienne Walker .

Konrad Rudolph
fuente
1
Konrad, seguramente tienes razón desde una perspectiva teórica, pero ¿alguna vez has intentado usar la función hash Paul Hsieh que mencioné en mi comentario? ¡Es realmente bastante bueno contra muchos tipos diferentes de datos!
Chris Harris
9

Hay dos propósitos principales de las funciones de hash:

  • para dispersar puntos de datos uniformemente en n bits.
  • para identificar de forma segura los datos de entrada.

Es imposible recomendar un hash sin saber para qué lo está utilizando.

Si solo está haciendo una tabla hash en un programa, entonces no necesita preocuparse por cuán reversible o pirateable es el algoritmo ... SHA-1 o AES es completamente innecesario para esto, sería mejor usar Una variación de FNV . FNV logra una mejor dispersión (y, por lo tanto, menos colisiones) que un simple mod principal como usted mencionó, y es más adaptable a diferentes tamaños de entrada.

Si está utilizando los hash para ocultar y autenticar información pública (como el hash de una contraseña o un documento), entonces debe usar uno de los principales algoritmos de hash examinados por el escrutinio público. El Hash Function Lounge es un buen lugar para comenzar.

Myrddin Emrys
fuente
enlace actualizado a The Hash Function Lounge: larc.usp.br/~pbarreto/hflounge.html
Tim Partridge
¿Qué tan bien resiste FNV la colisión de cumpleaños en comparación con, digamos, la misma cantidad de bits de un SHA1?
Kevin Hsu
@Kevin Mientras las características de avalancha de un hash sean buenas (pequeños cambios en la entrada = grandes cambios en la salida), las colisiones de cumpleaños son simplemente una función de los bits en el hash. El FNV-1a es excelente en este sentido, y puede tener tantos o tan pocos bits en el hash como desee (aunque se necesita un poco de esfuerzo adicional para obtener un recuento de bits que no es una potencia de 2).
Myrddin Emrys
5

Este es un ejemplo de uno bueno y también un ejemplo de por qué nunca querrías escribir uno. Es un Hash Fowler / Noll / Vo (FNV) que es a partes iguales genio de la informática y vudú puro:

unsigned fnv_hash_1a_32 ( void *key, int len ) {
    unsigned char *p = key;
    unsigned h = 0x811c9dc5;
    int i;

    for ( i = 0; i < len; i++ )
      h = ( h ^ p[i] ) * 0x01000193;

   return h;
}

unsigned long long fnv_hash_1a_64 ( void *key, int len ) {
    unsigned char *p = key;
    unsigned long long h = 0xcbf29ce484222325ULL;
    int i;

    for ( i = 0; i < len; i++ )
      h = ( h ^ p[i] ) * 0x100000001b3ULL;

   return h;
}

Editar:

  • Landon Curt Noll recomienda en su sitio el algoritmo FVN-1A sobre el algoritmo FVN-1 original: el algoritmo mejorado dispersa mejor el último byte en el hash. Ajusté el algoritmo en consecuencia.
Nick Van Brunt
fuente
3
Es posible que desee ver este sitio para obtener información sobre por qué se eligen estos valores: isthe.com/chongo/tech/comp/fnv/#fnv-prime
Cthutu
Salud. Esta breve, simple, eficiente, genérica y efectiva función hash de 64 bits fue exactamente lo que necesitaba.
mattarod
3

Yo diría que la regla general es no tirar la tuya. Intente usar algo que haya sido probado exhaustivamente, por ejemplo, SHA-1 o algo similar.

Einar
fuente
No parece necesitar nada criptográficamente seguro, por lo que SHA-1 sería una exageración.
Erik
por cierto, aunque no se han encontrado colisiones para SHA-1, se cree que es cuestión de años o meses antes de que se encuentre uno. Recomendaría usar SHA-256.
Samuel Allan
1

Una buena función hash tiene las siguientes propiedades:

  1. Dado un hash de un mensaje, es computacionalmente inviable que un atacante encuentre otro mensaje de modo que sus hashes sean idénticos.

  2. Dado un par de mensajes, m 'ym, es computacionalmente inviable encontrar dos tales que h (m) = h (m')

Los dos casos no son lo mismo. En el primer caso, hay un hash preexistente para el que estás tratando de encontrar una colisión. En el segundo caso, se está tratando de encontrar alguna dos mensajes que entran en colisión. La segunda tarea es significativamente más fácil debido a la "paradoja" de cumpleaños.

Cuando el rendimiento no es un gran problema, siempre debe usar una función hash segura. Hay ataques muy inteligentes que se pueden realizar forzando colisiones en un hash. Si usa algo fuerte desde el principio, se protegerá contra estos.

No use MD5 o SHA-1 en nuevos diseños. La mayoría de los criptógrafos, incluido yo, los considerarían rotos. La principal fuente de debilidad en ambos diseños es que la segunda propiedad, que describí anteriormente, no es válida para estas construcciones. Si un atacante puede generar dos mensajes, m y m ', ambos hash al mismo valor pueden usar estos mensajes en su contra. SHA-1 y MD5 también sufren ataques de extensión de mensajes, que pueden debilitar fatalmente su aplicación si no tiene cuidado.

Un hash más moderno como Whirpool es una mejor opción. No sufre estos ataques de extensión de mensajes y utiliza las mismas matemáticas que AES para probar la seguridad contra una variedad de ataques.

¡Espero que ayude!

Simon Johnson
fuente
1
Creo que la recomendación de la función hash criptográfica es un consejo realmente malo en este caso.
Slava
@Slava: ¿Por qué? ¿Cuáles son sus razones para decir que una "función hash criptográfica es realmente un mal consejo en este caso?" ¿Por qué es un mal consejo? ¿Cuáles son las desventajas relativas que lo hacen así?
Déjame pensarlo el
2
@Mowzer debido a que una función de hash que se usa en el mapa de hash debe ser rápida y ligera (suponiendo que todavía proporcione un buen hash), los cripto hash explícitamente fueron muy costosos para evitar el ataque de fuerza bruta.
Slava
1

Lo que estás diciendo aquí es que quieres tener uno que tenga resistencia a la colisión. Intenta usar SHA-2. O intente utilizar un cifrado de bloque (bueno) en una función de compresión unidireccional (nunca lo había intentado antes), como AES en el modo Miyaguchi-Preenel. El problema con eso es que necesita:

1) tener una vía intravenosa. Intenta usar los primeros 256 bits de las partes fraccionarias de la constante de Khinchin o algo así. 2) tener un esquema de relleno. Fácil. Llévelo de un hash como MD5 o SHA-3 (Keccak [pronunciado 'ket-chak']). Si no te importa la seguridad (algunos otros dijeron esto), mira FNV o look2 de Bob Jenkins (en realidad soy el primero que recomienda look2) También prueba MurmurHash, es rápido (mira esto: .16 cpb )

Gavriel Feria
fuente