Jaro-Winklerアルゴリズムの最適化
-
27-09-2019 - |
質問
Jaro-Winklerアルゴリズムのこのコードが撮影されています これ Webサイト。差異間の距離をとるには、150,000回走る必要があります。 Androidモバイルデバイスで実行するにつれて、長い時間がかかります。
もっと最適化できますか?
public class Jaro {
/**
* gets the similarity of the two strings using Jaro distance.
*
* @param string1 the first input string
* @param string2 the second input string
* @return a value between 0-1 of the similarity
*/
public float getSimilarity(final String string1, final String string2) {
//get half the length of the string rounded up - (this is the distance used for acceptable transpositions)
final int halflen = ((Math.min(string1.length(), string2.length())) / 2) + ((Math.min(string1.length(), string2.length())) % 2);
//get common characters
final StringBuffer common1 = getCommonCharacters(string1, string2, halflen);
final StringBuffer common2 = getCommonCharacters(string2, string1, halflen);
//check for zero in common
if (common1.length() == 0 || common2.length() == 0) {
return 0.0f;
}
//check for same length common strings returning 0.0f is not the same
if (common1.length() != common2.length()) {
return 0.0f;
}
//get the number of transpositions
int transpositions = 0;
int n=common1.length();
for (int i = 0; i < n; i++) {
if (common1.charAt(i) != common2.charAt(i))
transpositions++;
}
transpositions /= 2.0f;
//calculate jaro metric
return (common1.length() / ((float) string1.length()) +
common2.length() / ((float) string2.length()) +
(common1.length() - transpositions) / ((float) common1.length())) / 3.0f;
}
/**
* returns a string buffer of characters from string1 within string2 if they are of a given
* distance seperation from the position in string1.
*
* @param string1
* @param string2
* @param distanceSep
* @return a string buffer of characters from string1 within string2 if they are of a given
* distance seperation from the position in string1
*/
private static StringBuffer getCommonCharacters(final String string1, final String string2, final int distanceSep) {
//create a return buffer of characters
final StringBuffer returnCommons = new StringBuffer();
//create a copy of string2 for processing
final StringBuffer copy = new StringBuffer(string2);
//iterate over string1
int n=string1.length();
int m=string2.length();
for (int i = 0; i < n; i++) {
final char ch = string1.charAt(i);
//set boolean for quick loop exit if found
boolean foundIt = false;
//compare char with range of characters to either side
for (int j = Math.max(0, i - distanceSep); !foundIt && j < Math.min(i + distanceSep, m - 1); j++) {
//check if found
if (copy.charAt(j) == ch) {
foundIt = true;
//append character found
returnCommons.append(ch);
//alter copied string2 for processing
copy.setCharAt(j, (char)0);
}
}
}
return returnCommons;
}
}
私は全体のプロセスで、スクリプトのインスタンスだけを作成しているので、一度だけに言及しています
jaro= new Jaro();
あなたがテストして例を必要としているので、スクリプトを壊さない場合、あなたはそれを見つけるでしょう ここ, 、Python最適化のための別のスレッドで
解決
はい、でもあなたはそれを楽しむつもりはありません。それらすべてを交換してください new
コンストラクターに割り当てられ、二度と決して整理されないチャーアレイを備えたEd StringBuffers。
この保留中のCommons-Langパッチ フレーバーの一部を提供します。
他のヒント
この質問はおそらくしばらくの間解決されていることを知っていますが、アルゴリズム自体についてコメントしたいと思います。文字列をそれ自体と比較すると、答えは1/|文字列であることが判明しました|オフ。わずかに異なる値を比較すると、値も低くなります。
これに対する解決策は、GetCommonCharactersメソッド内の内側のforステートメントで「M-1」を「M」に調整することです。コードは魅力のように機能します:)
見る http://en.wikipedia.org/wiki/jaro%E2%80%93Winkler_Distance いくつかの例についても。
- GetCommonCharactersループの2つのネストされたループを避けてください。
方法についての提案:すべてのcharを小さな文字列に何らかのマップに保存します(Javaにはいくつかあります)。キーは文字であり、値は位置です。共通しています。私はアルゴリズムをよく理解していませんが、これは実行可能だと思います。 - それとBmarguliesの答えを除いて、私はビットなどのようなものを超えてさらに最適化されていません。これが本当に重要な場合は、この部分をCで書き換えることを検討してください。
Androidとそれがデータベースでどのように機能するかについてはあまり知りません。 WP7には(:))sql ceがあります。次のステップは、通常、データを操作することです。文字列の長さを追加し、比較を制限します。両方の列にインデックスを追加し、長さで並べ替え、次に値で並べ替えます。長さのインデックスもソートする必要があります。 150 000の医療用語を備えた古いサーバーで実行してもらい、0.5秒以内に提案とスペルチェックを行いました。特に別のスレッドで実行されている場合、ユーザーはほとんど気付かないことができます。
必要があるので、私はそれについて長い間(2年:)など)ブログを書くつもりでした。しかし、私はついにそれについていくつかの言葉を書き、いくつかのヒントを提供することができました。ここでチェックしてください:
Microsoftプラットフォーム向けですが、依然として一般的な原則は同じです。
はい、これははるかに速くすることができます。一つには、ストリングバッファーはまったく必要ありません。別の場合、転置をカウントするために別のループは必要ありません。
発見できる ここでの私の実装, 、そしてそれははるかに速いはずです。 Apache 2.0ライセンスの下にあります。
getCommonCharactersメソッドを使用して一般的な文字を返す代わりに、ここでCバージョンと同様に一致を保持するためにいくつかの配列を使用してください https://github.com/miguelvps/c/blob/master/jarowinkler.c
/*Calculate matching characters*/
for (i = 0; i < al; i++) {
for (j = max(i - range, 0), l = min(i + range + 1, sl); j < l; j++) {
if (a[i] == s[j] && !sflags[j]) {
sflags[j] = 1;
aflags[i] = 1;
m++;
break;
}
}
}
別の最適化は、各文字列のビットマスクを事前に計算することです。それを使用して、最初の文字列の現在の文字が2番目の文字列に存在するかどうかを確認します。これは、効率的なビットワイズ操作を使用して実行できます。
これにより、Max/minの計算と行方不明の文字のループがスキップされます。