レーベンシュタイン距離ビジュアライザー

2つの文字列を入力すると、そのレーベンシュタイン(編集)距離 — 一方をもう一方に変換するために必要な最小の1文字単位の編集回数 — がわかります。

インタラクティブシミュレーションを読み込んでいます...

レッスン

理論 — レーベンシュタイン距離ビジュアライザー

レーベンシュタイン距離とは、2 つの文字列について、一方を他方へ変えるのに必要な 1 文字単位の編集の最小回数です。編集とは挿入・削除・置換のいずれかを指します。これは「やり方のあらゆる可能性にわたる最小値」であり、だからこそ目で見て求めるのは難しく、表を使えば簡単になります。編集手順の組み合わせは天文学的な数にのぼりますが、この方法はその一つも列挙せずに最短のものを求めます。

各記号の意味

m, n
2 つの文字列 A と B の長さ。
D(i, j)
A の先頭 i 文字を B の先頭 j 文字へ変える最小コスト。このページの表の 1 マスにあたります。
[aᵢ ≠ bⱼ]
その 2 文字が異なれば 1、一致すれば 0。置換の値段で、置換する必要がなければ無料になります。
d
求める答え D(m, n)。右下のマスです。

公式の導き方

  1. マスを埋める前に、1 マスの意味を決めます。D(i, j) を、A の先頭 i 文字を B の先頭 j 文字へ変えるのに必要な最小の編集回数とします。求めたいのは D(m, n) であり、ほかのマスはすべて同じ問いの小さい版です。
  2. 端は考えるまでもありません。何もない状態から B の先頭 j 文字を作るには j 回すべて挿入するしかないので D(0, j) = j。A の先頭 i 文字を何もない状態まで削るには i 回すべて削除するしかないので D(i, 0) = i。これが表の最上行と左端の列であり、そこが単に数を増やしていくだけの理由です。
  3. ここからが要となる一歩です。D(i, j) の最適な手順を一つ取り、その最後の一手だけを見ます。可能性はちょうど三つしかありません。aᵢ を削除して D(i−1, j) を残したか、bⱼ を挿入して D(i, j−1) を残したか、aᵢbⱼ を対応づけて D(i−1, j−1) を残したかです。いずれもすでに埋め終えたマスです。
  4. したがってそのマスは三つのうち最も安いものになります。D(i, j) = min( D(i−1, j) + 1, D(i, j−1) + 1, D(i−1, j−1) + [aᵢ ≠ bⱼ] )。左から右へ、上から下へ埋めていけば、どのマスに来たときにも三つの隣は既知です。これがアルゴリズムのすべてで、m × n マス、1 マスにつき 3 回の比較で終わります。

表示の読み方

このページの表がまさにその表です。行が A、列が B で、左上に一つ余分にある行と列が手順 2 の空の接頭辞です。だからそこは 0, 1, 2, 3 と数えるだけになっています。マスの濃淡は値そのもので、淡い対角の帯は 2 つの文字列がまだ安く一致している場所です。どのマスでもカーソルを合わせるかタップすれば、下のパネルがそのマスについて手順 3 を書き下します — 三つの候補それぞれの計算と、勝った候補の強調表示です。色のついた線は右下から逆にたどった最適経路の一つで、その下のアラインメント帯は同じ経路を文字で書き出したものです。

前提
編集はどれもちょうど 1 のコストで、文字は完全一致で比較されます。大文字小文字の吸収も、Unicode の正規化も、どの文字が似ているかという概念もありません。その結果がこのページで二つ見えます。ここで数えているのは UTF-16 の符号単位であって、あなたが文字と呼ぶものではありません。片方の欄に絵文字を 1 つだけ入力すると表は 3 × 1 になります。絵文字 1 つが 2 単位だからで、削除には 2 かかります。また、合成済みの é を使った café と、e に結合アクセントを付けた café は、画面上は見分けがつかないのに距離 2 と出ます。
成り立たない場合
表は m × n マスなので、手間は 2 つの長さの積で増えます。単語 2 つなら問題ありませんが、100 万語の辞書に対する 1 件の問い合わせでは絶望的です。表が 100 万枚必要になります。ここで自然に望まれるのはもっと賢いアルゴリズムですが、驚くべきことに事実上存在しません。Backurs と Indyk は 2015 年に、真に劣二次時間の編集距離が求まるなら強指数時間仮説が覆ることを示しました。実際のスペルチェッカーはこの下限を破るのではなく、避けています。距離が小さな上限を超えた時点で打ち切るか、ほとんどの候補がそもそも比較されないように索引を張るのです。

すべての部分問題が一度しか問われないため、グリッドは小さく済む 🖖

編集手順のあらゆる組み合わせを探索するのは絶望的である — 単語の長さとともに順序の数が爆発的に増加するためだ。これを救う洞察は、どんな接頭辞のペアにも最適な答えがひとつだけ存在し、文字列の後半で何が起きようとその答えは決して変わらないということである。したがって、手順を探索する代わりにグリッドを埋めていく。最初の単語の1文字につき1行、2番目の単語の1文字につき1列、さらに空の接頭辞を表す空の1行と1列を加える。したがって kittensitting に変換することは 7 × 8 = 56 個のマス目を埋めることになり、各マス目はすでに埋まっている3つの隣接マスを見るだけで決まり、答えの 3 が右下の隅で待っている。計算量は編集方法のパターン数ではなく、2つの長さのに応じて増加する。

入れ替えは、そう指定しない限り 2 回分の編集になる 🖖

formfrom を入力すると、レーベンシュタインは 2 と答えます。文字が入れ替わったという概念を持たないため、置換 2 回分を支払うのです。しかし文字の入れ替えは最もありふれた打ち間違いのひとつであり、だからこそダメラウは 4 つ目の操作を加えました。必要な 2 文字が、いま逆順で手元にあるその 2 文字と同じなら、2 つまとめて 1 回分の値段で取ってよい、というものです。上のコストモデルを切り替えれば、同じ組が 1 になります。単語は何も変わっていません。変わったのは価格表です。編集距離を類似度スコアとして使う場面では、この点を覚えておく価値があります。その数値は 2 つの文字列の性質であると同時に、選んだコストモデルの性質でもあるからです。

速い実装はこの図を描けない 🖖

各セルが必要とするのは真上の行と左隣のセルだけで、それより前の行は残っていても何の役にも立ちません。実際の実装はこの取引に乗り、表全体ではなく 2 行だけを保持します。これでメモリは m × n から min(m, n) になります — 1000 文字の文字列 2 本なら、約 100 万セルが 2000 セルへ。その代わりに手放すのが、まさに上に描かれているものです。距離は最後のセルに残りますが、そこへ至る経路は上書きされた行の中にありました。つまり 2 行方式の実装は、2 つの文字列が 3 回の編集で隔たっていることは言えても、どの 3 回かは言えません。完全な表なしに経路を取り戻すのは見た目より難しく、ハーシュバーグが 1975 年に、表を中央で二分して両半分へ再帰的に降りることで解決しました。

全プロセスの詳細解説

  1. kittenをsittingに変換する最小コストの編集手順 5 ステップ

    kittensitting に変換せよ。最もコストの低い編集系列を求め、それより低コストなものが存在しないことを証明せよ。さらに、2 つ目の単語が辞書全体である場合にこの計算コストがどうなるかを算出せよ。

    1. 漸化式こそがアルゴリズムのすべてである。各セルは、削除、挿入、または現在の 2 文字を整列させること(両者が一致していれば最後の操作は無料)によってここに到達するコストを求める。あるセルに必要なのは、上および左に隣接する 3 つのセルだけであり、左から右へ 1 回スキャンすれば表全体が埋まる。

    2. 表を埋める前に、解の範囲を挟み込む。文字列の長さの差は 1 であるため、少なくとも 1 回の挿入は避けられない。また、単語全体を完全に書き換えるコストは 7 である。解はその間に存在し、この時点ですでに大半の推測が排除される。

    3. 3 回の編集で達成できる。k → s の置換、e → i の置換、g の挿入である。それぞれが 1 回の正当な編集であり、この手順の連鎖によって目的の文字列に到達する。

    4. 3 回の編集を示しただけでは、距離が高々 3 であることしか証明できない。表は距離が正確に 3 であることを証明する。なぜなら、右下の角に至るすべての経路がスコア化され、その途中のすべてのセルで最小値が取られるからである。これこそが、単に解を見つけることと、それが決して上回られない(最善である)と知ることの違いである。

    5. コストは文字のペアごとに 1 セルであり、ここでは 42 セルに過ぎず、取るに足らない。しかし、1 単語あたり 7 文字からなる 100 000 単語の辞書に対しては、わずか 1 回の検索で 400 万回を超えるセルの更新が必要となる。

    解答

    ツールは 3 と、類似度 57.14%(これは 1 − 3/7、すなわち長い方の単語で正規化した距離)を出力する。どちらも手計算で 1 分もあれば埋められる表から得られる。スケーラビリティを欠いているのは表そのものである。計算量は Θ(mn) であるため、両方の文字列の長さを 2 倍にすると計算量は 4 倍になり、これまで使ったすべてのスペルチェッカーはこれを回避する設計になっている。抜け道は、より高速な表を作ることではない。レーベンシュタイン距離が三角不等式を満たすこと、すなわち d(a, c) ≤ d(a, b) + d(b, c) を利用して、インデックスが候補との実際の距離を測定することなく、それが離れすぎていることを証明できる点にある。

参考文献 (4)

例題

  • kitten → sitting - kitten → sitting は教科書どおりの例で、答えは 3。置換2回と挿入1回で、類似度は 57.14%
  • book → back - book → back は4文字どうしで 2 回の操作。類似度は 50.00% と表示される
  • Saturday → Sunday - Saturday → Sunday は 3 回の編集で、類似度は 62.50%。うち 2 回は削除
  • form → from(入れ替え) - form → from はダメラウ–レーベンシュタインでは入れ替え 1 回だけで、ツールは類似度 75.00% と表示する
  • DNA の比較 - GATTACA → GCATGCG には 4 回の操作が必要で 42.86%。同じ尺度を配列データに当てはめた例