xorjest niebezpieczną funkcją domyślną do użycia podczas mieszania. Jest lepszy niż andi or, ale to niewiele mówi.
xorjest symetryczny, więc kolejność elementów zostaje utracona. Więc "bad"hash będzie łączyć to samo co "dab".
xor odwzorowuje parami identyczne wartości na zero i należy unikać mapowania „wspólnych” wartości na zero:
Jest więc (a,a)mapowane na 0, a (b,b)także na 0. Ponieważ takie pary są prawie zawsze bardziej powszechne, niż może to sugerować przypadkowość, kończy się z dużą liczbą kolizji na poziomie zerowym, niż powinieneś.
Z tymi dwoma problemami xorkończy się sumatorem mieszania, który wygląda na w połowie przyzwoicie, ale nie po dalszej kontroli.
Na nowoczesnym sprzęcie, dodawanie zwykle tak szybko, jak xor(trzeba przyznać, że prawdopodobnie zużywa więcej energii, aby to zrobić). Dodawanie tabeli prawdy jest podobne do tego xorna danym bicie, ale wysyła również trochę do następnego bitu, gdy obie wartości są równe 1. Oznacza to, że usuwa mniej informacji.
Więc hash(a) + hash(b)jest lepsze niż hash(a) xor hash(b)w przypadku a==b, gdy wynikiem jest hash(a)<<1zamiast 0.
To pozostaje symetryczne; więc "bad"i "dab"otrzymuję ten sam rezultat pozostaje problemem. Możemy złamać tę symetrię niewielkim kosztem:
hash(a)<<1 + hash(a) + hash(b)
aka hash(a)*3 + hash(b). ( hash(a)zaleca się jednorazowe obliczenie i przechowywanie, jeśli używasz rozwiązania zmianowego). Każda nieparzysta stała zamiast 3będzie bijektywnie odwzorowywać " k-bit" liczbę całkowitą bez znaku na siebie, ponieważ mapowanie na liczbach całkowitych bez znaku jest 2^kdla niektórych matematyczne modulo k, a każda nieparzysta stała jest względnie pierwsza 2^k.
Aby uzyskać jeszcze bardziej wyszukaną wersję, możemy sprawdzić boost::hash_combine, co jest efektywne:
size_t hash_combine( size_t lhs, size_t rhs ) {
lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
return lhs;
}
tutaj dodajemy razem kilka przesuniętych wersji seedze stałą (która jest w zasadzie losowym 0s i 1s - w szczególności jest to odwrotność złotego podziału jako 32-bitowy ułamek z punktem stałym) z dodatkiem i xor. To łamie symetrię i wprowadza pewien „szum”, jeśli przychodzące wartości skrótu są słabe (tj. Wyobraź sobie, że każdy komponent hashuje do 0 - powyższe działa dobrze, generując rozmazanie 1i 0s po każdym połączeniu. Mój naiwny 3*hash(a)+hash(b)po prostu wyświetla 0in ta walizka).
(Dla tych, którzy nie znają języka C / C ++, a size_tjest liczbą całkowitą bez znaku, która jest wystarczająco duża, aby opisać rozmiar dowolnego obiektu w pamięci. W systemie 64-bitowym jest to zwykle 64-bitowa liczba całkowita bez znaku. W systemie 32-bitowym , 32-bitowa liczba całkowita bez znaku).