У меня есть много несвязанных именованных вещей, по которым я хотел бы быстро поискать. «Муравьед» всегда везде «трубкозуб», поэтому хеширование строки и повторное использование целого числа будет хорошо работать для ускорения сравнений. Полный набор имен неизвестен (и со временем меняется). Что такое алгоритм быстрого хеширования строк, который будет генерировать небольшие (32 или 16) битовые значения и иметь низкую частоту конфликтов?
Я бы хотел увидеть оптимизированную реализацию, специфичную для C / C++.
На следующей странице представлено несколько реализаций хэш-функций общего назначения, которые являются «производительными» и имеют низкие «уровни конфликтов»: partow.net/programming/hashfunctions/index.html





Взгляните на GNU gperf.
Ура идеальным генераторам хешей!
Идеальное хеширование НЕ подходит для этого приложения, поскольку набор имен неизвестен и меняется. Следовательно, gperf для этого не подойдет.
CRC-32. На это есть около триллиона ссылок в Google.
CRC предназначены для обнаружения и исправления ошибок. Их характеристики распределения обычно не очень хорошие.
Очевидно, что Arachnid никогда не пробовал использовать CRC32 в качестве хэшей. Они хорошо работают.
«CRC32 никогда не предназначался для использования хеш-таблиц. На самом деле нет веских причин использовать его для этой цели». ср. home.comcast.net/~bretm/hash/8.html
Один из Варианты FNV должен соответствовать вашим требованиям. Они быстрые и производят довольно равномерно распределяемые результаты.
Если вы собираетесь использовать FNV, придерживайтесь FNV-1a, поскольку он дает приемлемые результаты на лавинном тесте (см. home.comcast.net/~bretm/hash/6.html). Или просто используйте MurmurHash2, который лучше и по скорости, и по распределению (murmurhash.googlepages.com).
@Steven: Хэш MurmurHash был проанализирован только его автором. Я использовал его в нескольких разных сценариях, и более новая версия FNV, похоже, работает лучше.
@sonicoder: Хотя я не собираюсь перепродавать MurmurHash, простой FNV просто ужасен, а FNV-1a только проходим. Как оказалось, MurmurHash был тщательно проанализирован и признан полезным. Это все еще не криптографический хеш, и коллизии будут, несмотря ни на что, но это все равно огромное улучшение по сравнению с любым типом FNV.
@Steven Sudit: Как я уже сказал, он был проанализирован "только" его автором и никем другим. следовательно, результаты «анализа» не совсем объективны.
@sonicoder: Я буду откровеннее: нет, вы ошибаетесь. Он был проанализирован рядом сторонних организаций, в том числе академических. Посетите Википедию для получения ссылок. Что еще более важно, это не только хорошо в целом, но и конкретные недостатки, которые были обнаружены, были устранены путем создания MurmurHash3.
Кроме того, я был бы признателен, если бы тот, кто делал то же самое с sonicoder, нашел время, чтобы объяснить себя.
Я не понимаю этих значений битового сдвига hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24);
Хеши FNV медленные и посредственного качества - сделайте некоторые измерения и прочитайте настоящие статьи (которые содержат довольно много взбалтывающей ерунды) вместо того, чтобы принимать утверждения автора за чистую монету и помогать им увековечивать миф о том, что хеши FNV быстрые и хорошего качества. Придерживайтесь хэша проверенного исключительного качества, такого как MurmurHash, или выберите один из множества простых хешей, которые превосходят FNV по скорости и качеству. Единственное, что имеет FNV, - это его простота, если производительность не является проблемой (или если стоимость операций искажена, как в интерпретируемых языках).
@DarthGizka, Пример этого "одного из множества простых хешей, которые побеждают FNV по скорости и качеству" ?? Мне действительно интересно.
@Richard: в зависимости от выбранного приложения вы можете попробовать самокоррекцию повернутого аккумулятора (с вращением, которое является относительно простым по отношению к размеру слова) или xorshift, в любом случае с последующим в конце хеширования расширяющимся умножением. CRC имеет отличное качество и может быть очень быстрым, если вы можете использовать встроенные функции. Когда все становится сложнее, я уверен, что обширное и тщательное исследование хешей Боба Дженкинса где-то покрыл именно то, что вам нужно. Модули простых чисел могут быть быстрыми, если вы умножаете на обратное вместо деления.
Для фиксированного набора строк используйте gperf.
Если ваш набор строк изменится, вам нужно выбрать одну хеш-функцию. Эта тема уже обсуждалась ранее:
Какой алгоритм хеширования лучше всего использовать для строки stl при использовании hash_map?
Идеальный хеш - это очень элегантное решение, если оно доступно.
Шепот хеша довольно хорош.
Да, это текущая ведущая хеш-функция общего назначения для хеш-таблиц. Это, конечно, не криптовалюта, с парой очевидных дифференциалов.
Примечание: новый URL для MurmurHash3 - code.google.com/p/smhasher
Другое решение, которое может быть даже лучше в зависимости от вашего варианта использования, - интернированные строки. Вот как работают символы, например в Лиспе.
Интернированная строка - это строковый объект, значение которого является адресом фактических байтов строки. Таким образом, вы создаете интернированный строковый объект, проверяя глобальную таблицу: если строка там есть, вы инициализируете интернированную строку адресом этой строки. Если нет, вы вставляете его, а затем инициализируете свою интернированную строку.
Это означает, что две интернированные строки, построенные из одной и той же строки, будут иметь одно и то же значение, которое является адресом. Итак, если N - количество интернированных строк в вашей системе, характеристики следующие:
Ваше здоровье,
Карл
В этом предыдущий вопрос есть хорошее обсуждение
И хороший обзор того, как выбирать хеш-функции, а также статистику распределения нескольких распространенных здесь
Почему бы вам просто не использовать Библиотеки Boost?? Их функция хеширования проста в использовании, и большая часть вещей в Boost скоро станет частью стандарта C++. Некоторые из них уже есть.
Boost hash так же просто, как
#include <boost/functional/hash.hpp>
int main()
{
boost::hash<std::string> string_hash;
std::size_t h = string_hash("Hash me");
}
Вы можете найти повышение на boost.org
И STL, и boost tr1 имеют чрезвычайно слабую хеш-функцию для строк.
У Боба Дженкинса доступно множество хеш-функций, все они быстрые и имеют низкую частоту столкновений.
Хеши довольно надежные и технически интересные, но не обязательно быстрые. Учтите, что хеш-функция One-at-a-Time обрабатывает входные данные побайтно, тогда как другие хеш-значения занимают 4 или даже 8 байтов за раз. Разница в скорости существенная!
Хэши Боба очень быстрые: azillionmonkeys.com/qed/hash.html
Вы можете увидеть, что .NET использует в методе String.GetHashCode (), используя Reflector.
Рискну предположить, что Microsoft потратила много времени на оптимизацию этого. Они также напечатали во всей документации MSDN, что она может постоянно меняться. Так ясно, что это на их "радаре настройки производительности" ;-)
Я бы подумал, что было бы довольно просто перенести на C++.
Также есть хорошая статья в eternalconfuzzled.com.
Хеш-код Jenkins для строк должен выглядеть примерно так:
#include <stdint.h>
uint32_t hash_string(const char * s)
{
uint32_t hash = 0;
for(; *s; ++s)
{
hash += *s;
hash += (hash << 10);
hash ^= (hash >> 6);
}
hash += (hash << 3);
hash ^= (hash >> 11);
hash += (hash << 15);
return hash;
}
На хорошую тему никогда не поздно, и я уверен, что людям будут интересны мои открытия.
Мне нужна была хеш-функция, и после прочтения этого поста и небольшого исследования приведенных здесь ссылок я придумал этот вариант алгоритма Дэниела Дж. Бернштейна, который я использовал для проведения интересного теста:
unsigned long djb_hashl(const char *clave)
{
unsigned long c,i,h;
for(i=h=0;clave[i];i++)
{
c = toupper(clave[i]);
h = ((h << 5) + h) ^ c;
}
return h;
}
Этот вариант хеширует строки, игнорируя регистр, что соответствует моей потребности в хешировании учетных данных пользователей. «clave» - это «ключ» на испанском языке. Прошу прощения за испанский, но это мой родной язык, и программа написана на нем.
Что ж, я написал программу, которая будет генерировать имена пользователей от test_aaaa до test_zzzz и, чтобы сделать строки длиннее, я добавил к ним случайный домен в этом списке: cloud-nueve.com, yahoo.com ',' gmail.com 'и' hotmail.com '. Поэтому каждый из них будет выглядеть так:
[email protected], [email protected], [email protected], [email protected] and so on.
Вот результат теста: «Столкновение между ХХХ и ХХХ» означает «Столкновение ХХХ и ХХХ». «palabras» означает «слова», а «Total» одинаково на обоих языках.
Buscando Colisiones...
Colision entre '[email protected]' y '[email protected]' (1DB903B7)
Colision entre '[email protected]' y '[email protected]' (2F5BC088)
Colision entre '[email protected]' y '[email protected]' (51FD09CC)
Colision entre '[email protected]' y '[email protected]' (52F5480E)
Colision entre '[email protected]' y '[email protected]' (74FF72E2)
Colision entre '[email protected]' y '[email protected]' (7FD70008)
Colision entre '[email protected]' y '[email protected]' (9BD351C4)
Colision entre '[email protected]' y '[email protected]' (A86953E1)
Colision entre '[email protected]' y '[email protected]' (BA6B0718)
Colision entre '[email protected]' y '[email protected]' (D0523F88)
Colision entre '[email protected]' y '[email protected]' (DEE08108)
Total de Colisiones: 11
Total de Palabras : 456976
Это неплохо, 11 коллизий из 456 976 (конечно, с использованием полных 32 бита в качестве длины таблицы).
При запуске программы с использованием 5 символов, то есть от «test_aaaaa» до «test_zzzzz», на самом деле не хватает памяти для построения таблицы. Ниже представлен результат. «No hay memoria para insertar XXXX (insertadas XXX)» означает «Не осталось памяти для вставки XXX (вставлено XXX)». В основном malloc () потерпел неудачу в этот момент.
No hay memoria para insertar 'test_epjcv' (insertadas 2097701).
Buscando Colisiones...
...451 'colision' strings...
Total de Colisiones: 451
Total de Palabras : 2097701
Это означает всего 451 коллизию на 2 097 701 строке. Обратите внимание, что ни в одном из случаев не было более двух коллизий на код. Я подтверждаю, что это отличный хеш для меня, так как мне нужно преобразовать идентификатор входа в 40-битный уникальный идентификатор для индексации. Поэтому я использую это для преобразования учетных данных для входа в 32-битный хэш и использую дополнительные 8 бит для обработки до 255 коллизий на код, которые, глядя на результаты теста, было бы почти невозможно сгенерировать.
Надеюсь, это кому-то будет полезно.
Обновлено:
Как и в случае с тестовым блоком AIX, я запускаю его с использованием LDR_CNTRL = MAXDATA = 0x20000000, чтобы получить больше памяти и он работал дольше, результаты здесь:
Buscando Colisiones ... Всего коллизий: 2908 Всего в Палабрасе: 5366384
Это 2908 после 5366384 попыток !!
ОЧЕНЬ ВАЖНО: Компиляция программы с -maix64 (таким образом, длина без знака составляет 64 бита), количество коллизий равно 0 для всех случаев !!!
Здесь описан простой способ реализовать это самостоятельно: http://www.devcodenote.com/2015/04/collision-free-string-hashing.html
Отрывок из поста:
если, скажем, у нас есть набор символов заглавных английских букв, то длина набора символов равна 26, где A может быть представлено числом 0, B числом 1, C числом 2 и так далее до Z числом 25. Теперь, когда мы хотим сопоставить строку этого набора символов с уникальным числом, мы выполняем то же преобразование, что и в случае двоичного формата.
Как это (с учетом гипертекстового браузера, который отображает содержимое ссылок) отображается на (32 or 16) bit values, учитывая наборы символов, скажем, от 24 до 1.111.998 кодовых точек? Базовое преобразование не является полезной хеш-функцией.
пожалуйста, добавьте ключевые слова: уникальный алгоритм хеширования с низким уровнем коллизий