Почему 5381 и 33 настолько важны в алгоритме djb2?

алгоритм djb2 имеет хеш-функцию для строк.

unsigned long hash = 5381;
int c;

while (c = *str++)
    hash = ((hash << 5) + hash) + c; /* hash * 33 + c */

Почему 5381 и 33 так важны?

Ответ 1

Эта хэш-функция похожа на Линейный конгруэнтный генератор (LCG - простой класс функций, которые генерируют серию псевдо-случайных чисел), который обычно имеет вид:

X = (a * X) + c;  // "mod M", where M = 2^32 or 2^64 typically

Обратите внимание на сходство с хэш-функцией djb2... a = 33, M = 2 ^ 32. Для того, чтобы LCG имел "полный период" (то есть как бы он ни был случайным), a должен иметь определенные свойства:

  • a-1 делится на все простые множители M (a-1 равно 32, которое делится на 2, единственный простой множитель 2 ^ 32)
  • a-1 кратно 4, если M кратно 4 (да и да)

Кроме того, c и M должны быть взаимно простыми (что будет верно для нечетных значений c).

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

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

EDIT: Этот хороший ответ объясняет, почему 33 и 5381 были выбраны по практическим соображениям.

Ответ 2

В 5381 году Дан Бернстайн (djb2) говорит в в этой статье:

[...] практически любой хороший мультипликатор работает. Я думаю, вы беспокоитесь о том, что 31c + d не охватывает разумный диапазон хеширования значения, если c и d находятся между 0 и 255. Вот почему, когда я обнаружил 33 хэш-функции и начал использовать ее в моих компрессорах, я начал с хеш-значением 5381. Я думаю, вы обнаружите, что это так же, как а также множитель 261.

Весь поток здесь, если вам интересно.

Ozan Yigit имеет страницу с хэш-функциями, в которой говорится:

[...] магия числа 33 (почему она работает лучше, чем многие другие константы, простые или нет) никогда не была должным образом объяснена.

Ответ 3

33 был выбран потому, что:

1) Как указано выше, умножение легко вычислить с помощью shift и add.

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

3) Смещение 5 относительно просто с 32 (количество бит в регистре), что помогает с лавиной. Хотя в строке осталось достаточно символов, каждый бит входного байта будет в конечном итоге взаимодействовать с каждым предыдущим битом ввода.

4) Смещение 5 является хорошей суммой сдвига при рассмотрении символьных данных ASCII. Символ ASCII может рассматриваться как 4-битный селектор типа символа и 4-разрядный селектор типа символа. Например. все цифры имеют 0x3 в первых 4 битах. Таким образом, 8-битный сдвиг приведет к тому, что биты с определенным значением будут в основном взаимодействовать с другими битами, имеющими то же значение. 4-битный или 2-битный сдвиг аналогичным образом создавал бы сильные взаимодействия между битами с одинаковым вниманием. 5-битный сдвиг приводит к тому, что многие из четырех младших бит символа сильно взаимодействуют со многими из четырех верхних бит в одном и том же символе.

Как указано в другом месте, выбор 5381 не слишком важен, и многие другие варианты также должны работать здесь.

Это не быстрая хеш-функция, так как она обрабатывает входной символ за раз и не пытается использовать уровень команды parallelism. Это, однако, легко написать. Качество вывода, деленное на простоту написания кода, скорее всего, попадет в сладкое место.

На современных процессорах умножение происходит намного быстрее, чем когда был разработан этот алгоритм, а другие коэффициенты умножения (например, 2 ^ 13 + 2 ^ 5 + 1) могут иметь схожую производительность, немного лучше выход и быть немного легче писать.

В отличие от ответа выше, хорошая некриптографическая хэш-функция не хочет создавать случайный вывод. Вместо этого, учитывая два входа, которые почти идентичны, он хочет производить разные результаты. Если введенные значения распределены случайным образом, вам не нужна хорошая хеш-функция, вы можете просто использовать произвольный набор бит из вашего ввода. Некоторые из современных хэш-функций (Jenkins 3, Murmur, вероятно, CityHash) обеспечивают лучшее распределение результатов, чем случайные данные, которые очень похожи.

Ответ 4

Может быть, потому что 33 == 2^5 + 1 и многие алгоритмы хэширования используют 2^n + 1 как их множитель?

Кредит Джером Бергер

Update:

Это, по-видимому, подтверждается текущей версией программного пакета djb2, первоначально взятой из: cdb

Заметки, которые я связывал, чтобы описать сердце алгоритма хэширования как использование h = ((h << 5) + h) ^ c для хэширования... x << 5 - это быстрый аппаратный способ использования 2 ^ 5 в качестве множителя.