ダメラウ・レーベンシュタイン距離

提供: testwiki
ナビゲーションに移動 検索に移動

ダメラウ・レーベンシュタイン距離(ダメラウ・レーベンシュタインきょり、テンプレート:Lang-en-short)は、2つの配列の間の編集距離を測定するために情報理論計算機科学で使われる文字列計量である。2つの単語間のダメラウ・レーベンシュタイン距離は、一方の単語を他方の単語に変換するのに必要な最小の操作回数である。ここで1回の「操作」とは1文字の挿入、削除、置換、あるいは2つの隣り合う文字の交換である。ダメラウ・レーベンシュタイン距離はテンプレート:Illウラジミール I. レーベンシュタインにちなんで名付けられた[1][2] [3]

古典的なレーベンシュタイン距離を定義する操作は3つの古典的単文字編集操作、すなわち文字の挿入、削除、および置換である。ダメラウ・レーベンシュタイン距離を定義する操作にはこれらに加えて隣接文字交換が含まれている[4][2]

ダメラウによると、スペルミス全体の80%以上は、これら4操作のうちの1操作の誤り1つで表現できる[5]。ダメラウの論文では、最大1回の編集操作で訂正できる綴り間違いのみが考慮されていた。当初の動機は、スペルチェッカなどのアプリケーション・ソフトウェアを改善するために人間のスペルミス間の距離を測定することであったが、ダメラウ・レーベンシュタイン距離は、生物学においてタンパク質配列の変異を測定するためにも使われている[6]

ダメラウ・レーベンシュタイン距離と制限ダメラウ・レーベンシュタイン距離

同じ文字列を2度以上編集することを許さない条件のもとで求められる編集距離を制限編集距離と呼ぶ。制限編集距離は最適文字列整列距離と呼ばれる量に一致することが知られている。この条件を課さない(つまり同じ文字列を1回より多く編集してよい)場合には、単に編集距離と呼ぶ。制限レーベンシュタイン距離は常にレーベンシュタイン距離に一致する。これは、レーベンシュタイン距離の計算では編集操作は1文字ごとであり、1度編集した文字列を2度編集する必要がないからである。しかし2文字の編集操作が存在するダメラウ・レーベンシュタイン距離に関しては、制限ダメラウ・レーベンシュタイン距離とダメラウ・レーベンシュタイン距離とが一致しない場合がある。

例として、CAABCの編集距離を求める。CAに1回の隣接文字交換および1回の文字挿入を施せば CAACABC となるから、ダメラウ・レーベンシュタイン距離はLD(CA,ABC)=2である。これに対して、制限距離(あるいは最適文字列整列距離 Optimal String Alignment, OSA)を求めるための編集は1回の文字削除に続いて2回の文字挿入を施す CAAABABC であり、制限距離は OSA(CA,ABC)=3である。ダメラウ・レーベンシュタイン距離を求めるときに使った編集 CAACABC が使えないのは、B を挿入する時点で同じ文字列を2回編集しており、この編集は制限距離を求める操作では禁止されているからである。制限距離については、三角不等式が一般には成立しないことに注意する。実際、OSA(CA,AC)+OSA(AC,ABC)<OSA(CA,ABC)である。つまり制限距離は計量ではない。

ここでは計算が簡単な制限ダメラウ・レーベンシュタイン距離を求める。文字列 aの先頭から数えて i番目の1文字を a[i]とし、aの先頭から i番目までの長さ iの部分文字列を a[1,i]とする。2つの文字列 aおよび bの間の制限ダメラウ・レーベンシュタイン距離を定義するためにまず、文字列 aの部分文字列 a[1,i]と、bの部分文字列 b[1,j]の間の制限距離関数 da,b(i,j)を、次のように再帰的に定義する: [7] テンプレート:Rp

da,b(i,j)=min{0if i=j=0da,b(i1,j)+1if i>0da,b(i,j1)+1if j>0da,b(i1,j1)+1(a[i]b[j])if i,j>0da,b(i2,j2)+1if i,j>1 and a[i]=b[j1] and a[i1]=b[j]

ここで1(a[i]b[j])は、a[i]=b[j]のとき0になり、それ以外の場合に1となる指示関数である。これらの場合分けはそれぞれ、次に示す部分文字列末尾の編集操作(あるいは編集操作しないこと)に対応している:

  • 0:2つの長さ0の文字列を一致させるのに必要な編集回数は0
  • da,b(i1,j)+1a[1,i]が、a[1,i1]に1回の挿入をすることで得られたと見なして編集回数を1だけ増やす
  • da,b(i,j1)+1b[1,j]が、b[1,j1]に1回の挿入をすることで得られたと見なして編集回数を1だけ増やす
  • da,b(i1,j1)+1(a[i]b[j])a[i]b[j]が一致する場合には、a[1,i]b[1,j]の間の距離はa[1,i1]b[1,j1]の間の距離に等しいので編集回数を変更しない。a[i]b[j]が一致しない場合には、a[i]b[j]に、あるいはb[j]a[i]に置換したと見なして編集回数を1だけ増やす
  • da,b(i2,j2)+1a[i]=b[j1]かつa[i1]=b[j]のとき、a[i1]a[i]の交換、あるいはa[i1]a[i]の交換をしたと見なして編集回数を1回だけ増やす

abの間の制限ダメラウ・レーベンシュタイン距離は、文字列全体の関数値 da,b(|a|,|b|)で与えられる。ここで|a|および|b|はそれぞれ文字列aおよびbの長さ(文字数)である。

アルゴリズム

ここでは2つのアルゴリズムを示す。1つ目は、制限ダメラウ・レーベンシュタイン距離[7]を求めるアルゴリズム[8]である。これは2つ目のアルゴリズムに比べて単純である。2つ目のアルゴリズムは、非制限ダメラウ・レーベンシュタイン距離を求めるもので、レーベンシュタイン距離に隣接交換を追加して計算する[9]。隣接交換を追加すると非常に複雑になる。

制限ダメラウ・レーベンシュタイン距離

制限ダメラウ・レーベンシュタイン距離は、レーベンシュタイン距離を計算するワグナー・フィッシャー動的計画法アルゴリズムを単純に拡張することで計算できる。その擬似コードは:

 algorithm OSA-distance is
     input: strings a[1..length(a)], b[1..length(b)]
     output: distance, integer
     
     let d[0..length(a), 0..length(b)] // d は整数二次元配列で次元は length(a)+1, length(b)+1
     // ここでは d は添字が 0 から始まる配列であり、a と b は添字が 1 から始まる配列であることに注意する
     
     for i := 0 to length(a) inclusive do
         d[i, 0] := i
     for j := 0 to length(b) inclusive do
         d[0, j] := j
     
     for i := 1 to length(a) inclusive do
         for j := 1 to length(b) inclusive do
             if a[i] = b[j] then
                 cost := 0
             else
                 cost := 1
             d[i, j] := minimum(d[i-1, j] + 1,     // 削除
                                d[i, j-1] + 1,     // 挿入
                                d[i-1, j-1] + cost)  // 置換
             if i > 1 and j > 1 and a[i] = b[j-1] and a[i-1] = b[j] then // 隣接文字交換:ここから
                 d[i, j] := minimum(d[i, j],
                                    d[i-2, j-2] + cost)  // 隣接文字交換:ここまで
     return d[length(a), length(b)]

レーベンシュタイン距離を求めるアルゴリズムとの違いは、隣接文字交換にあたる計算が含まれる点である。

ダメラウ・レーベンシュタイン距離

次は、真のダメラウ・レーベンシュタイン距離を求めるアルゴリズムである。このアルゴリズムでは、アルファベットに含まれる全文字 テンプレート:Math の個数|Σ|が必要になる。成分が テンプレート:Math に含まれる配列を使うからである[7]テンプレート:Rp。擬似コードは:

 algorithm DL-distance is
     input: strings a[1..length(a)], b[1..length(b)]
     output: distance, integer
     
     da := |Σ|個の整数からなる配列
     for i := 1 to |Σ| inclusive do
         da[i] := 0
     
     let d[1..length(a), 1..length(b)] // d は次元が length(a)+2, length(b)+2 の二次元整数配列
     // d の添字は -1 から始まり、a, b, および da の添字は 1 から始まることに注意
     maxdist := length(a) + length(b)
     d[1, 1] := maxdist
     for i := 0 to length(a) inclusive do
         d[i, 1] := maxdist
         d[i, 0] := i
     for j := 0 to length(b) inclusive do
         d[1, j] := maxdist
         d[0, j] := j
     
     for i := 1 to length(a) inclusive do
         db := 0
         for j := 1 to length(b) inclusive do
             k := da[b[j]]
              := db
             if a[i] = b[j] then
                 cost := 0
                 db := j
             else
                 cost := 1
             d[i, j] := minimum(d[i1, j1] + cost,  //置換
                                d[i,   j1] + 1,     //挿入
                                d[i1, j  ] + 1,     //削除
                                d[k1, 1] + (ik1) + 1 + (j-1)) //交換
         da[a[i]] := i
     return d[length(a), length(b)]

非制限ダメラウ・レーベンシュタイン距離を求める正しいアルゴリズムを作るには次のことに注意する:1度交換された文字がそのあと2度と編集されないような編集操作の最適配列が存在する(交換のコストWTが挿入のコストWIと削除のコストWDの平均以上つまり2WTWI+WDの場合に成立する[9])。したがって、1つの部分文字列を2回以上編集する2つの対称な方法:

  1. 隣接文字を交換して任意の個数の文字をその間に挿入する
  2. 文字列を削除し、その削除によって新たに隣り合うことになった隣接文字を交換する

のいずれかだけを考えればよい。このアイディアを素直に実行するアルゴリズムは、文字列の長さMおよびNに対してO(MNmax(M,N))の計算複雑性を持つ。ローランスとワグナーのアイディア[9]を使ったアルゴリズムでは、最悪の場合でもO(MN)に改善される。

Bitapアルゴリズムを、隣接交換を処理するように変更できる。この例については参考文献テンプレート:Refの情報収集の節を見よ。

応用

ダメラウ・レーベンシュタイン距離は、自然言語処理において重要である。自然言語では文字列が短く、誤り(綴り間違い)の個数が2を超えることは少ない。このような場合、制限編集距離と非制限編集距離の値が異なることは稀である。オーメンとローク[8]は、一般化交換を導入して制限編集距離を拡張した。しかし、制限編集距離は一般には三角不等式を満足せず計量ではないので、メトリックツリーに使えないことに注意すべきである。

DNA

DNAでは挿入、削除、置換、および交換が頻繁に生じ、しかもどれも同程度の時間スケールで起こるので、ダメラウ・レーベンシュタイン距離は2つのDNA鎖の間の変化を計る計量として使うことができる。DNA、タンパク質、および他のバイオインフォマティクスなど、要素の整列に関連する課題では、ダメラウ・レーベンシュタイン距離に深く関係するニードルマン・ブンシュ・アルゴリズムやスミス・ウォーターマン・アルゴリズムなどを使うのが一般的である。

輸出規制

アメリカ合衆国政府は統合スクリーニング・リストAPI[10]で、あいまい語検索のためにダメラウ・レーベンシュタイン距離を利用している。

その他

  • Ispell はダメラウ・レーベンシュタイン距離が1の単語を訂正候補として提示する

脚注

テンプレート:脚注ヘルプ テンプレート:Reflist

参考文献