Почему настройка HashTable на Prime Number является хорошей практикой?

Я пережил последнее сообщение в блоге Эрика Липперта для Руководства и правила для GetHashCode, когда я ударил этот параграф:

Мы могли бы быть еще более умными здесь; так же, как List изменяет размеры, когда он заполняется, набор ковша может также изменить размер, чтобы средняя длина ковша оставалась низкой. Кроме того, по техническим причинам часто бывает хорошей идеей сделать длину набора ведра простым числом, а не 100. Есть много улучшений, которые мы можем внести в эту хеш-таблицу. Но этот быстрый набросок наивной реализации хеш-таблицы будет делать пока. Я хочу, чтобы это было просто.

Похоже, я что-то упускаю. Почему это хорошая практика, чтобы установить его на простое число?.

Ответ 1

Скажите, что длина вашего ведра равна 2, что делает вычисления мод довольно быстрыми. Это также означает, что выбор ковша определяется исключительно верхними битами m хеш-кода. (Где m = 32 - n, где n - мощность 2). Так что, как будто вы выбрасываете полезные биты хэш-кода сразу.

Или как в этот пост в блоге от 2006 года ставит:

Предположим, что ваша функция hashCode приводит к следующим хэш-кодам среди других {x, 2x, 3x, 4x, 5x, 6x...}, тогда все они собираются в виде всего m кол-во кодов, где m = table_length/GreatestCommonFactor (table_length, x). (Тривиально проверить/вывести это). Теперь вы можете сделать одно из следующих действий, чтобы избежать кластеризации:

...

Или просто сделайте m равным table_length, сделав GreatestCommonFactor (table_length, x) равным 1, т.е. сделав table_length взаимно просты с x. И если x может быть почти любым числом, тогда убедитесь, что table_length является простым числом.

Ответ 2

Вы можете найти людей, которые предлагают два противоположных конца спектра. С одной стороны, выбор простого числа для размера хеш-таблицы уменьшит вероятность столкновений, даже если хеш-функция не слишком эффективно распределяет результаты. Обратите внимание, что если (в простейшем примере для аргументации) определяется мощность 2-го размера, только нижние биты влияют на ведро, тогда как для простого числа будет использоваться большинство бит в результате хеша.

С другой стороны, вы можете получить больше, выбирая лучшую хеш-функцию или даже перефразируя результат хеш-функции, применяя некоторые битовые операции и используя мощность 2 хэш-размера, чтобы ускорить вычисления.

В качестве примера из реальной жизни Java HashTable изначально были реализованы с использованием простых (или почти простых размеров), но с версии Java 1.4 дизайн был изменен на использование мощности двух кодов и добавил вторую быструю хэш-функцию применяется к результату начального хэша. Интересная статья, в которой говорится о том, что изменения можно найти здесь.

Итак, в основном:

  • простое число помогает диспергировать входы по разным ведрам даже в случае не очень хороших хеш-функций.

  • аналогичный эффект может быть достигнут путем последующей обработки результата хеш-функции и использования мощности 2 размера для ускорения работы по модулю (бит-маска) и компенсации пост-обработки.

Ответ 3

Потому что это создает лучшую хэш-функцию и уменьшает количество возможных столкновений. Это объясняется в Выбор хорошей функции хеширования:

Основным требованием является то, что функция должна обеспечивать единую распределение хэш-значений. неравномерное распределение увеличивает количество столкновений и стоимость разрешая их.

Распределение должно быть однородным только для размеров таблиц s, которые встречаются в приложение. В частности, если один использует динамическое изменение размера с точным удвоение и уменьшение пополам s, хэш функция должна быть однородной только тогда, когда s - сила двух. С другой стороны, некоторые алгоритмы хеширования обеспечивают однородные хэши только тогда, когда s является простым число.