xoré uma função padrão perigosa a ser usada no hash. É melhor que ande or, mas isso não diz muito.
xoré simétrico, então a ordem dos elementos é perdida. Então "bad", o hash combinará o mesmo que "dab".
xor mapeia valores idênticos aos pares para zero e evite mapear valores "comuns" para zero:
Então, (a,a)é mapeado para 0 e (b,b)também para 0. Como esses pares são quase sempre mais comuns do que a aleatoriedade pode implicar, você acaba com muitas colisões em zero do que deveria.
Com esses dois problemas, xoracaba sendo um combinador de hash que parece meio decente na superfície, mas não após uma inspeção mais aprofundada.
No hardware moderno, adicionar normalmente tão rápido quanto xor(provavelmente usa mais energia para fazer isso, é certo). A tabela verdade de Adding é semelhante à xordo bit em questão, mas também envia um bit para o próximo bit quando ambos os valores são 1. Isso significa que apaga menos informações.
Portanto, hash(a) + hash(b)é melhor do hash(a) xor hash(b)que se a==b, o resultado for em hash(a)<<1vez de 0.
Isso permanece simétrico; portanto, "bad"e "dab"obter o mesmo resultado continua sendo um problema. Podemos quebrar essa simetria por um custo modesto:
hash(a)<<1 + hash(a) + hash(b)
aka hash(a)*3 + hash(b). ( hash(a)é recomendável calcular uma vez e armazenar se você usar a solução de turno). Qualquer constante ímpar, em vez de 3, mapeará bijetivamente um knúmero inteiro não assinado de "bits" para si próprio, pois o mapa em números inteiros não assinados é o módulo matemático 2^kpara alguns k, e qualquer constante ímpar é relativamente primordial 2^k.
Para uma versão ainda mais sofisticada, podemos examinar o boost::hash_combineque é efetivamente:
size_t hash_combine( size_t lhs, size_t rhs ) {
lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
return lhs;
}
aqui adicionamos algumas versões deslocadas de seedcom uma constante (que é basicamente aleatória se 0es 1- em particular, é o inverso da proporção áurea como uma fração de ponto fixo de 32 bits) com alguma adição e um xor. Isso quebra a simetria, e introduz alguns "ruído" se os valores hash de entrada são pobres (ou seja, imaginar cada hashes de componentes para 0 - as alças acima bem, gerando uma mancha de 1e 0. S após cada combinar meu ingênuo 3*hash(a)+hash(b)simplesmente emite um 0em Aquele caso).
(Para aqueles que não estão familiarizados com C / C ++, a size_té um valor inteiro não assinado que é grande o suficiente para descrever o tamanho de qualquer objeto na memória. Em um sistema de 64 bits, geralmente é um número inteiro não assinado de 64 bits. Em um sistema de 32 bits , um número inteiro não assinado de 32 bits.)