web-dev-qa-db-ja.com

レーベンシュタイン距離マッチングを使用したマッチのパーセンタイルランク

レーベンシュタイン距離アルゴリズムを使用して、単一の検索語を一致する可能性のある辞書と照合しようとしています。アルゴリズムは、検索文字列を一致した文字列に変換するために必要な操作の数として表される距離を返します。結果を上位「N」(たとえば10)の一致のランク付けされたパーセンテージリストに表示したいと思います。

検索文字列は個々の辞書文字列よりも長くても短くてもよいため、距離をパーセンテージで表すための適切なロジックは何でしょうか。これは、「パーセンテージとして」の各結果がクエリ文字列にどれだけ近いかを定性的に反映します。 %は完全一致を示します。

私は次のオプションを検討しました。

Q = query string
M = matched string
PM = Percentage Match
Option 1. PMi = (1 - Lev_distance(Q, Mi)/Strlen(Q)) * 100
Option 2. PMi = (1 - Lev_distance(Q, Mi)/max(Strlen(Q), strlen(Mi))) * 100

オプション1は、距離が検索文字列の長さよりも長く、一致文字列が長い場合、負のパーセンテージになる可能性があります。たとえば、「ABCCorp。」と一致するクエリ「ABC」。一致率が負になります。

オプション2は、Miのセット全体で一貫したパーセンテージを提供するようには見えません。これは、各計算で異なる分母が使用される可能性があり、結果のパーセンテージ値が正規化されないためです。

私が考えることができる他の唯一の方法は、lev_distanceの比較をいずれかの文字列長と捨てることですが、代わりに、上位の「N」一致の比較距離を逆パーセンタイルランク(100パーセンタイルランク)として提示します。

何かご意見は?より良いアプローチはありますか?レーベンシュタイン距離はおそらくあいまい一致の最も一般的なアルゴリズムであり、これは非常に一般的な問題であるため、私は何かを見逃しているに違いありません。

26
user1368587

私も同様の問題を抱えていましたが、このスレッドは解決策を見つけるのに役立ちました。それが他の人にも役立つことを願っています。

int levDis = Lev_distance(Q, Mi)
int bigger = max(strlen(Q), strlen(Mi))
double pct = (bigger - levDis) / bigger

両方の文字列が完全に同じである場合は100%を返し、完全に異なる場合は0%を返す必要があります。

(私の英語があまり上手ではない場合は申し訳ありません)

32

この問題に対する私のアプローチは、最大許容操作を計算することでした。これは、レーベンシュタイン距離です。私が使用した式は次のとおりです。

percent = 0.75; // at least 75% of string must match
maxOperationsFirst = s1.length() - s1.length() * percent;
maxOperationsSecond = s2.length() - s2.length() * percent;
maxOperations = round(min(maxOperationsFirst, maxOperationsSecond));

文字列ごとに最大演算を計算しているので、わかりやすいと思います。両方の結果の最小値を使用して、最も近い整数に丸めます。この部分をスキップして、いずれかの文字列からのmax操作の値だけを使用できます。これは、実際にはデータによって異なります。

最大操作数を取得したら、それをレーベンシュタイン距離の結果と比較して、文字列が受け入れ可能かどうかを判断できます。このようにして、任意の拡張レーベンシュタイン法を使用できます。たとえば、 ダメラウ・レーベンシュタイン距離 、スペルミスをカウントします例: test-> tset、1回の操作としてのみ。これは、これらのスペルミスが頻繁に発生するユーザー入力をチェックするときに非常に役立ちます。

これが、この問題を解決する方法についてのアイデアを得るのに役立つことを願っています。

5
Marko Grešak

これは基本的に私の質問で述べたオプション2です。ただし、そのアプローチの問題を示しましょう。

Q = "ABC Corp" (len = 8)
M1 = "ABC"
M2 = "ABC Corporati"
M3 = "ABC Corp"

Lev距離が同じになるようにM1とM2を選択しました(それぞれ5つ)。オプション2を使用すると、一致率は次のようになります。

M1 = (1 - 5/8)*100  = 37.5%
M2 = (1 - 5/13)*100 = 61.5%
M3 = 100%

マッチをこの順序で提示するとわかるように、M1とM2は、まったく同じlev距離を持っていても、ランクに大きな違いがあります。問題がわかりますか?

0
NG Algo

これはどうですか:

100 - ( ((2*Lev_distance(Q, Mi)) / (Q.length + Mi.length)) * 100 )

(Q, M1)(Q,M2)で同じ距離になります

0
Wakan Tanka
(1 - (levNum / Math.max(s.length,t.length) ) ) *100

正しいはずです

0
cocoa coder

レーベンシュタイン距離の最大数は[l1, l2].max。本当だと思います。しかし、それで割るべきではありません。

gem install levenshtein diff-lcs

Diff::LCS.lcs "abc", "qwer"
=> []
Levenshtein.distance("abc", "qwer").to_f / [3, 4].max
=> 1.0

Diff::LCS.lcs "abc", "cdef"
=> ["c"]
Levenshtein.distance("abc", "cdef").to_f / [3, 4].max
=> 1.0

Diff::LCS.lcs "1234", "34567890"
=> ["3", "4"]
Levenshtein.distance("1234", "34567890").to_f / [4, 8].max
=> 1.0

レーベンシュタインは、percentsの文字列を比較するための信頼できる方法のようには見えません。同様の文字列を100%異なるとして扱いたくありません。

各シーケンスとLCSの違いを分析することをお勧めします。

def get_similarity(sequence_1, sequence_2)
  lcs_length = Diff::LCS::Internals.lcs(sequence_1, sequence_2).compact.length
  lcs_length.to_f * 2 / (sequence_1.length + sequence_2.length)
end
0
puchu