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

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

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

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

slashmais 24.09.2008 14:31

На следующей странице представлено несколько реализаций хэш-функций общего назначения, которые являются «производительными» и имеют низкие «уровни конфликтов»: partow.net/programming/hashfunctions/index.html

Matthieu N. 01.11.2010 02:08
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
68
2
88 516
14
Перейти к ответу Данный вопрос помечен как решенный

Ответы 14

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

Ура идеальным генераторам хешей!

Chris Jester-Young 22.09.2008 14:08

Идеальное хеширование НЕ подходит для этого приложения, поскольку набор имен неизвестен и меняется. Следовательно, gperf для этого не подойдет.

TimB 24.09.2008 08:43

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

CRC предназначены для обнаружения и исправления ошибок. Их характеристики распределения обычно не очень хорошие.

Nick Johnson 22.09.2008 14:09

Очевидно, что Arachnid никогда не пробовал использовать CRC32 в качестве хэшей. Они хорошо работают.

Nils Pipenbrinck 22.09.2008 14:11

«CRC32 никогда не предназначался для использования хеш-таблиц. На самом деле нет веских причин использовать его для этой цели». ср. home.comcast.net/~bretm/hash/8.html

obecalp 17.02.2009 00:24
Ответ принят как подходящий

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

Если вы собираетесь использовать FNV, придерживайтесь FNV-1a, поскольку он дает приемлемые результаты на лавинном тесте (см. home.comcast.net/~bretm/hash/6.html). Или просто используйте MurmurHash2, который лучше и по скорости, и по распределению (murmurhash.googlepages.com).

Steven Sudit 10.07.2009 07:38

@Steven: Хэш MurmurHash был проанализирован только его автором. Я использовал его в нескольких разных сценариях, и более новая версия FNV, похоже, работает лучше.

Matthieu N. 26.01.2011 01:03

@sonicoder: Хотя я не собираюсь перепродавать MurmurHash, простой FNV просто ужасен, а FNV-1a только проходим. Как оказалось, MurmurHash был тщательно проанализирован и признан полезным. Это все еще не криптографический хеш, и коллизии будут, несмотря ни на что, но это все равно огромное улучшение по сравнению с любым типом FNV.

Steven Sudit 26.01.2011 23:50

@Steven Sudit: Как я уже сказал, он был проанализирован "только" его автором и никем другим. следовательно, результаты «анализа» не совсем объективны.

Matthieu N. 30.01.2011 15:07

@sonicoder: Я буду откровеннее: нет, вы ошибаетесь. Он был проанализирован рядом сторонних организаций, в том числе академических. Посетите Википедию для получения ссылок. Что еще более важно, это не только хорошо в целом, но и конкретные недостатки, которые были обнаружены, были устранены путем создания MurmurHash3.

Steven Sudit 03.02.2011 10:25

Кроме того, я был бы признателен, если бы тот, кто делал то же самое с sonicoder, нашел время, чтобы объяснить себя.

Steven Sudit 03.02.2011 10:29

Я не понимаю этих значений битового сдвига hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24);

jokoon 06.03.2013 14:26

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

DarthGizka 17.09.2016 14:03

@DarthGizka, Пример этого "одного из множества простых хешей, которые побеждают FNV по скорости и качеству" ?? Мне действительно интересно.

Richard McFriend Oluwamuyiwa 15.02.2021 22:54

@Richard: в зависимости от выбранного приложения вы можете попробовать самокоррекцию повернутого аккумулятора (с вращением, которое является относительно простым по отношению к размеру слова) или xorshift, в любом случае с последующим в конце хеширования расширяющимся умножением. CRC имеет отличное качество и может быть очень быстрым, если вы можете использовать встроенные функции. Когда все становится сложнее, я уверен, что обширное и тщательное исследование хешей Боба Дженкинса где-то покрыл именно то, что вам нужно. Модули простых чисел могут быть быстрыми, если вы умножаете на обратное вместо деления.

DarthGizka 15.02.2021 23:42

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

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

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

Идеальный хеш - это очень элегантное решение, если оно доступно.

Steven Sudit 27.01.2011 21:23

Шепот хеша довольно хорош.

Да, это текущая ведущая хеш-функция общего назначения для хеш-таблиц. Это, конечно, не криптовалюта, с парой очевидных дифференциалов.

obecalp 16.02.2009 22:21

Примечание: новый URL для MurmurHash3 - code.google.com/p/smhasher

Emil Styrke 27.11.2013 18:03

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

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

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

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

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

Карл

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

В этом предыдущий вопрос есть хорошее обсуждение

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

Почему бы вам просто не использовать Библиотеки 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 имеют чрезвычайно слабую хеш-функцию для строк.

obecalp 16.02.2009 22:19

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

Хеши довольно надежные и технически интересные, но не обязательно быстрые. Учтите, что хеш-функция One-at-a-Time обрабатывает входные данные побайтно, тогда как другие хеш-значения занимают 4 или даже 8 байтов за раз. Разница в скорости существенная!

Steven Sudit 23.07.2009 19:23

Хэши Боба очень быстрые: azillionmonkeys.com/qed/hash.html

user7116 24.07.2009 00:20

Вы можете увидеть, что .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 кодовых точек? Базовое преобразование не является полезной хеш-функцией.

greybeard 17.04.2015 07:01

Другие вопросы по теме