普遍的なレヌベンシュタむンオヌトマトンを䜿甚した蟞曞でのファゞヌ怜玢。 パヌト2





蚘事の最初の郚分では 、普遍的なレヌベンシュタむンオヌトマトンを調べたした。これは、ある単語Wから䞎えられたレヌベンシュタむン距離以䞋の単語をフィルタリングするための匷力なツヌルです。 次は、このツヌルを䜿甚しお、蟞曞のファゞヌ怜玢の問題を効果的に解決する方法を孊習したす。







レヌベンシュタむンオヌトマトンを䜿甚しお蟞曞のファゞヌ怜玢問題を解決するための最も簡単で明癜なアルゎリズムは、網矅的な怜玢アルゎリズムです。 この堎合、蟞曞の各単語に぀いお、怜玢ク゚リたでのレヌベンシュタむンDamerau-Levenshteinの距離が掚定されたす。 Cの実装䟋は、蚘事の最初の郚分に蚘茉されおいたす。



この単玔なアルゎリズムでさえ、動的プログラミング手法を䜿甚する堎合ず比范しお生産性が向䞊したす。 ただし、より効率的なアルゎリズムがありたす。



シュルツずミホフの基本アルゎリズム



最初のそのようなアルゎリズムを考えおみおください-私の知る限り、 Schultz and Mikhov2002によっお提案されたので、基本的なSchultz and Mikhov アルゎリズムたたは単に基本的なアルゎリズムず呌びたす 。 したがっお、確定したレヌベンシュタむンオヌトマトンA N Wが、歪んだず思われる単語Wず線集距離のしきい倀Nに察しお䞎えられるずしたす。 怜蚎䞭の蟞曞Dも、入力アルファベットEを持぀決定論的有限オヌトマトンA Dずしお衚されたす。 オヌトマトンの状態をそれぞれqおよびq Dで 、遷移関数をVおよびV Dで 、最終状態のセットをFおよびF Dで瀺したす。 SchultzずMihovによっお提案された蟞曞のファゞヌ怜玢のアルゎリズムは、リタヌンを䌎う暙準の怜玢手順であり 、次の擬䌌コヌドで蚘述できたす。







アルゎリズムは、オヌトマトンの初期状態で動䜜を開始したす。 新しい文字が入力に送られるず、埌続の状態が空でない堎合、スタックにプッシュされたす。 䞡方のオヌトマトンの次の状態が有限である堎合、怜玢ワヌドが芋぀かりたす。



このアルゎリズムは、有限状態マシンの「亀差点」ず考えるこずもできたす。 䞡方のオヌトマトンの最終状態に察応する単語のみが、結果のサンプルに分類されたす。 この堎合、䞡方のマシンを空でない状態に倉換するプレフィックスのみが考慮されたす。



アルゎリズムの蚈算の耇雑さは、蟞曞のサむズず線集距離Nに䟝存したす。 Nが蟞曞内の最長単語のサむズに達するず、アルゎリズムはオヌトマトンA Dの状態の完党な列挙になりたす。 しかし、実際の問題を解決するずきは、原則ずしお、小さな倀のNが䜿甚されたす。 この堎合、アルゎリズムはオヌトマトンA Dの状態の非垞に小さなサブセットのみを考慮したす。 N = 0の堎合、アルゎリズムは時間O| W |の蟞曞で単語Wを芋぀けたす。



説明したアルゎリズムは、怜玢䞭に損倱がないこずを保蚌するこずに泚意する必芁がありたす。 ぀たり、 N以内の距離でWから分離されおいる蟞曞の単語の100は、結果のサンプルに分類されたす。



゜フトりェア実装の機胜



プレフィックスツリヌなどのデヌタ構造に既に粟通しおいるず思いたす。 プレフィックスツリヌは、「ロヌドされたツリヌ」たたは「ホり玠」、「ビヌム」、「トラむ」ずも呌ばれ、蟞曞の保存に䜿甚されたす。 この図は、「高速」、「面癜い」、「完党に」、「ファゞヌ」の4぀の単語の蟞曞のプレフィックスツリヌを瀺しおいたす。







プレフィックスツリヌに粟通しおいない堎合は、この構造が詳现に説明されおいる出版物たずえば、 こちらに慣れるこずができたす 。



蟞曞を保存するためにプレフィックスツリヌを䜿甚したいのはなぜですか プレフィックスツリヌは有限状態マシンず芋なすこずができるためです。 ツリヌの各ノヌドは、オヌトマトンの状態を衚したす。 初期状態はツリヌのルヌトであり、最終状態は単語に察応するノヌドです。 各ノヌドずシンボルに察しお、遷移は1぀だけ可胜です-オヌトマトンは決定論的です。



そのため、プレフィックスツリヌを決定論的な有限状態マシンず芋なし、決定論的なレヌベンシュタむンオヌトマトンの゜フトりェア実装を持぀こずを考慮するず、アルゎリズムの擬䌌コヌドをプログラミング蚀語のコヌドに倉えるこずは難しくありたせん。 Cのネタバレの䟋



基本的なアルゎリズム
/// <summary> /// Find all words in dictionary such that Levenstein distance to typo less or equal to editDistance /// </summary> /// <returns>Set of possible corrections</returns> /// <param name="typo">Garbled word.</param> /// <param name="dictionary">Dictionary.</param> /// <param name="editDistance">Edit distance.</param> IEnumerable<string> GetCorrections(string typo, TrieNode dictionary, int editDistance) { var corrections = new List<string> (); if (string.IsNullOrEmpty (typo.Trim())) { return corrections; } //Create automaton LevTAutomataImitation automaton = new LevTAutomataImitation (typo, editDistance); //Init stack with start states Stack<TypoCorrectorState> stack = new Stack<TypoCorrectorState> (); stack.Push (new TypoCorrectorState () { Node = dictionary, AutomataState = 0, AutomataOffset = 0, }); //Traversing trie with standart backtracking procedure while (stack.Count > 0) { //State to process TypoCorrectorState state = stack.Pop(); automaton.LoadState (state.AutomataState, state.AutomataOffset); //Iterate over TrieNode children foreach (char c in state.Node.children.Keys) { //Evaluate state of Levenstein automaton for given letter int vector = automaton.GetCharacteristicVector (c, state.AutomataOffset); AutomataState nextState = automaton.GetNextState (vector); //If next state is not empty state if (nextState != null) { TrieNode nextNode = state.Node.children [c]; //Push node and automaton state to the stack stack.Push (new TypoCorrectorState () { Node = nextNode, AutomataState = nextState.State, AutomataOffset = nextState.Offset }); //Check if word with Levenstein distance <= n found if (nextNode.IsWord && automaton.IsAcceptState (nextState.State, nextState.Offset)) { corrections.Add (nextNode.Value); } } } } return corrections; }
      
      









FB-Trieアルゎリズム



どうぞ 2004 幎、MikhovずSchulzは、䞊蚘のアルゎリズムの修正を提案したした。その䞻なアむデアは、怜玢ク゚リWを2぀のほが等しい郚分W 1ずW 2に分割するこずず組み合わせお、フォワヌドおよびリバヌスプレフィックスツリヌを䜿甚するこずです。 このアルゎリズムはFB-Trie前方および埌方トラむからずしお知られおいたす。



逆プレフィックスツリヌは、蟞曞内のすべおの単語の反転から構築されたプレフィックスツリヌずしお理解する必芁がありたす。 単語の反転、私は単に埌方に曞かれた単語を意味したす。



重芁な点-ミホフずシュルツは圌らの研究においお、2本の線の反転間のDamerau-Levenshtein距離が線自䜓の距離に等しいこずを瀺したした。



N = 1のアルゎリズムの動䜜は、次のステヌトメントに基づいおいたす。Damerau-Levenshtein距離dS、W<= 1だけラむンWから離れおいるラむンSは、 S 1ずS 2の2぀の郚分に分割できたす。盞互に排他的な3぀の条件のいずれか



adS 1 、W 1 = 0およびdS 2 、W 2 <= 1







bdS 1 、W 1 <= 1およびdS 2 、W 2 = 0







cdS 1 、W 1 '= 0およびdS 2 、W 2 '= 0







「c」段萜では、行W 1 'ずW 2 'は、行W 1ずW 2から、最埌の文字W 1を最初の文字W 2に 、たたはその逆に眮き換えるこずによっお取埗されたす。 たずえば、 W 1 = 'FU'およびW 2 = 'ZZY'の堎合、 W 1 ' =' FZ 'およびW 2 ' = 'UZY'です。



オプション「a」に適合するすべおの単語を蟞曞ですばやく芋぀けるにはどうすればよいですか これは非垞に簡単です。プレフィックスツリヌでプレフィックスW 1を持぀ノヌドを芋぀け、基本的なSchultz and Mihovアルゎリズムに埓っおそのすべおの盞続人をバむパスし、 W 2からキヌが1以䞋のものを遞択したす。



オプションA
 //May be for some S d(S1, W1) = 0 and d(S2, W2) <= 1? TrieNode lnode = ForwardDictionary.GetNode (left); if (lnode != null) { corrections.AddRange(GetCorrections(right, lnode, 1)); }
      
      









オプション「b」の堎合、逆接頭蟞ツリヌが䟿利です。逆行W 2に察応するノヌドを芋぀け、そのすべおの盞続人をバむパスし、逆W 1からキヌが1以䞋であるものを遞択したす-基本アルゎリズムに埓っお。



オプションB
 //May be for some S d(S1, W1) = 1 and d(S2, W2) = 0? TrieNode rnode = BackwardDictionary.GetNode (rright); if (rnode != null) { corrections.AddRange(GetCorrections(rleft, rnode, 1)))); }
      
      









オプション「c」の堎合、パヌティション境界䞊の単語Wの 2文字を亀換し、結果の単語がプレフィックスツリヌに含たれおいるかどうかを確認するだけです。



オプションB
 //May be there is a transposition in the middle? char[] temp = typo.ToCharArray (); char c = temp [llen - 1]; temp [llen - 1] = temp [llen]; temp [llen] = c; TrieNode n = ForwardDictionary.GetWord (new string(temp)); if (n != null) { corrections.Add (n.Key); }
      
      









FB-Trieアルゎリズムを䜿甚しおディクショナリのファゞヌ怜玢の問題を解決するには、䞊蚘の3぀の単語セットを芋぀けおそれらを結合するだけです。



N = 2の堎合、さらに2぀のケヌスを考慮する必芁がありたす。



adS 1 、W 1 = 0およびdS 2 、W 2 <= 2







b1 <= dS 1 、W 1 <= 2およびdS 2 、W 2 = 0







cdS 1 、W 1 = 1およびdS 2 、W 2 = 1







たた、文字列S 1の最埌の文字がW 2の最初の文字に等しく、 W 1の最埌の文字がS 2の最初の文字に等しい堎合、さらに2぀のケヌスが考えられたす。



ddS 1 、W 1 '= 0およびdS 2 、W 2 '<= 1







ddS 1 、W 1 '<= 1およびdS 2 、W 2 '= 0







最初の2぀のケヌスは、 N = 1のオプション「a」ず「b」の類掚によっお簡単に怜出されたすが、 N = 2のレヌベンシュタむンオヌトマトンが䜿甚されたす。



N = 2のオプション「g」ず「d」は、 N = 1のオプション「a」ず「b」を繰り返したす。郚分文字列W 1ずW 2の代わりに、 W 1 'ずW 1 'が䜿甚されたす。



オプション「c」はもう少し耇雑です。 最初のステップでは、 W 1基本アルゎリズムから1以䞋のプレフィックスに察応するすべおのノヌドを盎接プレフィックスツリヌで芋぀ける必芁がありたす。 2番目のステップでは、そのようなノヌドごずに、子䌚瀟をバむパスし、 W 2から1以䞋の間隔のキヌに察応する子䌚瀟を遞択する必芁がありたす再び、基本アルゎリズム。



N = 3の堎合、7぀のケヌスを考慮する必芁がありたす。 ここでは玹介したせん。Mikhovand Schulz2004のオリゞナル蚘事をご芧ください。 類掚により、任意のNに぀いお続行できたすが、実際的な問題を解決する際にこれが必芁になるこずはほずんどありたせん。



性胜評䟡



興味深いこずに、FB-Trieアルゎリズムを䜿甚した怜玢時間は、語長Wが増加するに぀れお枛少したす。



他の広く知られおいるファゞヌ怜玢アルゎリズムず比范したFB-Trieアルゎリズムを䜿甚した怜玢時間の詳现な分析は、Leonid Boytsovの研究「近䌌蟞曞怜玢のための玢匕付け方法比范分析」2011にありたす。 この䜜業により、次のようなアルゎリズムで怜玢時間ず消費されるメモリ量を培底的に比范できたす。





ここで倚数の数字ずグラフをすべお繰り返すのではなく、自然蚀語の䞀般的な結論に限定したす。



そのため、FB-Trieアルゎリズムは、パフォヌマンスずメモリ消費の合理的な劥協点を提䟛したす。 アプリケヌションが2以䞊の線集距離を維持する必芁があり、蟞曞に500,000以䞊の単語が含たれおいる堎合-FB-Trieアルゎリズムは合理的な遞択です。 合理的なメモリ消費で最小怜玢時間を提䟛したすレキシコンが䜿甚するメモリの玄300。



線集距離をN = 1に制限する堎合、たたは小さな蟞曞がある堎合は、倚くのアルゎリズムがより高速に動䜜する可胜性がありたすたずえば、Mor-Fraenkelメ゜ッドたたはFastSS が、メモリ消費の増加レキシコンサむズの最倧20,000に備えおください。 ファゞヌむンデックスを保存するために数十ギガバむトのRAMがある堎合、これらのメ゜ッドを倧きな蟞曞サむズで䜿甚できたす。



読者がこれがどれだけであるかを決めるこずができるように-500,000単語、ロシア語の単語数 ここから匕甚 に぀いおいく぀かの数字を瀺したす。



  1. Lopatinのスペル蟞曞には162,240語が含たれおいたす。レキシコンファむルのサむズは2 MBです。
  2. ロシア語の姓のリストには、少なくずも247,780の姓が含たれおいたす。レキシコンファむルのサむズは4.6 MBです。
  3. A. A. Zaliznyakによるロシア語の完党に匷調されたパラダむムは、2,645,347の単語圢匏であり、レキシコンのファむルサむズは玄35 MBです。




しかし、2぀のプレフィックスツリヌの圢匏で蟞曞を保存する機胜がない堎合はどうでしょうか。 たずえば、゜ヌトされたリストずしお衚瀺されたす。 この堎合、ファゞヌ怜玢にレヌベンシュタむンオヌトマトンを䜿甚するこずは可胜ですが、実甚的ではありたせん。 おそらく-培底的な怜玢のさたざたな倉曎が残っおいるためですたずえば、単語の長さに沿ったクリッピング| W | plus minus N 。 実装がより簡単な方法 サンプル拡匵アルゎリズムなど ず比范しおパフォヌマンスが向䞊しないため、実甚的ではありたせん。



基本的なSchultz and Mihovアルゎリズムは、FB-Trieアルゎリズムよりも2倍少ないメモリしか必芁ずしないこずに泚意しおください。 ただし、怜玢時間を1桁増やすこずで、この費甚を支払う必芁がありたすアルゎリズムの䜜成者の評䟡。



これに぀いおは、完党なLevenshteinオヌトマトンを䜿甚した蟞曞内のファゞヌ怜玢アルゎリズムの怜蚎を怜蚎したす。



はい、CのSpellcheckerの完党な゜ヌスコヌドはこちらにありたす 。 おそらく私の実装はパフォヌマンスの点では最適ではありたせんが、FB-Trieアルゎリズムの動䜜を理解するのに圹立ち、アプリケヌションの問題を解決するのに圹立぀かもしれたせん。



出版物を読んだすべおの人に-あなたの興味に感謝したす。



参照資料



  1. 私の投皿の最初の郚分
  2. レヌベンシュタむンオヌトマトンずシュルツずミホフの基本アルゎリズム2002
  3. 怜玢を返す
  4. Mihov and Schulzによる蚘事のFB-Trieアルゎリズム2004
  5. ファゞィ怜玢専甚のLeonid Boytsovのサむト
  6. 蟞曞ずテキストのあいたい怜玢に関する良い投皿
  7. プレフィックスツリヌに぀いおHabréに投皿する
  8. FastSSアルゎリズム
  9. Cの蚘事の゜ヌス
  10. Javaでの実装 1ず2
  11. ロシア語蟞曞のセット



All Articles