Что такое хорошая хеш-функция? Я видел много хеш-функций и приложений на моих курсах по структурам данных в колледже, но в основном я понял, что довольно сложно создать хорошую хеш-функцию. Мой профессор, как показывает опыт, чтобы избежать столкновений, сказал:
function Hash(key)
return key mod PrimeNumber
end
(mod - это оператор% в C и подобных языках)
с простым числом, чтобы быть размером хеш-таблицы. Я понимаю, что это довольно быстрая функция, позволяющая избежать столкновений, но как я могу ее улучшить? Есть ли лучшие хэш-функции для строковых клавиш вместо числовых?
В fnv_func тип p [i] - char, что произойдет с h после первой итерации? Это было сделано специально?
@martinatime сказал: В википедии en.wikipedia.org/wiki/Hash_function есть много информации о хэш-функциях, а в нижней части этой статьи partow.net/programming/hashfunctions/index.html содержит алгоритмы, реализованные на разных языках.





Я бы сказал, что главное практическое правило - не катить самостоятельно. Попробуйте использовать что-то, что было тщательно протестировано, например, SHA-1 или что-то в этом роде.
Кажется, ему не нужно ничего криптографически безопасного, поэтому SHA-1 будет излишним.
кстати, хотя никаких коллизий для SHA-1 обнаружено не было, считается, что их обнаружение займет несколько лет или месяцев. Я бы рекомендовал использовать SHA-256.
Для универсальных хешей не существует такой вещи, как «хорошая хеш-функция» (ред. Да, я знаю, что есть такая вещь, как «универсальное хеширование», но я не это имел в виду). В зависимости от контекста качество хеша определяется разными критериями. Два человека уже упоминали SHA. Это криптографический хеш, и он совсем не подходит для хеш-таблиц, о которых вы, вероятно, имеете в виду.
К хеш-таблицам предъявляются самые разные требования. Но все же найти универсальную хорошую хеш-функцию сложно, потому что разные типы данных предоставляют разную информацию, которая может быть хеширована. Как правило, полезно рассматривать информацию все, которую имеет тип в равной степени. Это не всегда легко или даже возможно. По причинам статистики (и, следовательно, столкновения) также важно создать хороший разброс по проблемному пространству, то есть по всем возможным объектам. Это означает, что при хешировании чисел от 100 до 1050 не следует позволять старшей цифре играть большую роль в хеш-функции, потому что для ~ 90% объектов эта цифра будет равна 0. Гораздо важнее оставить последние три цифры. цифры определяют хеш.
Точно так же при хешировании строк важно учитывать все символы, за исключением случаев, когда заранее известно, что первые три символа всех строк будут одинаковыми; учитывая это, то это пустая трата.
На самом деле это один из случаев, когда я советую прочитать, что говорит Кнут в Искусство программирования, vol. 3. Еще одно хорошее чтение - Искусство хеширования Жюльен Уокер.
Конрад, вы, безусловно, правы с теоретической точки зрения, но пробовали ли вы когда-нибудь использовать хеш-функцию Пола Хси, о которой я упоминал в своем комментарии? Это действительно неплохо для множества различных данных!
Хорошая хеш-функция имеет следующие свойства:
Учитывая хэш сообщения, злоумышленник с вычислительной точки зрения не может найти другое сообщение, в котором их хеши идентичны.
Для данной пары сообщений m 'и m вычислительно невозможно найти два таких, что h (m) = h (m')
Два случая нет одинаковы. В первом случае существует уже существующий хэш, для которого вы пытаетесь найти коллизию. Во втором случае вы пытаетесь найти два конфликтующих сообщения любой. Вторая задача значительно проще за счет «парадокса» дня рождения.
Если производительность не так важна, вы всегда должны использовать безопасную хеш-функцию. Есть очень хитрые атаки, которые можно выполнять, вызывая коллизии в хэше. Если вы с самого начала используете что-то сильное, вы обезопасите себя от этого.
Не используйте MD5 или SHA-1 в новых проектах. Большинство криптографов, включая меня, сочли бы их взломанными. Главный источник слабости обоих этих конструкций заключается в том, что второе свойство, которое я обозначил выше, не выполняется для этих конструкций. Если злоумышленник может сгенерировать два сообщения, m и m ', оба хеш-значения которых имеют одно и то же значение, они могут использовать эти сообщения против вас. SHA-1 и MD5 также страдают от атак с расширением сообщений, которые могут фатально ослабить ваше приложение, если вы не будете осторожны.
Более современный хэш, такой как Whirpool, - лучший выбор. Он не страдает от этих атак с расширением сообщений и использует ту же математику, что и AES, для доказательства защиты от различных атак.
Надеюсь, это поможет!
Я думаю, что рекомендация криптографической хеш-функции - действительно плохой совет в этом случае.
@ Слава: Почему? По каким причинам вы говорите, что «криптографическая хеш-функция - действительно плохой совет в данном случае»? Почему это плохой совет? Какие относительные недостатки делают это так?
@Mowzer, поскольку хеш-функция, которая используется в хэш-карте, должна быть быстрой и легкой (при условии, что она по-прежнему обеспечивает хороший хеш-код), криптографические хеш-коды явно должны были быть дорогостоящими в вычислительном отношении, чтобы предотвратить атаку методом грубой силы.
Есть две основные цели хеш-функций:
Невозможно рекомендовать хеш, не зная, для чего вы его используете.
Если вы просто создаете хеш-таблицу в программе, вам не нужно беспокоиться о том, насколько обратим или взломан алгоритм ... SHA-1 или AES для этого совершенно не нужны, вам лучше использовать а вариация FNV. FNV обеспечивает лучшую дисперсию (и, следовательно, меньшее количество столкновений), чем простой простой мод, как вы упомянули, и он более адаптируется к различным размерам ввода.
Если вы используете хеши для сокрытия и аутентификации общедоступной информации (например, хеширования пароля или документа), вам следует использовать один из основных алгоритмов хеширования, проверенных общественностью. Зал хеш-функций - хорошее место для начала.
обновлена ссылка на The Hash Function Lounge: larc.usp.br/~pbarreto/hflounge.html
Насколько хорошо FNV выдерживает коллизию по случаю дня рождения по сравнению, скажем, с таким же количеством бит в SHA1?
@Kevin Пока лавинообразные характеристики хэша хороши (крошечные изменения на входе = большие изменения на выходе), коллизии по случаю дня рождения являются просто функцией битов в хэше. FNV-1a превосходен в этом отношении, и вы можете иметь столько битов в хэше, сколько захотите (хотя требуется немного дополнительных усилий, чтобы получить количество битов, которое не является степенью двойки).
Это пример хорошего, а также пример того, почему вы никогда не захотите его писать. Это хэш Fowler / Noll / Vo (FNV), который в равной степени является гением информатики и чистым вуду:
unsigned fnv_hash_1a_32 ( void *key, int len ) {
unsigned char *p = key;
unsigned h = 0x811c9dc5;
int i;
for ( i = 0; i < len; i++ )
h = ( h ^ p[i] ) * 0x01000193;
return h;
}
unsigned long long fnv_hash_1a_64 ( void *key, int len ) {
unsigned char *p = key;
unsigned long long h = 0xcbf29ce484222325ULL;
int i;
for ( i = 0; i < len; i++ )
h = ( h ^ p[i] ) * 0x100000001b3ULL;
return h;
}
Редактировать:
Вы можете посмотреть на этом сайте некоторую информацию о том, почему выбраны эти значения: isthe.com/chongo/tech/comp/fnv/#fnv-prime
Будьте здоровы. Эта короткая, простая, эффективная, универсальная и эффективная 64-битная хеш-функция была именно тем, что мне было нужно.
Для выполнения «нормального» поиска по хэш-таблице практически по любым данным - эта, написанная Полом Хси, - лучшее, что я когда-либо использовал.
http://www.azillionmonkeys.com/qed/hash.html
Если вас волнует криптографическая безопасность или что-то еще более продвинутое, тогда YMMV. Если вам просто нужна хеш-функция общего назначения для поиска в хеш-таблице, то это то, что вы ищете.
Спасибо за информативную ссылку! Я знаю анализ немного Боба Дженкинса и других, который указывает на неплохие универсально приемлемые хэш-функции, но я еще не встречал этого.
Я читал с сайта Дженкинса, что SFH тогда был одним из лучших, но я думаю, что Murmur мог бы справиться лучше, см. Этот отличный ответ: programmers.stackexchange.com/questions/49550/…
Что означает YMMV?
@cobarzan Ваш пробег может отличаться
Хеш-функция Се ужасна, с на порядок больше коллизий, чем мы хотим. В частности, строки, которые отличаются только последними 4 байтами, могут легко конфликтовать. Если у вас есть 30-символьная строка, которые отличаются последними 4 байтами, после обработки 28 байтов хэши различаются только в последних 2 байтах. Это означает, что вам ГАРАНТИРУЕТСЯ коллизия для одного из оставшихся двухбайтовых значений. (Да, это быстро. Ну и что.)
То, что вы здесь говорите, это то, что вы хотите иметь тот, который использует сопротивление столкновению. Попробуйте использовать SHA-2. Или попробуйте использовать (хороший) блочный шифр с функцией одностороннего сжатия (никогда не пробовал раньше), например AES в режиме Миягути-Принил. Проблема в том, что вам необходимо:
1) иметь IV. Попробуйте использовать первые 256 бит дробных частей константы Хинчина или что-то в этом роде.
2) иметь схему заполнения. Легко. Выбросьте его из хэша, такого как MD5 или SHA-3 (Keccak [произносится как "кет-чак"]).
Если вас не волнует безопасность (некоторые другие сказали это), посмотрите FNV или lookup2 Боба Дженкинса (на самом деле я первый, кто рекомендует lookup2) Также попробуйте MurmurHash, это быстро (проверьте это: 0,16 cpb ).
Хорошая хеш-функция должна
Модуль простого числа не удовлетворяет ни одной из этих точек. Этого просто недостаточно. Часто это лучше, чем ничего, но даже не быстро. Умножение на беззнаковое целое число и взятие модуля степени двойки так же хорошо распределяет значения, что совсем нехорошо, но всего с 2 циклами ЦП это намного быстрее, чем от 15 до 40, которое займет простой модуль ( да, целочисленное деление действительно такое медленное).
Чтобы создать хеш-функцию, которая быстрая и хорошо распределяет значения, лучший вариант - составить ее из быстрых перестановок с меньшим качеством, как это было с PCG для генерации случайных чисел.
Среди прочих полезных перестановок:
Следуя этому рецепту, мы можем создать свой собственный хеш-функция или взять сплитмикс, который протестирован и хорошо принят.
Если требуются криптографические качества, я настоятельно рекомендую использовать функцию семейства sha, которая хорошо протестирована и стандартизирована, но в образовательных целях вы бы сделали ее следующим образом:
Сначала вы берете хорошую некриптографическую хеш-функцию, затем применяете одностороннюю функцию, такую как возведение в степень для простого поля или k, многие приложения (n*(n+1)/2) mod 2^k, перемежающиеся с xorshift, когда k - это количество бит в результирующем хеш-коде.
Рассматривали ли вы использование одной или нескольких из следующих хеш-функций общего назначения: partow.net/programming/hashfunctions/index.html