Python: хеширование алгоритма Рабина-карпа



Я реализую алгоритм Рабина-карпа для развлечения. Я наткнулся на этот псевдокод:



    RABIN -KARP -MATCHER (T, P, d, q)
1 n = T.length
2 m = P.length
3 h = d^(m-1) mod q
4 p=0
5 t= 0
6 for i = 1 to m
/ preprocessing
/
7 p = (dp + P [i]) mod q
8 t = (dt + T [i]) mod q
9 for s = 0 to n-m
/ matching
/
10 if p == t
11 if P [1... m] == T [s + 1...s + m]
12 print “Pattern occurs with shift” s
13 if s < n-m
14 t = (d(t-T[s + 1]h) + T [s + m + 1]) mod q


Я реализовал в Python 2.7 так:



def Rabin_Karp_Matcher(text, pattern, d, q):
n = len(text)
m = len(pattern)
h = pow(d,m-1)%q
p = 0
t =0
result = []
for i in range(m): # preprocessing
p = (d*p+ord(pattern[i]))%q
t = (d*t+ord(text[i]))%q
for s in range(n-m):
if p == t: # check character by character
match = True
for i in range(m):
if pattern[i] != text[s+i]:
match = False
break
if match:
result = result + [s]
if s < n-m:
t = (d*(t-ord(text[s+1])*h)+ord(text[s+m]))%q #index out of bounds here
return result


Где результатом является список, содержащий индекс в тексте шаблона.



Мой код не может найти 26 В 3141592653589793
и я подозреваю, что это как-то связано с моим хэшкодом, определенным строкой 14 псевдокода. Может кто-нибудь, пожалуйста, помочь с этим.



Я перешел в следующие парамеры:



P = " 26"
T = "3141592653589793"
d = 257
q = 11



P и T должны быть строками / массивами символов

910   2  

2 ответов:

Вот рабочая версия вашего кода:

def Rabin_Karp_Matcher(text, pattern, d, q):
    n = len(text)
    m = len(pattern)
    h = pow(d,m-1)%q
    p = 0
    t = 0
    result = []
    for i in range(m): # preprocessing
        p = (d*p+ord(pattern[i]))%q
        t = (d*t+ord(text[i]))%q
    for s in range(n-m+1): # note the +1
        if p == t: # check character by character
            match = True
            for i in range(m):
                if pattern[i] != text[s+i]:
                    match = False
                    break
            if match:
                result = result + [s]
        if s < n-m:
            t = (t-h*ord(text[s]))%q # remove letter s
            t = (t*d+ord(text[s+m]))%q # add letter s+m
            t = (t+q)%q # make sure that t >= 0
    return result
print (Rabin_Karp_Matcher ("3141592653589793", "26", 257, 11))
print (Rabin_Karp_Matcher ("xxxxx", "xx", 40999999, 999999937))

Вывод:

[6]
[0, 1, 2, 3]

На первом шаге вы проверяете, является ли text[0..m] == pattern. На втором шаге вы хотите проверить, является ли text[1..m+1] == pattern. Таким образом, вы удаляете text[0] из хэша (в данный момент он умножается на ваш предварительно вычисленный h): t = (t-h*ord(text[s]))%q. А затем добавьте к нему text[m]: t = (t*d+ord(text[s+m]))%q. На следующем шаге вы удаляете text[1] и добавляете text[m+1], и так далее. Линия t = (t+q)%q существует потому, что отрицательное число по модулю q дает остаток в диапазоне (-q; 0], и мы хотим, чтобы он был в диапазоне [0; q).

Обратите внимание, что вы хотите проверить всего n-m+1 подстрок, а не n-m, следовательно, правильный цикл - for s in range(n-m+1). Проверено по второму примеру (нахождение " xx " в "xxxxx").

Также стоит отметить:

  1. Линия h = pow(d,m-1)%q может быть слишком медленной, если m велика. Результат лучше брать по модулю q после каждого из умножений m-2.

  2. Этот алгоритм все еще O (nm) в худшем случае. С помощью text="a"*100000 и pattern="a"*50000 он найдет 50001 позицию, в которой подстрока текста соответствует шаблону, и проверит их все по символам. Если вы ожидаете, что ваш код будет работать быстро в таких экстремальных случаях, вы должны пропустить сравнение символов и найти способ справиться с ложными срабатываниями (т. е. хэши равны, а строки-нет). Выбор большого простого числа q может помочь снизить вероятность ложного срабатывания до приемлемого уровня.

Итак, ответ заключается в том, что вам нужно сделать отступ в цикле "for s":

def Rabin_Karp_Matcher(text, pattern, d, q):
    n = len(text)
    m = len(pattern)
    h = pow(d,m-1)%q
    p = 0
    t =0
    result = []
    for i in range(m): # preprocessing
        p = (d*p+ord(pattern[i]))%q
        t = (d*t+ord(text[i]))%q

        for s in range(n-m):
            if p == t: # check character by character
                match = True
                for i in range(m):
                    if pattern[i] != text[s+i]:
                        match = False
                        break
                if match:
                    result = result + [s]
            if s < n-m:
                    t = (d*(t-ord(text[s+1])*h)+ord(text[s+m]))%q #index out of bounds here


    return result
Это дает мне ответ 6, который является тем, что вы ищете, я полагаю. Интересный алгоритм человек.

Comments

    Ничего не найдено.