Существуют ли какие-либо рабочие реализации катящейся хэш-функции, используемой в строчном алгоритме поиска Рабина-Карпа?

Я хочу использовать функцию хеш-ролика, чтобы я мог принимать хэши n-граммов очень большой строки.

Например:

"stackoverflow", разбитый на 5 граммов, будет:

"stack", "tacko", "ackov", "ckove", "" kover "," overf "," verfl "," erflo "," rflow"

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

Я знаю, что в общем случае эта хэш-функция генерируется как:

H = c 1 a k - 1 + c 2 a k - 2 + c 3 a k - 3 +... + c k a 0 где a - константа и c1,...., ck - входные символы.

Если вы следуете этой ссылке в строчном алгоритме поиска Rabin-Karp, в нем говорится, что "a" обычно является большим большим числом.

Я хочу, чтобы мои хэши были сохранены в 32-битных целых числах, поэтому насколько велика простое число "a", так что я не переполняю свое целое число?

Существует ли существующая реализация этой хэш-функции где-то, что я уже мог использовать?


Вот реализация, которую я создал:

public class hash2
{

    public int prime = 101;

    public int hash(String text)
    {
        int hash = 0;

        for(int i = 0; i < text.length(); i++)
        {
            char c = text.charAt(i);
            hash += c * (int) (Math.pow(prime, text.length() - 1 - i));
        }

        return hash;
    }

    public int rollHash(int previousHash, String previousText, String currentText)
    {

        char firstChar = previousText.charAt(0);
        char lastChar = currentText.charAt(currentText.length() - 1);

        int firstCharHash = firstChar * (int) (Math.pow(prime, previousText.length() - 1));
        int hash = (previousHash - firstCharHash) * prime + lastChar;

        return hash;
    }

    public static void main(String[] args)
    {
        hash2 hashify = new hash2();

        int firstHash = hashify.hash("mydog");
        System.out.println(firstHash);
        System.out.println(hashify.hash("ydogr"));
        System.out.println(hashify.rollHash(firstHash, "mydog", "ydogr"));
    }

}

Я использую 101 как мое основное. Это имеет значение, если мои хеши будут переполняться? Я думаю, что это желательно, но я не уверен.

Это похоже на правильный путь?

Ответ 1

Я помню немного другую реализацию, которая, по-видимому, взята из одной из книг алгоритмов sedgewick (она также содержит пример кода - попытайтесь ее найти). здесь сводка, скорректированная на 32 битные целые числа:

вы используете модульную арифметику для предотвращения переполнения целого числа после каждой операции.

изначально установлен:

  • c = text ( "stackoverflow" )
  • M = длина "n-граммов"
  • d = размер вашего алфавита (256)
  • q = большое простое число, так что (d + 1) * q не переполняется (8355967 может быть хорошим выбором)
  • dM = d M-1 mod q

сначала вычислить хэш-значение первого n-грамма:

h = 0
for i from 1 to M:
  h = (h*d + c[i]) mod q

и для каждого следующего n-грамма:

for i from 1 to lenght(c)-M:
  // first subtract the oldest character
  h = (h + d*q - c[i]*dM) mod q

  // then add the next character
  h = (h*d + c[i+M]) mod q

причина, по которой вам нужно добавить d * q перед вычитанием самого старого символа, потому что вы можете столкнуться с отрицательными значениями из-за небольших значений, вызванных предыдущей операцией modulo.

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

Ответ 2

Как я понимаю, это минимизация функции для:

2^31 - sum (maxchar) * A^kx

где maxchar = 62 (для A-Za-z0-9). Я просто вычислил его в Excel (OO Calc, точно):) и max A, который он нашел, это 76 или 73, для простого числа.

Ответ 3

Не уверен, что ваша цель здесь, но если вы пытаетесь повысить производительность, использование math.pow будет стоить вам гораздо больше, чем вы можете сэкономить, вычислив значение хеш-роли.

Я предлагаю вам начать с простого и эффективного, и вы, скорее всего, найдете его достаточно быстро.