From: Martin Mares Date: Sun, 15 Jan 2012 22:07:57 +0000 (+0100) Subject: KMP: Rabin-Karp X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=c1cd5e44ed2c260117e89eb95de8e92d8dbd81a2;p=ads2.git KMP: Rabin-Karp --- diff --git a/1-kmp/1-kmp.tex b/1-kmp/1-kmp.tex index 2fd259b..a0d36fc 100644 --- a/1-kmp/1-kmp.tex +++ b/1-kmp/1-kmp.tex @@ -371,9 +371,9 @@ paraleln jeden krok ka¾dého z~tìchto hledání, co¾ nám dá zpìtné hrany v~dal¹ím patøe stromu. Kdykoliv vytvoøíme zpìtnou hranu, sestrojíme také zkratkovou hranu z~tého¾ -vrcholu. Pokud vede zpìtná hrana z~$S$ do~$Q$ a $\(Q) \ne \emptyset$, -musí vést zkratka z~$S$ také do~$Q$. Pokud v~$Q$ ¾ádné slovo neskonèí, musí -zkratka z~$S$ vést do tého¾ vrcholu, jako zkratka z~$Q$. +vrcholu. Pokud vede zpìtná hrana z~$K$ do~$Q$ a $\(Q) \ne \emptyset$, +musí vést zkratka z~$K$ také do~$Q$. Pokud v~$Q$ ¾ádné slovo neskonèí, musí +zkratka z~$K$ vést do tého¾ vrcholu, jako zkratka z~$Q$. \s{Algoritmus: Konstrukce automatu} \algo @@ -381,15 +381,15 @@ zkratka z~$S$ v \:Vlo¾íme do~stromu slova $\iota_1 \dots \iota_n$, nastavíme $\$ ve~v¹ech stavech. \:$\(R) \= \emptyset$, $\(R) \= \emptyset$. \:Zalo¾íme frontu $F$ a~vlo¾íme do~ní syny koøene. -\:Pro v¹echny syny~$S$ koøene: $\(S) \= R$, $\(S) \= \emptyset$. +\:Pro v¹echny syny~$K$ koøene: $\(K) \= R$, $\(K) \= \emptyset$. \:Dokud $F \neq \emptyset$: \::Vybereme $I$ z~fronty $F$. -\::Pro v¹echny syny $S$ vrcholu $I$: -\:::$Q \= \(\(I), \hbox{písmeno na~hranì $IS$})$. -\:::$\(S) \= Q$. -\:::Pokud $\(Q) \neq \emptyset$: $\(S) \= Q$. -\:::Jinak $\(S) \= \(Q)$. -\:::Vlo¾íme $S$ do~fronty $F$. +\::Pro v¹echny syny $K$ vrcholu $I$: +\:::$Q \= \(\(I), \hbox{písmeno na~hranì $IK$})$. +\:::$\(K) \= Q$. +\:::Pokud $\(Q) \neq \emptyset$: $\(K) \= Q$. +\:::Jinak $\(K) \= \(Q)$. +\:::Vlo¾íme $K$ do~fronty $F$. \endalgo Jeliko¾ tento algoritmus pouze napøeskáèku hledá v¹echny jehly, musí být jeho @@ -407,27 +407,64 @@ jednotliv \h{Rabinùv-Karpùv algoritmus} -Nyní si uká¾eme je¹tì jeden algoritmus na~hledání jedné jehly, který nebude mít v~nejhor¹ím pøípadì lineární slo¾itost, ale bude ji mít prùmìrnì. Bude daleko jednodu¹¹í a~uká¾e se, ¾e je v~praxi daleko rychlej¹í. Bude to algoritmus zalo¾ený na~hashování. +Nyní si uká¾eme je¹tì jeden algoritmus na~hledání jedné jehly, tentokrát +zalo¾ený na he¹ování. Aèkoliv jeho èasová slo¾itost v~nejhor¹ím pøípadì +bude srovnatelná s~hledáním hrubou silou, v~prùmìru bude lineární a v~praxi +èasto pobì¾í rychleji ne¾ KMP. + +Pøedstavme si, ¾e máme seno délky $S$ a~jehlu délky $J$. Poøídíme si nìjakou +he¹ovací funkci~$H$, které $J$-ticím znakù pøiøazuje èísla z~mno¾iny $\{0,\ldots,N-1\}$ +pro nìjaké dost velké~$N$. Budeme posouvat okénko délky~$J$ po~senì, pro ka¾dou +jeho polohu si spoèteme he¹ znakù uvnitø okénka, porovnáme s~he¹em jehly a pokud +se rovnají, porovnáme je znak po~znaku. + +Pokud je he¹ovací funkce \uv{kvalitní}, málokdy se stane, ¾e by se he¹e rovnaly, +tak¾e místo èasu $\Theta(J)$ na porovnáváním øetìzcù si vystaèíme s~porovnáním +he¹ù v~konstantním èase. Jen¾e ouha, èas $\Theta(J)$ potøebujeme i na vypoètení +he¹e pro ka¾dou polohu okénka, tak¾e jsme si nepomohli. + +Poøídíme si proto he¹ovací funkci, kterou lze pøi posunutí okénka o~jednu +pozici doprava v~konstantním èase pøepoèítat. Tyto po¾adavky splòuje tøeba +polynom +$$ +H(x_1,\ldots,x_J) = (x_1P^{J-1} + x_2P^{J-2} + \ldots + x_{J-1}P^1 + x_JP^0) \bmod N, +$$ +pøièem¾ písmena pova¾ujeme za pøirozená èísla a $P$ je nìjaká vhodná konstanta -- potøebujeme, +aby byla nesoudìlná s~$N$ a aby $P^J$ bylo øádovì vìt¹í ne¾~$N$. +Posuneme-li nyní okénko z~$x_1,\ldots,x_J$ na $x_2,\ldots,x_{J+1}$, he¹ se zmìní takto: +$$\eqalign{ +H(x_2,\ldots,x_{J+1}) &= (x_2P^{J-1} + x_3P^{J-2} + \ldots + x_JP^1 + x_{J+1}P^0) \bmod N \cr +&= (P\cdot H(x_1,\ldots,x_J) - x_1P^J + x_{J+1}) \bmod N. \cr +}$$ +Pokud si mocninu $P^J$ pøepoèítáme, probìhne aktualizace he¹e v~konstantním èase. + +Celý algoritmus pak bude vypadat následovnì: +\algo +\:$X \= H(\iota)$. \cmt{he¹ jehly} +\:$Y \= H(\sigma[{}:J])$. \cmt{he¹ první pozice okénka} +\:Zvolíme~$P$ a~$N$ a pøedpoèítáme $P^J \bmod N$. +\:Pro $I$ od~$0$ do~$S-J$: \cmt{mo¾né pozice okénka} +\::Je-li $X=Y$: +\:::Pokud $\sigma[I:I+J] = \iota$, ohlásíme výskyt na pozici~$I$. +\::Pokud $I SJ$, +algoritmus pobì¾í v~prùmìrném èase $\O(S)$. -Tato hashovací funkce se dokonce nejen hezky poèítá, ale dokonce se i~opravdu \uv{hezky} chová (tedy \uv{rozumnì} náhodnì), pokud zvolíme vhodné~$p$. To bychom mìli zvolit tak, aby bylo rozhodnì nesoudìlné s~$N$ -- tedy $\(p, N) = 1$. Aby se nám navíc dobøe projevilo modulo obsa¾ené v~hashovací funkci, mìlo by být~$p$ relativnì velké (lze dopoèítat, ¾e optimum je mezi $2/3 \cdot N$ a~$3/4 \cdot N$). S~takto zvoleným~$p$ se tato hashovací funkce chová velmi pøíznivì a~v~praxi má celý algoritmus takøka lineární èasovou slo¾itost (prùmìrnou). %% Cvièení: velké abecedy