Алгоритм быстрого хеширования строк с низкой частотой конфликтов с 32-битным целым числом

У меня есть много несвязанных именованных вещей, по которым я хотел бы быстро поискать. «Муравьед» всегда везде «трубкозуб», поэтому хеширование строки и повторное использование целого числа будет хорошо работать для ускорения сравнений. Полный набор имен неизвестен (и со временем меняется). Что такое алгоритм быстрого хеширования строк, который будет генерировать небольшие (32 или 16) битовые значения и иметь низкую частоту конфликтов?

Я бы хотел увидеть оптимизированную реализацию, специфичную для C / C ++.


person Jason Citron    schedule 22.09.2008    source источник
comment
пожалуйста, добавьте ключевые слова: уникальный алгоритм хеширования с низким уровнем коллизий   -  person slashmais    schedule 24.09.2008
comment
На следующей странице представлено несколько реализаций хэш-функций общего назначения, которые являются производительными и имеют низкую частоту конфликтов: partow. net / programming / hashfunctions / index.html.   -  person    schedule 01.11.2010


Ответы (14)


Один из вариантов FNV должен соответствовать вашим требованиям. Они быстрые и производят довольно равномерно распределяемые результаты.

person Nick Johnson    schedule 22.09.2008
comment
Если вы собираетесь использовать FNV, придерживайтесь FNV-1a, поскольку он дает приемлемые результаты на лавинном тесте (см. home.comcast.net/~bretm/hash/6.html). Или просто используйте MurmurHash2, который лучше как по скорости, так и по распространению (murmurhash.googlepages.com). - person Steven Sudit; 10.07.2009
comment
@Steven: Хэш MurmurHash был проанализирован только его автором. Я использовал его в нескольких разных сценариях, и более новая версия FNV, похоже, работает лучше. - person ; 26.01.2011
comment
@sonicoder: Хотя я не собираюсь перепродавать MurmurHash, простой FNV просто ужасен, а FNV-1a только проходим. Как оказалось, MurmurHash был тщательно проанализирован и признан полезным. Это все еще не криптографический хеш, и коллизии будут, несмотря ни на что, но это все равно огромное улучшение по сравнению с любым типом FNV. - person Steven Sudit; 26.01.2011
comment
@Steven Sudit: Как я уже сказал, его анализировал только его автор и никто другой. следовательно, результаты анализа не совсем объективны. - person ; 30.01.2011
comment
@sonicoder: Я буду откровеннее: нет, вы ошибаетесь. Он был проанализирован рядом сторонних организаций, в том числе академических. Посетите Википедию для получения ссылок. Что еще более важно, это не только хорошо в целом, но и конкретные недостатки, которые были обнаружены, были устранены путем создания MurmurHash3. - person Steven Sudit; 03.02.2011
comment
Кроме того, я был бы признателен, если бы тот, кто делал то же самое с sonicoder, нашел время, чтобы объяснить себя. - person Steven Sudit; 03.02.2011
comment
Я не понимаю этих значений битового сдвига hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24); - person jokoon; 06.03.2013
comment
Хеши FNV медленные и посредственного качества - сделайте некоторые измерения и прочтите настоящие статьи (которые содержат довольно много взбалтывающей чепухи) вместо того, чтобы принимать утверждения автора за чистую монету и помогать им увековечивать миф о том, что хеши FNV быстрые и хорошего качества. Придерживайтесь хэша проверенного исключительного качества, такого как MurmurHash, или выберите один из множества простых хешей, которые превосходят FNV по скорости и качеству. Единственное, что имеет FNV, - это его простота, если производительность не является проблемой (или если стоимость операций искажена, как в интерпретируемых языках). - person DarthGizka; 17.09.2016
comment
@DarthGizka, Пример этого одного из многих простых хешей, которые побеждают FNV по скорости и качеству ?? Мне действительно интересно. - person Richard McFriend Oluwamuyiwa; 15.02.2021
comment
@Richard: в зависимости от выбранного приложения вы можете попробовать самокоррекцию повернутого аккумулятора (с вращением, которое является относительно простым по отношению к размеру слова) или xorshift, в любом случае с последующим в конце хеширования расширяющимся умножением. CRC имеет отличное качество и может быть очень быстрым, если вы можете использовать встроенные функции. Когда все становится еще сложнее, я уверен, что Боб Дженкинс провел обширное и тщательное исследование хэшей покрыл именно то, что вам нужно, где-то. Модули простых чисел могут быть быстрыми, если вы умножаете на обратное вместо деления. - person DarthGizka; 15.02.2021

Murmur Hash довольно приятный.

person yrp    schedule 22.09.2008
comment
Да, это текущая ведущая хеш-функция общего назначения для хеш-таблиц. Это, конечно, не криптовалюта, с парой очевидных дифференциалов. - person obecalp; 16.02.2009
comment
Примечание. Новый URL для MurmurHash3 - code.google.com/p/smhasher. - person Emil Styrke; 27.11.2013

Для фиксированного набора строк используйте gperf.

Если ваш набор строк изменится, вам нужно выбрать одну хеш-функцию. Эта тема уже обсуждалась ранее:

Какой алгоритм хеширования лучше всего использовать для строки stl при использовании hash_map?

person Nils Pipenbrinck    schedule 22.09.2008
comment
Идеальный хеш - это очень элегантное решение, если оно доступно. - person Steven Sudit; 27.01.2011

Также есть хорошая статья по адресу 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;
}
person Christoph    schedule 16.12.2008

Другое решение, которое может быть даже лучше в зависимости от вашего варианта использования, - это интернированные строки . Вот как работают символы, например в Лиспе.

Интернированная строка - это строковый объект, значением которого является адрес фактических байтов строки. Таким образом, вы создаете объект интернированной строки, проверяя глобальную таблицу: если строка там есть, вы инициализируете интернированную строку адресом этой строки. Если нет, вы вставляете его, а затем инициализируете свою интернированную строку.

Это означает, что две интернированные строки, построенные из одной и той же строки, будут иметь одно и то же значение, которое является адресом. Итак, если N - количество интернированных строк в вашей системе, характеристики следующие:

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

Ваше здоровье,

Карл

person Carl Seleborg    schedule 22.09.2008

Почему бы вам просто не использовать библиотеки 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

person Bernard Igiri    schedule 16.12.2008
comment
И STL, и boost tr1 имеют чрезвычайно слабую хеш-функцию для строк. - person obecalp; 16.02.2009

На хорошую тему никогда не поздно, и я уверен, что людям будут интересны мои открытия.

I needed a hash function and after reading this post and doing a bit of research on the links given here, I came up with this variation of Daniel J Bernstein's algorithm, which I used to do an interesting test:

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 для всех случаев !!!

person Antonio Morales    schedule 26.09.2013

Хеш-функция Hsieh довольно хороша и имеет несколько тестов / сравнений в качестве общего хэш-функция в C. В зависимости от того, что вы хотите (это не совсем очевидно), вы можете рассмотреть что-то вроде cdb.

person James Antill    schedule 24.09.2008

У Боба Дженкинса доступно множество хеш-функций, все из которых работают быстро и с низким уровнем коллизий. .

person user7116    schedule 16.12.2008
comment
Хеши довольно надежные и технически интересные, но не обязательно быстрые. Учтите, что хеш-функция One-at-a-Time обрабатывает входные данные побайтно, тогда как другие хеш-значения занимают 4 или даже 8 байтов за раз. Разница в скорости существенная! - person Steven Sudit; 23.07.2009
comment
Хэши Боба очень быстрые: azillionmonkeys.com/qed/hash.html - person user7116; 24.07.2009

Взгляните на GNU gperf.

person Rob Wells    schedule 22.09.2008
comment
Ура идеальным генераторам хешей! - person Chris Jester-Young; 22.09.2008
comment
Идеальное хеширование НЕ подходит для этого приложения, так как набор имен неизвестен и меняется. Следовательно, gperf для этого не подойдет. - person TimB; 24.09.2008

Вы можете увидеть, что .NET использует в методе String.GetHashCode (), используя Reflector.

Рискну предположить, что Microsoft потратила много времени на оптимизацию этого. Они также напечатали во всей документации MSDN, что она может постоянно меняться. Так ясно, что это на их «радаре настройки производительности» ;-)

Я бы подумал, что было бы довольно просто перенести на C ++.

person nbevans    schedule 16.12.2008


Здесь описан простой способ его самостоятельной реализации: http://www.devcodenote.com/2015/04/collision-free-string-hashing.html

Отрывок из поста:

если, скажем, у нас есть набор символов заглавных английских букв, то длина набора символов равна 26, где A может быть представлено числом 0, B числом 1, C числом 2 и так далее до Z числом 25. Теперь, когда мы хотим сопоставить строку этого набора символов с уникальным числом, мы выполняем то же преобразование, что и в случае двоичного формата.

person Abhishek Jain    schedule 17.04.2015
comment
Как это (с учетом гипертекстового браузера, который отображает содержимое ссылок) отображается на (32 or 16) bit values с заданными наборами символов, скажем, от 24 до кодовых точек 1.111.998? Базовое преобразование не является полезной хеш-функцией. - person greybeard; 17.04.2015

CRC-32. На это есть около триллиона ссылок в Google.

person 1800 INFORMATION    schedule 22.09.2008
comment
CRC предназначены для обнаружения и исправления ошибок. Их характеристики распределения обычно не очень хорошие. - person Nick Johnson; 22.09.2008
comment
Очевидно, что Arachnid никогда не пробовал использовать CRC32 в качестве хэшей. Они хорошо работают. - person Nils Pipenbrinck; 22.09.2008
comment
CRC32 никогда не предназначался для использования хеш-таблицы. На самом деле нет веских причин использовать его для этой цели. ср. home.comcast.net/~bretm/hash/8.html - person obecalp; 17.02.2009