1

管理者がそれらをクリーンアップできるようにするために、一連のフィールドでほぼ重複する値を見つけようとしています。

私が一致している2つの基準があります

  1. 一方のストリングはもう一方のストリングに完全に含まれており、その長さの少なくとも 1/4 です。
  2. 文字列の編集距離は、2 つの文字列の合計の長さの 5% 未満です。

疑似 PHP コード:

foreach($values as $value){
$matches = array();
foreach($values as $match){
  if(
    (
      $value['length'] < $match['length']
      &&
      $value['length'] * 4 > $match['length']
      &&
      stripos($match['value'], $value['value']) !== false
    )
    ||
    (
      $match['length'] < $value['length']
      &&
      $match['length'] * 4 > $value['length']
      &&
      stripos($value['value'], $match['value']) !== false
    )
    ||
    (
      abs($value['length'] - $match['length']) * 20 < ($value['length'] + $match['length'])
      &&
      0 < ($match['changes'] = levenshtein($value['value'], $match['value']))
      &&
      $match['changes'] * 20 <= ($value['length'] + $match['length'])
      )
    ){
      $matches[] = &$match;
    }
}
// output matches for current outer loop value
}

可能な限り、比較的高価なstriposおよびlevenshtein関数の呼び出しを減らすようにしました。これにより、実行時間がかなり短縮されました。ただし、O(n^2) 演算として、これはより大きな値のセットに対応できず、配列を単純に反復処理するだけでかなりの量の処理時間が費やされているようです。

操作されている値のいくつかのセットのいくつかのプロパティ

合計 | ストリングス | 文字列あたりの一致数 | | |
ストリングス | マッチで | 平均 | 中央値 | マックス | 時間 |
------+--------------+---------+--------+------+ ----------+
    844 | 413 | 1.8 | 1 | 58 | 140 |
    593 | 156 | 1.2 | 1 | 5 | 62 |
    272 | 168 | 3.2 | 2 | 26 | 10 |
    157 | 47 | 1.5 | 1 | 4 | 3.2 |
    106 | 48 | 1.8 | 1 | 8 | 1.3 |
     62 | 47 | 2.9 | 2 | 16 | 0.4 |

基準をチェックする時間を短縮するために他にできることはありますか。さらに重要なことに、必要な基準チェックの数を減らす方法はありますか (たとえば、入力値を前処理することによって)。選択率が低い?


編集:実装されたソリューション

// $values is ordered from shortest to longest string length
$values_count = count($values); // saves a ton of time, especially on linux
for($vid = 0; $vid < $values_count; $vid++){
for($mid = $vid+1; $mid < $values_count; $mid++){ // only check against longer strings
  if(
    (
      $value['length'] * 4 > $match['length']
      &&
      stripos($match['value'], $value['value']) !== false
    )
    ||
    (
      ($match['length'] - $value['length']) * 20 < ($value['length'] + $match['length'])
      &&
      0 < ($changes = levenshtein($value['value'], $match['value']))
      &&
      $changes * 20 <= ($value['length'] + $match['length'])
      )
    ){
      // store match in both directions
      $matches[$vid][$mid] = true;
      $matches[$mid][$vid] = true;
    }

}
}
// Sort outer array of matches alphabetically with uksort()
foreach($matches as $vid => $mids){
  // sort inner array of matches by usage count with uksort()
  // output matches
}
4

2 に答える 2

0

最初に文字列を長さ ( O(N) ) で並べ替えてから、小さい文字列のみを部分文字列または大きい文字列としてチェックし、さらに差が大きすぎない文字列ペアのレーベンシュタインのみをチェックできます。

これらのチェックは既に実行していますが、今回はすべての N x N ペアに対して実行しますが、最初に長さで事前選択すると、最初にチェックするペアを減らすのに役立ちます。失敗するテストしか含まれていない場合でも、N x N ループは避けてください。

部分文字列の一致については、すべての小さなアイテムのインデックスを作成することでさらに改善でき、大きなアイテムを解析するときにそれに応じてこれを更新できます。インデックスは、各単語 (文字列) がルートからリーフへのパスを形成する、文字で分岐するツリー構造を形成できる必要があります。このようにして、インデックス内の単語のいずれかが一致する文字列と比較されているかどうかを確認できます。一致文字列の各文字について、ツリー インデックス内の任意のポインターを処理し、インデックスに新しいポインターを作成してみてください。ポインターがインデックス内の次の文字に進むことができない場合は、それを削除します。いずれかのポインターがリーフ ノートに到達した場合、部分文字列の一致が見つかりました。これを実装することは難しくはないと思いますが、簡単でもありません。

于 2010-05-20T23:03:41.620 に答える
0

内側のループを締めることで、すぐに 100% 改善できます。結果に重複した一致が表示されていませんか?

前処理のステップとして、文字の頻度を調べて計算します (文字のセットが a-z0-9 のように小さいと仮定します。これは、stripos を使用していることを考えると、可能性が高いと思います)。次に、シーケンスを比較する(高価な)のではなく、頻度を比較します(安価です)。これにより、誤検知が発生する可能性があり、それを受け入れるか、現在取り除かなければならないテストにプラグインすることができます.

于 2010-05-20T23:27:31.500 に答える