Зазвичай проста хеш-функція працює, приймаючи "складові частини" вхідних даних (символи у випадку рядка) та множуючи їх на сили деякої константи та додаючи їх разом у якийсь цілий тип. Так, наприклад, типовий (хоча і не особливо хороший) хеш рядка може бути:
(first char) + k * (second char) + k^2 * (third char) + ...
Тоді, якщо введено купу рядків, які мають один і той же перший знак, то всі результати будуть однаковими по модулю k, принаймні до тих пір, поки цілий тип не переповниться.
[Як приклад, строковий хеш-код Java на явно схожий на це - він робить символи у зворотному порядку, з k = 31. Таким чином, ви отримуєте яскраві зв'язки по модулю 31 між рядками, які закінчуються однаково, і яскраві зв'язки по модулю 2 ^ 32 між рядками, які однакові, за винятком кінця. Це серйозно не зіпсує поведінку хештеля.]
Хештеб працює, приймаючи модуль хеша на кількість відра.
У хештелі важливо не створювати зіткнень для ймовірних випадків, оскільки зіткнення знижують ефективність хештеля.
Тепер, припустимо, хтось вкладає цілу купу значень у хешблет, які мають певні стосунки між елементами, як і всі, які мають однаковий перший символ. Я б сказав, що це досить передбачувана схема використання, тому ми не хочемо, щоб він спричинив занадто багато зіткнень.
Виявляється, "через природу математики", якщо константа, яка використовується в хеші, і кількість відра, є спільними , то в деяких поширених випадках зіткнення мінімізуються. Якщо вони не є копром, то існують деякі досить прості відносини між входами, для яких зіткнення не зведені до мінімуму. Усі хеші виходять рівним за модулем загальним коефіцієнтом, а значить, всі вони потраплятимуть на 1 / n-ту відра, які мають це значення за модулем загальним фактором. Ви отримуєте n разів більше зіткнень, де n - загальний фактор. Оскільки n становить щонайменше 2, я б сказав, що для досить простого випадку використання неприпустимо генерувати принаймні вдвічі більше зіткнень, ніж зазвичай. Якщо якийсь користувач збирається розбити наш дистриб'ютор у відрах, ми хочемо, щоб це була випадкова випадковість, а не просте передбачуване використання.
Тепер реалізація хештету очевидно не має контролю над елементами, що вкладаються до них. Вони не можуть перешкодити їм бути пов’язаними. Тож потрібно зробити так, щоб константа та кількість відра були одночасно. Таким чином, ви не покладаєтесь лише на "останній" компонент, щоб визначити модуль відра стосовно деякого невеликого загального чинника. Наскільки я знаю, вони не повинні бути прем'єр-міністром, щоб досягти цього, лише копром.
Але якщо хеш-функція та хештел записуються незалежно, то хештил не знає, як працює хеш-функція. Це може бути використання константи з малими факторами. Якщо вам пощастить, це може працювати зовсім інакше і бути нелінійним. Якщо хеш досить хороший, то будь-яке кількість відра просто чудово. Але параноїдальний хештель не може брати на себе хорошу хеш-функцію, тому слід використовувати просту кількість відро. Аналогічно, параноїдальна хеш-функція повинна використовувати велику первинну константу, щоб зменшити ймовірність того, що хтось використовує ряд відра, які, мабуть, мають спільний фактор з постійною.
На практиці я думаю, що цілком нормально використовувати потужність 2 як кількість відра. Це зручно і економить необхідність пошуку або попереднього вибору простого числа потрібної величини. Тож ви покладаєтесь на хеш-функцію не використовувати навіть множників, що, як правило, є безпечним припущенням. Але ви все ще можете отримувати випадкові поводження з хешируванням на основі хеш-функцій, таких як вище, і кількість простих відра може допомогти надалі.
Якщо говорити про принцип, що "все має бути першочерговим", наскільки я знаю, є достатньою, але не необхідною умовою для хорошого розподілу хештелів. Це дозволяє всім взаємодіяти без необхідності припускати, що інші дотримуються того самого правила.
[Редагувати: є ще одна, більш спеціалізована причина використання простої кількості відра, тобто якщо ви керуєтесь зіткненнями з лінійним зондуванням. Тоді ви обчислюєте хід з хеш-коду, і якщо цей крок виявляється фактором кількості відра, то ви можете робити зонди (bucket_count / stride) зонди, перш ніж ви повернетесь з того місця, де ви почали. Випадки, яких ви найбільше хочете уникнути, - це stride = 0, звичайно, який повинен бути спеціалізованим, але щоб уникнути також bucket_count / stride, котрий має спеціальний об'єм, рівний малому цілому числу, ви можете просто зробити bucket_count prime і не байдуже, що кроку за умови, що це не 0.]