Dlaczego warto używać liczby pierwszej w hashCode?

Zastanawiałam się, dlaczego w metodzie klasy hashCode() używane są liczby pierwsze? Na przykład przy użyciu Eclipse do wygenerowania mojej metody hashCode() zawsze używana jest liczba pierwsza 31:

public int hashCode() {
     final int prime = 31;
     //...
}

Referencje:

Tutaj jest dobry podkład na Hashcode i artykuł o tym, jak działa haszowanie, które znalazłem (C# , ale pojęcia są przenoszone): Eric Lippert ' s Guidelines and rules for GetHashCode()

Author: Eric Lippert, 2010-09-01

8 answers

Ponieważ chcesz liczbę, którą mnożysz przez i liczbę wiadrów, do których wkładasz, aby mieć ortogonalne faktoryzacje pierwsze.

Załóżmy, że jest 8 wiadrów do włożenia. Jeśli liczba, której używasz do mnożenia jest wielokrotnością 8, to włożony do niej kubełek będzie oznaczony tylko przez najmniej znaczący wpis (ten w ogóle nie pomnożony). Podobne wpisy zderzą się. Nie nadaje się do funkcji hash.

31 jest na tyle duży, że jest mało prawdopodobne, aby Liczba łyżek była przez nią podzielna(a w rzeczywistości współczesne implementacje HashMap java utrzymują liczbę łyżek do potęgi 2).

 86
Author: ILMTitan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-31 21:30:10

Liczby pierwsze są wybierane tak, aby najlepiej rozdzielać dane między hash buckets. Jeśli rozkład wejść jest losowy i równomiernie rozłożony, to wybór kodu/modułu nie ma znaczenia. Ma wpływ tylko wtedy, gdy istnieje pewien wzór na wejściach.

Jest to często przypadek, gdy mamy do czynienia z lokalizacjami pamięci. Na przykład wszystkie 32-bitowe liczby całkowite są wyrównane do adresów podzielnych przez 4. W poniższej tabeli przedstawiono efekty używania prime vs. non-prime Moduł:

Input       Modulo 8    Modulo 7
0           0           0
4           4           4
8           0           1
12          4           5
16          0           2
20          4           6
24          0           3
28          4           0

Zwróć uwagę na rozkład prawie doskonały przy użyciu modułu prime vs. modułu non-prime.

Jednakże, chociaż powyższy przykład jest w dużej mierze wymyślony, ogólna zasada jest taka, że gdy mamy do czynienia z wzorem wejść , użycie modułu liczby pierwszej da najlepszy rozkład.

 116
Author: advait,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-31 21:43:39

Jeśli to coś warte, Effective Java 2nd Edition odręcznie pomija zagadnienie matematyki i po prostu mówi, że powodem wyboru 31 jest:

  • ponieważ jest to dziwna liczba pierwsza, a używanie liczb pierwszych jest "tradycyjne"
  • jest to również jedna moc mniejsza od dwóch, co pozwala na optymalizację bitową

Oto pełny cytat z pozycji 9: zawsze nadpisuj hashCode Kiedy nadpisujesz equals:

Wartość 31 została wybrana, ponieważ jest dziwna prime. Gdyby było równe i mnożenie przepełnione, informacja zostałaby utracona, ponieważ mnożenie przez 2 jest równoznaczne z przesunięciem. Zaleta stosowania prime jest mniej wyraźna, ale jest tradycyjna.

Ładną właściwością 31 jest to, że mnożenie można zastąpić przesunięciem (§15.19) i odejmowanie dla lepszej wydajności:

 31 * i == (i << 5) - i

Nowoczesne maszyny wirtualne wykonują tego rodzaju optymalizację automatycznie.


Podczas gdy przepis w tym punkcie daje rozsądnie dobre funkcje hashowe, nie dają najnowocześniejszych funkcji hashowych, ani biblioteki platformy Java nie dostarczają takich funkcji hashowych od wydania 1.6. Pisanie takich funkcji hashowych jest tematem badawczym, najlepiej pozostawionym matematykom i informatykom teoretycznym.

Być może późniejsze wydanie platformy dostarczy najnowocześniejszych funkcji hashowych dla swoich klas i metod użytkowych, aby umożliwić przeciętnym programistom konstruowanie takich funkcji hashowych. W międzyczasie techniki opisane w tej pozycji powinny być odpowiednie dla większości zastosowań.

W uproszczeniu można powiedzieć, że użycie mnożnika z licznymi dzielnikami spowoduje więcej kolizji hashowych. Ponieważ dla efektywnego hashowania chcemy zminimalizować liczbę kolizji, staramy się użyć mnożnika, który ma mniej dzielników. Liczba pierwsza z definicji ma dokładnie dwa odrębne, dodatnie dzielniki.

Podobne pytania

 22
Author: polygenelubricants,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:17:58

Słyszałem, że 31 zostało wybrane tak, że kompilator może zoptymalizować mnożenie do lewego przesunięcia 5 bitów, a następnie odjąć wartość.

 4
Author: Steve Kuo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-31 22:53:41

Oto cytat trochę bliżej źródła.

Sprowadza się do:

  • 31 jest liczbą pierwszą, która zmniejsza kolizje
  • 31]}
  • rozsądny kompromis w prędkości
 2
Author: John,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-31 21:15:55

Najpierw obliczysz wartość hash modulo 2^32 (wielkość int), więc chcesz coś względnie prime do 2^32 (względnie prime oznacza, że nie ma wspólnych dzielników). Każdy nieparzysty numer by do tego pasował.

Wtedy dla danej tabeli hash indeks jest zwykle obliczany z wartości hash modulo wielkości tabeli hash, więc chcesz coś, co jest względnie pierwsze do wielkości tabeli hash. Często rozmiary tabel skrótów są wybierane jako liczby pierwsze z tego powodu. W w przypadku Javy implementacja Sun upewnia się, że rozmiar jest zawsze potęgą dwóch, więc tutaj też wystarczy Nieparzysta liczba. Istnieje również dodatkowe masowanie klawiszy skrótu w celu dalszego ograniczenia kolizji.

Złym skutkiem, jeśli tabela hash i mnożnik mają wspólny czynnik n, może być to, że w pewnych okolicznościach zostaną użyte tylko 1/N wpisów w tabeli hash.

 2
Author: starblue,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-01 20:04:25

Ogólnie pomaga osiągnąć bardziej równomierne rozprzestrzenianie się danych między zasobnikami hash, szczególnie w przypadku kluczy o niskiej entropii.

 0
Author: fennec,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-31 21:13:47

31 jest również specyficzna dla Java HashMap, która używa int jako typu danych hash. Tak więc maksymalna pojemność 2^32. Nie ma sensu używać większych liczb pierwszych Fermata lub Mersenne ' a.

 0
Author: DED,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-05 15:01:31