¿Por qué XOR es la forma predeterminada de combinar hashes?


Digamos que tienes dos hashes H(A) y H(B) y quieres combinarlos. He leído que una buena manera de combinar dos hashes es XOR ellos, por ejemplo, XOR( H(A), H(B) ).

La mejor explicación que he encontrado se toca brevemente aquí en estas directrices de la función hash :

El cifrado xor de dos números con aproximadamente la distribución aleatoria de los resultados en otro número aún con aproximadamente la distribución al azar*, pero que ahora depende de los dos valores.
...
* En cada bit de los dos números a combinar, un 0 es salida si los dos bits son iguales, de lo contrario un 1. En otras palabras, en el 50% de las combinaciones, se emitirá un 1. Así que si los dos bits de entrada cada uno tiene una probabilidad de aproximadamente 50-50 de ser 0 o 1, entonces también lo hará 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 Y etc.)?

Author: Nate Murray, 2011-05-05

8 answers

Suponiendo entradas uniformemente aleatorias (1-bit), la distribución de probabilidad de salida de la función AND es del 75% 0 y del 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:

 a | b | a AND b
---+---+--------
 0 | 0 |    0
 0 | 1 |    0
 1 | 0 |    0
 1 | 1 |    1

 a | b | a OR b
---+---+--------
 0 | 0 |    0
 0 | 1 |    1
 1 | 0 |    1
 1 | 1 |    1

 a | b | a XOR b
---+---+--------
 0 | 0 |    0
 0 | 1 |    1
 1 | 0 |    1
 1 | 1 |    0

Ejercicio: ¿Cuántas funciones lógicas de dos entradas de 1 bit a y b tienen esta distribución de salida uniforme? Por qué es XOR el más adecuado para el propósito indicado en su pregunta?

 98
Author: Greg Hewgill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-04 20:13:18

Xor es una función predeterminada peligrosa para usar cuando se hace hash. Es mejor que y y o, pero eso no dice mucho.

Xor es simétrico, por lo que el orden de los elementos se pierde. Así que "bad" combinará el hash igual que "dab".

Xor asigna valores idénticos a cero, y debe evitar asignar valores "comunes" a cero:

Así que (a,a) se asigna a 0, y (b,b) también se asigna a 0. Como tales pares son más comunes que la aleatoriedad podría implicar, usted termina con lejos a muchas colisiones a cero de las que deberías.

Con estos dos problemas, xor termina siendo un combinador de hash que se ve medio decente en la superficie, pero no después de una inspección adicional.

En hardware moderno, añadiendo normalmente tan rápido como xor (probablemente usa más potencia para lograr esto, es cierto). La tabla de verdad de adición es similar a xor en el bit en cuestión, pero también envía un bit al siguiente bit cuando ambos valores son 1. Esto borra menos información.

So hash(a) + hash(b) es mejor en que si a==b, el resultado es en cambio hash(a)<<1 en lugar de 0.

Esto permanece simétrico. Podemos romper esta simetría por un costo modesto:

hash(a)<<1 + hash(a) + hash(b)

Aka hash(a)*3 + hash(b). (se recomienda calcular hash(a) una vez y almacenar si utiliza la solución shift). Cualquier constante impar en lugar de 3 se mapeará bijectivamente una size_t (o constante sin signo de k-bit) a sí misma, ya que map on unsigned constants is math modulo 2^k for some k, y cualquier constante impar es 2^k.

Para una versión aún más elegante, podemos examinar boost::hash_combine, que es efectivamente:

size_t hash_combine( size_t lhs, size_t rhs ) {
  lhs^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
  return lhs;
}

Aquí sumamos algunas versiones cambiadas de seed con una constante (que es básicamente aleatoria 0 s y 1 s particular en particular, es la inversa de la proporción áurea como una fracción de punto fijo de 32 bits) con alguna adición y un xor. Esto rompe la simetría, e introduce algo de "ruido" si los valores hash entrantes son pobres ( es decir, imagine cada componente hashes a 0 the lo anterior lo maneja bien, generando una mancha de 1 y 0 s después de cada combinación. Mine simplemente produce un 0).

Para aquellos que no están familiarizados con C/C++, un size_t es un valor entero sin signo que es lo suficientemente grande como para describir el tamaño de cualquier objeto en 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.

 130
Author: Yakk - Adam Nevraumont,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-14 21:21:35

A pesar de sus prácticas propiedades de mezcla de bits, XOR es no una buena manera de combinar hashes debido a su conmutatividad. Considerar qué sucedería si se almacenan 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.

 28
Author: Marcelo Cantos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-24 21:37:12

Xor puede ser la forma "predeterminada" de combinar hashes, pero la respuesta de Greg Hewgill también muestra por qué tiene sus trampas: El xor de dos valores hash idénticos es cero. En la vida real, hay hashes idénticos son más comunes de lo que uno podría haber esperado. 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, usted podría ser combinar contraseñas hash de usuarios de diferentes sitios web que administras. Desafortunadamente, un gran número de usuarios reutilizan sus contraseñas, ¡y una sorprendente proporción de los hashes resultantes son cero!

 16
Author: Leo Goodstadt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-19 00:09:13

Hay algo que quiero señalar explícitamente para otros que encuentren esta página. Y y O restringir 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 que es menor 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 se le administra: 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 hacerlos 1s. Así que cada bit cero permanece como un bit cero y cada bit tiene una oportunidad de convertirse en un bit cero (y por lo tanto un valor más pequeño).

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 es un uno no puede ser ored en ser un cero, por lo que sigue siendo un uno, pero cada bit cero tiene una oportunidad de convertirse en uno, y por lo tanto un número mayor.

Esto implica que el estado de la entrada aplica restricciones a la salida. Si usted Y cualquier cosa con 90, sabe que la salida será igual o inferior 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 donde usted puede encontrar que si usted XOR un byte con 255 que usted consigue el inverso pero cualquier byte posible puede ser salida de eso. Cada bit tiene la oportunidad de cambiar de estado dependiendo del mismo bit en el otro operando.

 8
Author: Corey Ogburn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-08 14:29:29

Si XOR una entrada aleatoria con una entrada sesgada, la salida es aleatoria. Lo mismo no es cierto para AND o OR. Ejemplo:

00101001 XOR 00000000 = 00101001
00101001 AND 00000000 = 00000000
00101001 OR  11111111 = 11111111

Como menciona @Greg Hewgill, incluso si ambas entradas son aleatorias, usar AND o OR 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 estúpido-rápido.

 3
Author: BlueRaja - Danny Pflughoeft,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-04 20:13:32

El código fuente para varias versiones de hashCode() en java.útil.Arrays es una gran referencia para algoritmos hash sólidos de uso general. Se entienden fácilmente y se traducen a otros lenguajes de programación.

En términos generales, la mayoría de las implementaciones de múltiples atributos hashCode() siguen este patrón:

public static int hashCode(Object a[]) {
    if (a == null)
        return 0;

    int result = 1;

    for (Object element : a)
        result = 31 * result + (element == null ? 0 : element.hashCode());

    return result;
}

Puede buscar en otras preguntas y respuestas de StackOverflow para obtener más información sobre la magia detrás de 31 y por qué el código Java lo usa con tanta frecuencia. Es imperfecto, pero tiene muy buenas características generales de rendimiento.

 1
Author: kevinarpe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-12 15:43:57

Cubre las 2 columnas de la izquierda e intenta averiguar qué entradas están usando solo la salida.

 a | b | a AND b
---+---+--------
 0 | 0 |    0
 0 | 1 |    0
 1 | 0 |    0
 1 | 1 |    1

Cuando vio un 1-bit debería haber averiguado que ambas entradas eran 1.

Ahora haga lo mismo para XOR

 a | b | a XOR b
---+---+--------
 0 | 0 |    0
 0 | 1 |    1
 1 | 0 |    1
 1 | 1 |    0

XOR no regala nada sobre sus entradas.

 1
Author: Robert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 10:58:25