情報理論、言語学、コンピュータ科学において、レーベンシュタイン距離は、2 つのシーケンス間の違いを測定するための文字列距離です。2 つの単語間のレーベンシュタイン距離は、一方の単語をもう一方の単語に変更するために必要な、1 文字の編集 (挿入、削除、または置換) の最小数です。これは、1965 年にこの距離を定義したソビエトの数学者ウラジーミル・レーベンシュタインにちなんで名付けられました。 [ 1 ]
レーベンシュタイン距離は編集距離とも呼ばれるが、この用語はより広い範囲の距離尺度を指す場合もある。[ 2 ]: 32これはペアワイズ文字列アライメントと密接に関連している。
2本の弦間のレーベンシュタイン距離(長さ)そしてそれぞれ)は次のように与えられるどこ
どこである文字列の最初の文字を除くすべての文字の文字列です(つまり))、 そしては最初の文字です(つまり)) 表記法またはは、文字列の 番目の文字0から数えて 、したがって。
最小値の最初の要素は削除に対応します(に)、2番目は挿入、3番目は交換です。
この定義は、素朴な再帰的実装に直接対応しています。
特別なケースがない式は次のとおりです。
例えば、「kitten」と「sitting」の間のレーベンシュタイン距離は3です。なぜなら、以下の3回の編集で一方を他方に変えることができ、3回未満の編集でそれを実現する方法はないからです。
削除の簡単な例として、「uninformed」と「uniformed」が挙げられます。これらの単語間の距離は1です。
レーベンシュタイン距離には、いくつかの単純な上限値と下限値があります。これらには以下が含まれます。
同じ長さの2つの文字列間のレーベンシュタイン距離がハミング距離よりも厳密に小さい例として、「flaw」と「lawn」のペアが挙げられます。この場合、レーベンシュタイン距離は2になります(先頭の「f」を削除し、末尾に「n」を挿入します)。ハミング距離は4です。
近似文字列マッチングでは、少数の差異が予想される状況において、多数の長いテキストの中から短い文字列の一致を見つけることが目的です。短い文字列は、例えば辞書から取得できます。この場合、一方の文字列は通常短く、もう一方は任意の長さです。この手法は、スペルチェッカー、光学文字認識の修正システム、翻訳メモリに基づく自然言語翻訳支援ソフトウェアなど、幅広い用途があります。
レーベンシュタイン距離は、より長い2つの文字列間でも計算できますが、計算コストは2つの文字列の長さの積にほぼ比例するため、実用的ではありません。したがって、レコードリンケージなどのアプリケーションであいまい文字列検索を支援するために使用する場合は、比較速度を向上させるために、比較対象の文字列は通常短くなります。
言語学では、レーベンシュタイン距離は言語的距離、つまり2つの言語が互いにどれだけ異なっているかを定量化する指標として使用されます。[ 3 ]これは相互理解可能性と関連しています。言語的距離が大きいほど相互理解可能性は低くなり、言語的距離が小さいほど相互理解可能性は高くなります。
レーベンシュタイン距離は、音声聴力検査などのさまざまな用途において、音声識別テスト中のリスナーのパフォーマンスを定量化するために使用できます。この文脈では、レーベンシュタイン距離は、リスナーに提示された刺激と識別された音素のシーケンス間の距離を定量化するために計算されます。音素置換に関連するコストは、固定されている場合と、置換された 2 つの音素間で異なる音韻的特徴の数に依存する場合があります。[ 4 ]
バイオインフォマティクスでは、レーベンシュタイン距離や類似のアルゴリズムは、DNAやタンパク質などの生物学的配列間の違いを測定します。アルゴリズムの編集は、遺伝子変異、すなわちヌクレオチド(DNA)またはアミノ酸(タンパク質)の挿入、欠失、または置換に対応します。2つの配列間の距離が小さいほど、進化的または機能的な関係が近いことを示します。[ 5 ]
編集距離には他にも一般的な指標があり、それらは異なる一連の許容編集操作を使用して計算されます。例えば、次のとおりです。
編集距離は通常、特定の許容編集操作セットを用いて計算されるパラメータ化可能な指標として定義され、各操作にはコスト(場合によっては無限大)が割り当てられます。これは、Smith-WatermanアルゴリズムなどのDNA配列アライメントアルゴリズムによってさらに一般化され、操作のコストはそれが適用される場所によって変化します。
これは、2つの文字列sとt 、およびそれぞれの長さを受け取り、それらの間のレーベンシュタイン距離を返す関数の、単純だが非効率的な再帰的なHaskell実装です。lDistance
lDistance :: Eq a => [ a ] -> [ a ] -> Int lDistance [] t = length t -- s が空の場合、距離は t の文字数ですlDistance s [] = length s -- t が空の場合、距離は s の文字数ですlDistance s @ ( a : s' ) t @ ( b : t' ) | a == b = lDistance s' t' -- 最初の文字が同じ場合は無視できます| otherwise = 1 + minimum -- それ以外の場合は、3 つの可能なアクションをすべて試して、最適なものを選択します[ lDistance s t' -- 文字が挿入されます (b が挿入) 、lDistance s' t -- 文字が削除されます (a が削除) 、lDistance s' t' -- 文字が置き換えられます (a が b に置き換えられます) ]この実装は、同じ部分文字列のレーベンシュタイン距離を何度も再計算するため、非常に非効率的です。
より効率的な方法では、同じ距離計算を繰り返すことは決してありません。例えば、考えられるすべての接尾辞のレーベンシュタイン距離を配列に格納することができます。、 どこ最後の間の距離は文字列の文字sと最後の文字列の文字t。この表は、0行目から始めて1行ずつ簡単に作成できます。表全体が作成されると、目的の距離は表の最後の行と列にあり、のすべての文字とのすべての文字 間の距離を表します。st
このセクションでは、0 ベースの文字列ではなく 1 ベースの文字列を使用します。mが行列の場合、は、行列のi行目とj列目であり、最初の行のインデックスは 0、最初の列のインデックスも 0 です。
レーベンシュタイン距離の計算は、最初の文字列のすべての接頭辞と2番目の文字列のすべての接頭辞の間のレーベンシュタイン距離を保持する行列を用意すれば、動的計画法を用いて行列内の値を計算し、最後に計算された値として2つの文字列全体の距離を求めることができるという観察に基づいています。
このアルゴリズムは、ボトムアップ動的計画法の例であり、その変種については、 Robert A. Wagner と Michael J. Fischer による1974 年の論文「文字列間の修正問題」で議論されている。[ 6 ]
これは、長さmの文字列sと長さnの文字列tの2つの文字列を受け取り、それらの間のレーベンシュタイン距離を返す関数の、簡潔な擬似コードによる実装です。LevenshteinDistance
function LevenshteinDistance ( char s [ 1 .. m ] , char t [ 1 .. n ]) : // すべての i と j について、d[i,j] にはs の最初の i 文字と t の最初の j 文字間のレーベンシュタイン距離が格納されますdeclare int d [ 0 .. m , 0 .. n ] dの各要素をゼロに設定します// ソースプレフィックスは、すべての文字を削除することで空の文字列に変換できますfor i from 1 to m : d [ i , 0 ] := i // 空のソースプレフィックスから、すべての文字を挿入することでターゲットプレフィックスに到達できますfor j from 1 to n : d [ 0 , j ] := j for j from 1 to n : for i from 1 to m : if s [ i ] = t [ j ] : substitutionCost := 0 else : substitutionCost := 1d [ i , j ] := minimum ( d [ i - 1 , j ] + 1 , // 削除d [ i , j - 1 ] + 1 , // 挿入d [ i - 1 , j - 1 ] + substitutionCost ) // 置換return d [ m , n ]結果として得られる行列の例を2つ示します(タグ付けされた数値にカーソルを合わせると、その数値を取得するために実行された操作が表示されます)。
アルゴリズム全体を通して維持される不変条件は、最小限の操作で初期セグメントを変換できるという点です。最終的に、配列の右下隅の要素が答えとなります。s[1..i]t[1..j]d[i,j]
編集された入力文字列を再構築する必要がない場合、構築に必要なのはテーブルの2行(前の行と現在計算中の行)だけであることが判明した。
レーベンシュタイン距離は、次のアルゴリズムを使用して反復的に計算できます。[ 7 ]
function LevenshteinDistance ( char s [ 0 .. m - 1 ] , char t [ 0 .. n - 1 ]) : // 整数距離の 2 つの作業ベクトルを作成しますdeclare int v0 [ n + 1 ] declare int v1 [ n + 1 ]// v0 (前の行の距離) を初期化します。// この行は A[0][i]: 空の s から t までの距離を編集します。// その距離は、s に追加して t を作成する文字数です。for i from 0 to n : v0 [ i ] = ifor i from 0 to m - 1 : // 前の行 v0 から現在の行の距離 v1 を計算する// v1 の最初の要素は A[i + 1][0] // 編集距離は、空の t に一致させるために s から (i + 1) 文字を削除するv1 [ 0 ] = i + 1// 式を使用して行の残りの部分を埋めます。jが0からn - 1までの場合:// A[i + 1][j + 1] のコストを計算します。deletionCost := v0 [ j + 1 ] + 1 insertionCost := v1 [ j ] + 1 if s [ i ] = t [ j ] : substitutionCost := v0 [ j ] else : substitutionCost := v0 [ j ] + 1v1 [ j + 1 ] := minimum ( deletionCost , insertionCost , substitutionCost )// 次のイテレーションのために、v1 (現在の行) を v0 (前の行) にコピーします// v1 のデータは常に無効化されるため、コピーなしのスワップの方が効率的ですv0とv1 をスワップします// 最後のスワップの後、v1 の結果は v0 に格納されますreturn v0 [ n ]ヒルシュベルクのアルゴリズムは、この方法と分割統治法を組み合わせたものです。編集距離だけでなく、最適な編集シーケンスも、同じ漸近的な時間と空間の範囲内で計算できます。[ 8 ]
レーベンシュタインオートマトンによって、ある文字列が、与えられた文字列から与えられた定数よりも編集距離が小さいかどうかを効率的に判定できます。[ 9 ]
長さnの 2 つの文字列間のレーベンシュタイン距離は、係数の範囲内で近似できます。
ここで、ε > 0は調整可能な自由パラメータであり、時間O ( n 1 + ε )で調整できます。[ 10 ] 。10年後、研究者たちは、実行時間は同じだが近似係数がf(1/ ε )のアルゴリズムを発見しました。このアルゴリズムは、 εのみに依存する関数fに対して機能します。 [ 11 ]。
長さnの 2 つの文字列のレーベンシュタイン距離は、強い指数時間仮説が偽でない限り、ゼロより大きい任意の ε に対してO ( n 2 − ε )の時間で計算できないことが示されている。[ 12 ] この問題の複雑さに関する別の (無条件の) 下限は、文字列のシンボルに対する唯一のクエリが2つのシンボルの比較であるモデルにおいて。[ 13 ]
…内容語、同族語の割合(直接的または同義語を介して関連)、語彙的関連性、文法的関連性。