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

Я обнаружил, что стандартная функция хеширования в VS2005 мучительно медленная при попытке добиться высокой производительности поиска. Какие есть хорошие примеры быстрых и эффективных алгоритмов хеширования, которые должны аннулировать большинство коллизий?


person PiNoYBoY82    schedule 18.09.2008    source источник
comment
Ниже приведен хороший набор хеш-функций общего назначения, вы должны попробовать их с вашим набором данных, некоторые из них могут превзойти другие в зависимости от коллизий: partow.net/programming/hashfunctions/index.html   -  person    schedule 24.10.2009
comment
Возможный дубликат хорошей хеш-функции для строк   -  person M.J. Rayburn    schedule 02.09.2017


Ответы (11)


Я работал с Полом Ларсоном из Microsoft Research над некоторыми реализациями хеш-таблиц. Он исследовал ряд функций хеширования строк на различных наборах данных и обнаружил, что простой цикл умножения на 101 и сложения работает на удивление хорошо.

unsigned int
hash(
    const char* s,
    unsigned int seed = 0)
{
    unsigned int hash = seed;
    while (*s)
    {
        hash = hash * 101  +  *s++;
    }
    return hash;
}
person George V. Reilly    schedule 20.09.2008
comment
Привет, Джордж. Я попробовал код в хеш-тесте, который написал в своем ответе. Хорошая находка. Он не превосходит по производительности или столкновениям, но всегда дает стабильные результаты. Похоже, это хороший и дешевый кандидат для хеширования строк общего назначения. - person Nils Pipenbrinck; 23.09.2008
comment
Но это работает только для струн небольшой длины. В больших случаях он часто бывает переполнен. - person Soumajyoti; 03.01.2013
comment
Сумаджйоти, переполнение не имеет значения. Большинство хеш-функций переполняются. Дело в том, что вы получаете приличное сочетание битов в младших 32-х битах. - person George V. Reilly; 05.01.2013
comment
Это похоже на реализацию Java, но в ней используется 31 вместо 101. - person Jorge Galvão; 12.03.2014

Из моего старого кода:

/* magic numbers from http://www.isthe.com/chongo/tech/comp/fnv/ */
static const size_t InitialFNV = 2166136261U;
static const size_t FNVMultiple = 16777619;

/* Fowler / Noll / Vo (FNV) Hash */
size_t myhash(const string &s)
{
    size_t hash = InitialFNV;
    for(size_t i = 0; i < s.length(); i++)
    {
        hash = hash ^ (s[i]);       /* xor  the low 8 bits */
        hash = hash * FNVMultiple;  /* multiply by the magic number */
    }
    return hash;
}

Это быстро. Действительно чертовски быстро.

person Dark Shikari    schedule 19.09.2008
comment
он может быть быстрым, но это, вероятно, одна из НАИБОЛЬШИХ хэш-функций, когда-либо изобретенных. - person ; 02.03.2010
comment
@Matthieu: Почему? Много дубликатов? У вас есть ссылки, где я могу прочитать об этом больше? - person Albert; 17.10.2012
comment
@Albert: ^ транзитивен, и это плохо. FNVMultiple не является простым, что плохо. InitialFNV тоже не простое, что может быть, а может и не плохо, я не уверен. - person Mooing Duck; 08.06.2015
comment
@MooingDuck FNVMultiple кажется простым числом. - person bysreg; 31.07.2017
comment
16777619 - (доказанное) простое число. 2166136261 - (проверенный) композит (проваленная тестовая база 2 sprp). primes.utm.edu/curios/includes/primetest.php - person Nick; 21.02.2018

Boost имеет библиотеку boost :: hash, которая предоставляет некоторые базовые хеш-функции для наиболее распространенных типов.

person David Pierre    schedule 19.09.2008

Это всегда зависит от вашего набора данных.

Я, например, получил удивительно хорошие результаты, используя CRC32 строки. Очень хорошо работает с широким спектром различных входных наборов.

В сети легко найти множество хороших реализаций CRC32.

Изменить: Чуть не забыл: на этой странице есть хорошая хэш-функция с числами производительности и тестовыми данными:

http://smallcode.weblogs.us/ ‹- дальше по странице.

person Nils Pipenbrinck    schedule 18.09.2008

Я использовал хеш Jenkins для написания библиотеки фильтров Блума, у нее отличная производительность.

Подробности и код доступны здесь: http://burtleburtle.net/bob/c/lookup3.c < / а>

Это то, что Perl использует для своей операции хеширования, fwiw.

person SquareCog    schedule 19.09.2008
comment
Также обратите внимание на жуткий хеш, который является улучшением по сравнению с Jenkins. - person Soren; 15.10.2015

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

Если вы не хешируете фиксированный набор слов, то, очевидно, это не применимо.

person bk1e    schedule 19.09.2008

Одно из классических предложений для строкового хеша - пошагово перебирать буквы одну за другой, добавляя их значения ascii / unicode в аккумулятор, каждый раз умножая аккумулятор на простое число. (допускает переполнение хеш-значения)

  template <> struct myhash{};

  template <> struct myhash<string>
    {
    size_t operator()(string &to_hash) const
      {
      const char * in = to_hash.c_str();
      size_t out=0;
      while(NULL != *in)
        {
        out*= 53; //just a prime number
        out+= *in;
        ++in;
        }
      return out;
      }
    };

  hash_map<string, int, myhash<string> > my_hash_map;

Трудно работать быстрее, не выбрасывая данные. Если вы знаете, что ваши строки можно различить только по нескольким символам, а не по всему их содержимому, вы можете сделать это быстрее.

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

person Brian    schedule 19.09.2008
comment
Предупреждение о состоянии Йоды! Кроме того, это похоже на алгоритм Ларсона (я заметил, что это было опубликовано ранее!). - person Helge Klein; 17.08.2014

Я немного поискал, и забавно, здесь обнаружился маленький алгоритм Пола Ларсона http://www.strchr.com/hash_functions, поскольку имеет наименьшее количество столкновений из всех протестированных в ряде условий, и очень быстро для тех, которые развернуты или управляются таблицей.

Ларсон - это простое умножение на 101 и добавление в цикле выше.

person Josh S    schedule 20.02.2012

Python 3.4 включает новый алгоритм хеширования, основанный на SipHash. PEP 456 очень информативен.

person George V. Reilly    schedule 19.03.2014
comment
Я провожу несколько тестов, и SipHash выглядит очень хорошо - person David Soroko; 24.04.2015

От хеш-функций до конца:

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

Это хороший выбор, но давайте посмотрим позже, сможем ли мы в целом добиться большего успеха. Еще один прекрасный выбор, особенно если вы знаете о своих данных больше, чем «это будет неизвестное количество байтов», - это использовать свои собственные (например, см. Ответы Вон Чуна или модифицированный xxHash / Murmur Rune, который специализируется на 4-байтовых ключах). и т.д.). Если вы знаете свои данные, всегда старайтесь увидеть, можно ли использовать эти знания для хорошего эффекта!

Без дополнительной информации я бы рекомендовал MurmurHash в качестве общего назначения некриптографическая хеш-функция. Для небольших строк (размером со средний идентификатор в программах) очень простой и известный заголовок djb2 и FNV очень хороши.

Здесь (размер данных ‹10 байт) мы видим, что интеллектуальность ILP других алгоритмов не проявляется, а суперпростота FNV или djb2 выигрывает в производительности.

djb2

unsigned long
hash(unsigned char *str)
{
    unsigned long hash = 5381;
    int c;

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

    return hash;
}

FNV-1

hash = FNV_offset_basis
for each byte_of_data to be hashed
     hash = hash × FNV_prime
     hash = hash XOR byte_of_data
return hash

FNV-1A

hash = FNV_offset_basis
for each byte_of_data to be hashed
     hash = hash XOR byte_of_data
     hash = hash × FNV_prime
return hash

Примечание о безопасности и доступности

Хеш-функции могут сделать ваш код уязвимым для атак типа «отказ в обслуживании». Если злоумышленник может заставить ваш сервер обрабатывать слишком много коллизий, ваш сервер может не справиться с запросами.

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

person philix    schedule 24.12.2016
comment
@FelixSFD Я только что улучшил ответ. - person philix; 24.12.2016

Если ваши строки в среднем длиннее одной строки кэша, но их длина + префикс достаточно уникальны, рассмотрите возможность использования только длины + первых 8/16 символов. (Длина содержится в самом объекте std :: string, поэтому ее легко читать)

person MSalters    schedule 19.09.2008