Digamos que tiene dos hashes H(A)
y H(B)
quiere combinarlos. He leído que una buena forma de combinar dos hashes es para XOR
ellos, por ejemplo XOR( H(A), H(B) )
.
La mejor explicación que he encontrado se trata brevemente aquí en estas pautas de función hash :
XORing dos números con distribución más o menos aleatoria da como resultado otro número aún con distribución más o menos aleatoria *, pero que ahora depende de los dos valores.
...
* En cada bit de los dos números a combinar, se emite un 0 si los dos bits son iguales, de lo contrario un 1. En otras palabras, en el 50% de las combinaciones, se emitirá un 1. Entonces, si los dos bits de entrada tienen cada uno una probabilidad de 50-50 de ser 0 o 1, entonces también lo tendrá el bit de salida.
¿Puede explicar la intuición y / o las matemáticas detrás de por qué XOR debería ser la operación predeterminada para combinar funciones hash (en lugar de OR o AND, etc.)?
cryptography
bit-manipulation
hash
probability
xor
Nate Murray
fuente
fuente
Respuestas:
Suponiendo entradas uniformemente aleatorias (1 bit), la distribución de probabilidad de salida de la función AND es 75%
0
y 25%1
. Por el contrario, OR es 25%0
y 75%1
.La función XOR es 50%
0
y 50%1
, por lo tanto, es buena para combinar distribuciones de probabilidad uniformes.Esto se puede ver escribiendo tablas de verdad:
Ejercicio: ¿Cómo muchas funciones lógicas de dos entradas de 1 bit
a
yb
tienen esta distribución uniforme de salida? ¿Por qué XOR es el más adecuado para el propósito indicado en su pregunta?fuente
(0, a & b, a > b, a, a < b, b, a % b, a | b, !a & !b, a == b, !b, a >= b, !a, a <= b, !a | !b, 1)
, las siguientes tienen 50% -50% de distribuciones de 0s y 1s, suponiendo que ayb tienen 50% -50% de distribuciones de 0s y 1s:a, b, !a, !b, a % b, a == b
es decir, lo contrario de XOR (EQUIV) podría haber sido utilizado también ...a, b, !a, !b
tendrá la misma distribución que sus respectivas entradas, perderá la entropía de la otra entrada. Es decir, XOR es el más adecuado para combinar hashes porque queremos capturar entropía tanto de a como de b.(a,a)
y(b,b)
ambos productos cero, que en muchos (la mayoría?) De los casos aumenta en gran medida la probabilidad de colisiones en las estructuras de datos basados en hash.xor
es una función predeterminada peligrosa para usar cuando se usa hashing. Es mejor queand
yor
, pero eso no dice mucho.xor
es simétrico, por lo que se pierde el orden de los elementos. Entonces"bad"
, el hash combinará lo mismo que"dab"
.xor
asigna valores idénticos por pares a cero, y debe evitar asignar valores "comunes" a cero:Por lo tanto,
(a,a)
se asigna a 0, y(b,b)
también se asigna a 0. Como tales pares son casi siempre más comunes de lo que podría implicar la aleatoriedad, terminas con muchas colisiones en cero de lo que deberías.Con estos dos problemas,
xor
termina siendo un combinador de hash que parece medio decente en la superficie, pero no después de una inspección adicional.En el hardware moderno, agregar generalmente casi tan rápido como
xor
(probablemente use más potencia para lograr esto, es cierto). Agregar la tabla de verdad es similar alxor
bit en cuestión, pero también envía un bit al siguiente bit cuando ambos valores son 1. Esto significa que borra menos información.Entonces
hash(a) + hash(b)
es mejor quehash(a) xor hash(b)
en eso sia==b
, el resultado es enhash(a)<<1
lugar de 0.Esto sigue siendo simétrico; por lo que el
"bad"
y"dab"
conseguir el mismo resultado sigue siendo un problema. Podemos romper esta simetría por un costo modesto:aka
hash(a)*3 + hash(b)
. (hash(a)
se recomienda calcular una vez y almacenar si usa la solución de turno). Cualquier constante impar en lugar de mapeará bijetivamente un entero sin signo3
"k
-bit" consigo mismo, ya que el mapa en enteros sin signo es un módulo matemático2^k
para algunosk
, y cualquier constante impar es relativamente primo2^k
.Para una versión aún más elegante, podemos examinar
boost::hash_combine
, que es efectivamente:aquí agregamos algunas versiones desplazadas de
seed
con una constante (que es básicamente0
s y1
s al azar , en particular, es la inversa de la proporción áurea como una fracción de punto fijo de 32 bits) con alguna suma y un xor. Esto rompe la simetría, e introduce un poco de "ruido" si los valores hash entrantes son pobres (es decir, imaginar cada hashes componentes a 0 - las manijas por encima de ella, así, generar una mancha de1
y0
. S después de cada combinar mi ingenua3*hash(a)+hash(b)
simplemente generan una0
en Ese caso).(Para aquellos que no están familiarizados con C / C ++, a
size_t
es un valor entero sin signo que es lo suficientemente grande como para describir el tamaño de cualquier objeto en la memoria. En un sistema de 64 bits, generalmente es un entero sin signo de 64 bits. En un sistema de 32 bits , un entero sin signo de 32 bits).fuente
0x9e3779b9
.A pesar de sus prácticas propiedades de mezcla de bits, XOR no es una buena forma de combinar hashes debido a su conmutatividad. Considere lo que sucedería si almacenara las permutaciones de {1, 2, ..., 10} en una tabla hash de 10 tuplas.
Una opción mucho mejor es
m * H(A) + H(B)
, donde m es un número impar grande.Crédito: El combinador anterior fue un consejo de Bob Jenkins.
fuente
long
ay luego volver a juntar la parte superior con la parte inferior.m = 3
en realidad es una buena opción y muy rápida en muchos sistemas. Tenga en cuenta que para cualquier númerom
entero impar, la multiplicación es módulo2^32
o,2^64
por lo tanto, es invertible, por lo que no está perdiendo ningún bit.Xor puede ser la forma "predeterminada" de combinar hashes, pero la respuesta de Greg Hewgill también muestra por qué tiene sus dificultades: el xor de dos valores hash idénticos es cero. En la vida real, hay hashes idénticos que son más comunes de lo que cabría esperar. Entonces puede encontrar que en estos casos de esquina (no tan infrecuentes), los hashes combinados resultantes son siempre los mismos (cero). Las colisiones de hash serían mucho, mucho más frecuentes de lo que esperas.
En un ejemplo artificial, puede estar combinando contraseñas hash de usuarios de diferentes sitios web que administra. Desafortunadamente, un gran número de usuarios reutiliza sus contraseñas, ¡y una proporción sorprendente de los hashes resultantes son cero!
fuente
Hay algo que quiero señalar explícitamente para otros que encuentran esta página. AND y OR restringen la salida como BlueRaja - Danny Pflughoe está tratando de señalar, pero se puede definir mejor:
Primero quiero definir dos funciones simples que usaré para explicar esto: Min () y Max ().
Min (A, B) devolverá el valor más pequeño entre A y B, por ejemplo: Min (1, 5) devuelve 1.
Max (A, B) devolverá el valor que es mayor entre A y B, por ejemplo: Max (1, 5) devuelve 5.
Si le dan:
C = A AND B
Entonces puedes encontrar que
C <= Min(A, B)
sabemos esto porque no hay nada que puedas Y con los 0 bits de A o B para convertirlos en 1s. Por lo tanto, cada bit cero permanece en un bit cero y cada bit tiene la posibilidad de convertirse en un bit cero (y, por lo tanto, en un valor menor).Con:
C = A OR B
Lo contrario es cierto:
C >= Max(A, B)
con esto, vemos el corolario de la función AND. Cualquier bit que ya sea uno no puede ORingarse para que sea un cero, por lo que permanece como uno, pero cada bit cero tiene la posibilidad de convertirse en uno y, por lo tanto, en un número mayor.Esto implica que el estado de la entrada aplica restricciones en la salida. Si usted Y cualquier cosa con 90, sabe que la salida será igual o menor a 90 independientemente de cuál sea el otro valor.
Para XOR, no hay restricción implícita basada en las entradas. Hay casos especiales en los que puede encontrar que si XOR un byte con 255, obtiene el inverso, pero cualquier byte posible se puede generar a partir de eso. Cada bit tiene la oportunidad de cambiar de estado dependiendo del mismo bit en el otro operando.
fuente
OR
es máximo a nivel de bits , yAND
es bit a bit min .Si tiene
XOR
una entrada aleatoria con una entrada sesgada, la salida es aleatoria. Lo mismo no es cierto paraAND
oOR
. Ejemplo:Como @Greg Hewgill menciona, incluso si ambas entradas son aleatorias, usar
AND
oOR
dará como resultado una salida sesgada.La razón por la que usamos
XOR
sobre algo más complejo es que, bueno, no hay necesidad:XOR
funciona perfectamente y es increíblemente rápido.fuente
Cubra las 2 columnas de la izquierda e intente averiguar qué están usando las entradas solo como salida.
Cuando vio un 1 bit, debería haber deducido que ambas entradas eran 1.
Ahora haz lo mismo para XOR
XOR no regala nada sobre sus entradas.
fuente
El código fuente para varias versiones de
hashCode()
en java.util.Arrays es una gran referencia para, uso en algoritmos de hash sólidos. Se entienden y traducen fácilmente a otros lenguajes de programación.En términos generales, la mayoría de las
hashCode()
implementaciones de múltiples atributos siguen este patrón:Puede buscar otras preguntas y respuestas de StackOverflow para obtener más información sobre la magia detrás
31
y por qué el código Java lo usa con tanta frecuencia. Es imperfecto, pero tiene muy buenas características generales de rendimiento.fuente
string
colisión constring + "AA"
IIRC) y hace mucho tiempo desearon no haber incorporado ese algoritmo en la especificación. Dicho esto, el uso de un número impar más grande con más bits establecidos y la adición de turnos o rotaciones corrige ese problema. La 'mezcla' de MurmurHash3 hace esto.XOR no ignora algunas de las entradas, a veces como OR y AND .
Si toma AND (X, Y) por ejemplo, y alimenta la entrada X con falso, entonces la entrada Y no importa ... y uno probablemente querría que la entrada importara al combinar hashes.
Si toma XOR (X, Y) , AMBAS entradas SIEMPRE importan. No habría valor de X donde Y no importa. Si se cambia X o Y, la salida reflejará eso.
fuente