Buscando una función hash de fuerza intermedia

Tengo un conjunto estático de ~ 35000 cadenas de texto ASCII únicas de 20 a 60 bytes cada una. Quiero introducir un índice único en ellos. La simple numeración no sería deseable por varias razones.

Las funciones de grado criptográfico como MD5 funcionan bien, pero creo que son exageradas. En última instancia, esto es para un proyecto móvil, por lo que soy un poco codicioso tanto con el almacenamiento como con los ciclos de CPU. Por otro lado, probé Adler32 de 32 bits y obtuve colisiones.

¿Alguien puede pensar en una buena función hash que produzca un valor de 64 bits?

preguntado el 30 de agosto de 11 a las 23:08

¿Podría explicar por qué no es deseable numerarlos, pero es deseable algo tan simple de romper como un hash de 64 bits? -

Quiero que el valor de la clave (es decir, el hash) sea invariable con modificaciones menores del conjunto: cadenas adicionales o eliminaciones agregadas. El conjunto de cadenas se actualiza ocasionalmente (no por mí), quiero que los valores hash almacenados conserven su significado. -

Um. ¿No podría simplemente agregar nuevos al final y "retirar" los índices de los eliminados? Funciona bastante bien para tablas de bases de datos con claves primarias incrementales. El problema que veo con eso es el costo de búsqueda del valor de la cadena al índice (querría un Trie, BST o, irónicamente, una tabla hash, cualquiera de los cuales podría ocupar más memoria de la que desea ahorrar) en lugar de cualquier dificultad para cambiar el conjunto. -

Además, quise preguntar antes, ¿cuál es la razón por la que se requiere la singularidad? ¿No funcionaría su mapa hash estándar con una implementación decente con un número limitado de colisiones? ¿Cómo se van a utilizar estas claves? ¿Qué pasa si hay una colisión? Me temo que puede estar intentando reinventar una rueda aquí. -

La lógica empresarial requiere una clave principal. No estoy tratando de reinventar un PK aquí, solo estoy buscando una implementación adecuada. -

5 Respuestas

Debido a que el conjunto de cadenas que tiene es fijo, debería intentar buscar un función hash perfecta, una función hash diseñada específicamente sobre un conjunto de datos para garantizar que no se produzcan colisiones. Hay muchas herramientas para crear funciones hash como estas, una de las cuales, gperf (no debe confundirse con gprof) Sé que está disponible gratuitamente. Sugeriría encarecidamente esto.

Si más tarde termina necesitando cambiar el conjunto de cadenas y desea una función hash simple y liviana, puede considerar usar el Función hash rodante de Rabin-Karp. Se puede calcular para una cadena de longitud n usando O (n) sumas, multiplicaciones y módulos, y asegura que cada dos cadenas tengan valores hash independientes por pares. Además, probablemente podría codificar esto en aproximadamente media hora para probar si funciona mejor o no que la suma de comprobación de Adler.

Dicho esto, usar una función hash conocida como MD5 probablemente sea una buena idea si no está tratando de lograr la seguridad criptográfica. Incluso un simple CRC32 podría ser suficiente en ese caso.

Respondido 31 ago 11, 05:08

MD4 está roto criptográficamente, pero es más rápido que MD5. Fowler – Noll – Vo es una buena función hash no criptográfica. - Rossum

Dado el hecho de que la probabilidad de colisiones disminuye tanto al pasar de 64 bits a 128 bits, consideraría encarecidamente optar por MD5128.

      Max entries before X chance of collision
Bits  10e−18   10e−15   10e−12   10e−9    10e−6    0.1%     1%       25%      50%      75%
----------------------------------------------------------------------------------------------
16    2        2        2        2        2        11       36       1.9e2    3.0e2    4.3e2
32    2        2        2        2.9      93       2.9e3    9.3e3    5.0e4    7.7e4    1.1e5
64    6.1      1.9e2    6.1e3    1.9e5    6.1e6    1.9e8    6.1e8    3.3e9    5.1e9    7.2e9
128   2.6e10   8.2e11   2.6e13   8.2e14   2.6e16   8.3e17   2.6e18   1.4e19   2.2e19   3.1e19
256   4.8e29   1.5e31   4.8e32   1.5e34   4.8e35   1.5e37   4.8e37   2.6e38   4.0e38   5.7e38
384   8.9e48   2.8e50   8.9e51   2.8e53   8.9e54   2.8e56   8.9e56   4.8e57   7.4e57   1.0e58
512   1.6e68   5.2e69   1.6e71   5.2e72   1.6e74   5.2e75   1.6e76   8.8e76   1.4e77   1.9e77

Entonces, con una cadena 35000 (3.5e4), con un hash de 64 bits, esto le da una posibilidad entre 10e ^ -12 y 10e ^ -9 de tener una colisión. Puede que esto no parezca muy alto, pero cuando se trata de hash, 1 en mil millones es bastante fácil de alcanzar.

Al aumentar a 128 bits, se reduce considerablemente a menos de 1 en (mil millones * mil millones).

Respondido 31 ago 11, 03:08

Por supuesto, dado que el conjunto de cadenas es estático, el interrogador puede ejecutar el hash de 64 bits. Si por una probabilidad entre mil millones hay una colisión en el conjunto de datos, agregue una sal y vuelva a intentarlo. Este segundo intento alarga las probabilidades a 1 en mil millones * mil millones sin tener que estirarse a 128 bits. La capacidad para probar una segunda sal llegaría a mil millones al cubo, por lo que estamos bien incluso con un hash no criptográfico de distribución decente, a menos que un atacante pueda elegir las cadenas de 35k. - Steve Jessop

@Steve, si vas tan lejos, entonces también podrías reducirlo a un hash de 32 bits. Eso solo tendrá un 20% de probabilidad de colisión. Mucho de esto se reduce a cómo se usará el hash. - corsiKa

Creo que podría concatenar los valores de dos funciones hash de 32 bits diferentes para obtener un hash de 64 bits.

Para obtener cuatro funciones hash diferentes, usaría un paso de preprocesamiento que altera la entrada a la función hash de alguna manera que no se conmute con los valores en la función hash. Una forma sería utilizar una tabla de búsqueda de 256 bytes para volver a numerar los bytes. Otra podría ser multiplicar cada byte por X mod 257, reemplazando cualquier cosa que produzca 256 = -1 mod 257 por -X mod 257, porque eso no sucederá de otra manera. Tenga en cuenta que (a * 256 + b) mod 257 es a + b mod 257.

Respondido 31 ago 11, 08:08

FWIW hay una función hash no segura con una garantía bastante buena. Como ejemplo, elija un número primo y haga todos sus cálculos en módulo ese número, lo que le da un campo matemático. Divida sus datos en una secuencia de números módulo ese primo y trátelos como los coeficientes de un polinomio. Además de elegir el módulo para su función hash, elige un número x mod el primo y luego evalúa el polinomio en esa x. En teoría, x se elige al azar.

Dos mensajes se asignan al mismo valor si la diferencia de sus polinomios es cero, lo que significa que la x elegida es una raíz de ese polinomio. Un polinomio de grado N tiene como máximo N raíces, por lo que en su caso, si tiene cadenas bastante cortas y elige un módulo grande, eso no es una mala garantía. Creo que vi esto sugerido como una forma más rápida de obtener una función hash segura si encripta el resultado de este cálculo. Creo que se suponía que era más rápido que MD5 porque aunque hacer números primos aritméticos en módulo de 128 bits es caro, alguien pensó que era más barato que hacer MD5.

Respondido 31 ago 11, 22:08

Establecido en 64 bits MurmurHash64B. Puntos extra por el nombre que suena ronroneante.

Respondido el 31 de enero de 14 a las 22:01

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.