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

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

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

Ответ 1

Один из вариантов

Ответ 2

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

Ответ 4

Там также хорошая статья в eternallyconfuzzled.com.

Дженкинс "Один на время" хеш для строк должен выглядеть примерно так:

#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;
}

Ответ 5

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

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

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

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

Приветствия,

Карл

Ответ 6

Почему бы вам просто не использовать Boost libraries? Их функция хэширования проста в использовании, и большая часть материала в Boost скоро будет часть стандарта С++. Некоторые из них уже есть.

Бит хэша так же просто, как

#include <boost/functional/hash.hpp>

int main()
{
    boost::hash<std::string> string_hash;

    std::size_t h = string_hash("Hash me");
}

Вы можете найти boost на boost.org

Ответ 7

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

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

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.

Вот результат теста -Colision entre XXX y XXX 'означает "Столкновение XXX и XXX". "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 977 701 строк. Обратите внимание, что ни в одном из случаев не было более двух столкновений на один код. Я подтверждаю, что это отличный хеш для меня, поскольку мне нужно преобразовать идентификатор входа в 40-битный уникальный идентификатор для индексирования. Поэтому я использую это, чтобы преобразовать учетные данные для входа в 32-битный хеш и использовать дополнительные 8 бит для обработки до 255 коллизий на код, которые выглядят практически без результатов. Результаты поиска будут практически невозможны.

Надеюсь, что это кому-то полезно.

EDIT:

Как и в тестовом поле AIX, я запускаю его, используя LDR_CNTRL = MAXDATA = 0x20000000, чтобы дать ему больше памяти, и он работает дольше, результаты здесь:

Коллизии... Всего полков: 2908 Всего де Палабр: 5366384

Это 2908 после 5 366 384 попытки!!

ОЧЕНЬ ВАЖНО: компиляция программы с помощью -maix64 (поэтому unsigned long - 64 бита), количество столкновений равно 0 для всех случаев.

Ответ 8

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

Ответ 9

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

Ответ 12

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

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

Было бы довольно тривиально переносить на С++ тоже я бы подумал.

Ответ 13

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

Отрывок из сообщения:

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

Ответ 14

CRC-32. Для этого существует около триллиона ссылок на Google.