4

ゼロと1の線形リストがあり、複数の単純なパターンを照合して最初の出現を見つける必要があります。たとえば、長さ800万のリスト内000110110101010100100、、、またはを検索する必要がある場合があります。10100100010どちらかが最初に出現したものを見つけて、それが発生したインデックスを返すだけです。ただし、大きなリストに対してループとアクセスを実行するとコストがかかる可能性があるため、何度も実行したくありません。

行うよりも速い方法はありますか

foreach (patterns) {
    for (i=0; i < listLength; i++)
        for(t=0; t < patternlength; t++)
            if( list[i+t] != pattern[t] ) {
                 break;
            }
            if( t == patternlength - 1 ) {
                 return i;  // pattern found!
            }
        }
    }
}

編集:ところで、私は上記の擬似コードに従ってこのプログラムを実装しました、そしてパフォーマンスはOKですが、素晴らしいものは何もありません。プロセッサのシングルコアで1秒間に約600万ビットを処理すると推定しています。私はこれを画像処理に使用していますが、数千の8メガピクセルの画像を処理する必要があるため、少しでも役立ちます。

編集:明確でない場合は、ビット配列を使用しているため、1と0の2つの可能性しかありません。そしてそれはC++です。

編集: BMおよびKMPアルゴリズムへのポインターをありがとう。BMのウィキペディアのページに、

アルゴリズムは、検索対象の文字列(キー)を前処理しますが、検索対象の文字列は前処理しません(検索対象の文字列を前処理し、繰り返し検索することで前処理の費用を償却できる一部のアルゴリズムとは異なります)。

それは面白そうに見えますが、そのようなアルゴリズムの例は示されていません。そのようなものも役立ちますか?

4

7 に答える 7

11

グーグルの鍵は、「マルチパターン」の文字列照合です。

1975年に、AhoとCorasickは(線形時間)アルゴリズムを公開しました。これは、の元のバージョンで使用されていましたfgrep。その後、アルゴリズムは多くの研究者によって洗練されました。たとえば、Commentz-Walter(1979)は、Aho&CorasickとBoyer&Mooreのマッチングを組み合わせたものです。Baeza-Yates(1989)は、ACとBoyer-Moore-Horspoolバリアントを組み合わせました。Wu and Manber(1994)も同様の作業を行いました。

マルチパターンマッチングアルゴリズムのACラインに代わるものは、ラビンとカープのアルゴリズムです。

エイホ-コラシックとラビン-カープのウィキペディアのページを読むことから始めて、それがあなたの場合に意味があるかどうかを判断することをお勧めします。もしそうなら、多分あなたの言語/ランタイムのための実装がすでに利用可能です。

于 2009-08-09T02:57:47.707 に答える
4

はい。

ボイヤームーア文字列検索アルゴリズム

参照:クヌース-モリス-プラットアルゴリズム

于 2009-08-09T02:23:34.597 に答える
2

SuffixArrayを作成して、ランタイムがおかしいと検索することができます:O(length(pattern))。しかし..あなたはその配列を構築しなければなりません。テキストが静的でパターンが動的である場合にのみ価値があります。

于 2009-08-09T02:24:44.377 に答える
1

効率的なソリューション:

  1. パターンをtrieデータ構造に格納します
  2. リストの検索を開始します
  3. 次のpattern_length文字がトライにあるかどうかを確認し、成功したら停止します(O(1)操作)
  4. 1文字ステップして#3を繰り返します

リストが変更可能でない場合は、一致するパターンのオフセットを保存して、次回の計算の繰り返しを回避できます。

于 2009-08-09T03:24:03.703 に答える
0

文字列を柔軟にする必要がある場合は、Mitch Wheatに従って、「ボイヤー-ムーア文字列検索アルゴリズム」を変更することもお勧めします。文字列を柔軟にする必要がない場合は、パターンマッチングをさらに折りたたむことができるはずです。ボイヤームーアのモデルは、照合する複数の文字列の1つを探すために、大量のデータを検索するのに非常に効率的です。

ジェイコブ

于 2009-08-09T02:43:15.737 に答える
0

0と1が交互になっている場合は、テキストをランとしてエンコードします。n 0の実行は-nであり、n1の実行はnです。次に、検索文字列をエンコードします。次に、エンコードされた文字列を使用する検索関数を作成します。

コードは次のようになります。

try:
    import psyco
    psyco.full()
except ImportError:
    pass

def encode(s):
    def calc_count(count, c):
        return count * (-1 if c == '0' else 1)
    result = []
    c = s[0]
    count = 1
    for i in range(1, len(s)):
        d = s[i]
        if d == c:
            count += 1
        else:
            result.append(calc_count(count, c))
            count = 1
            c = d
    result.append(calc_count(count, c))
    return result

def search(encoded_source, targets):
    def match(encoded_source, t, max_search_len, len_source):
        x = len(t)-1
        # Get the indexes of the longest segments and search them first
        most_restrictive = [bb[0] for bb in sorted(((i, abs(t[i])) for i in range(1,x)), key=lambda x: x[1], reverse=True)]
        # Align the signs of the source and target
        index = (0 if encoded_source[0] * t[0] > 0 else 1)
        unencoded_pos = sum(abs(c) for c in encoded_source[:index])
        start_t, end_t = abs(t[0]), abs(t[x])
        for i in range(index, len(encoded_source)-x, 2):
            if all(t[j] == encoded_source[j+i] for j in most_restrictive):
                encoded_start, encoded_end = abs(encoded_source[i]), abs(encoded_source[i+x])
                if start_t <= encoded_start and end_t <= encoded_end:
                    return unencoded_pos + (abs(encoded_source[i]) - start_t)
            unencoded_pos += abs(encoded_source[i]) + abs(encoded_source[i+1])
            if unencoded_pos > max_search_len:
                return len_source
        return len_source
    len_source = sum(abs(c) for c in encoded_source)
    i, found, target_index = len_source, None, -1
    for j, t in enumerate(targets):
        x = match(encoded_source, t, i, len_source)
        print "Match at: ", x
        if x < i:
            i, found, target_index = x, t, j
    return (i, found, target_index)


if __name__ == "__main__":
    import datetime
    def make_source_text(len):
        from random import randint
        item_len = 8
        item_count = 2**item_len
        table = ["".join("1" if (j & (1 << i)) else "0" for i in reversed(range(item_len))) for j in range(item_count)]
        return "".join(table[randint(0,item_count-1)] for _ in range(len//item_len))
    targets = ['0001101101'*2, '01010100100'*2, '10100100010'*2]
    encoded_targets = [encode(t) for t in targets]
    data_len = 10*1000*1000
    s = datetime.datetime.now()
    source_text = make_source_text(data_len)
    e = datetime.datetime.now()
    print "Make source text(length %d): " % data_len,  (e - s)
    s = datetime.datetime.now()
    encoded_source = encode(source_text)
    e = datetime.datetime.now()
    print "Encode source text: ", (e - s)

    s = datetime.datetime.now()
    (i, found, target_index) = search(encoded_source, encoded_targets)
    print (i, found, target_index)
    print "Target was: ", targets[target_index]
    print "Source matched here: ", source_text[i:i+len(targets[target_index])]
    e = datetime.datetime.now()
    print "Search time: ", (e - s)

提供した文字列の2倍の長さの文字列では、1,000万文字で3つのターゲットの最も早い一致を見つけるのに約7秒かかります。もちろん、私はランダムなテキストを使用しているので、実行ごとに少し異なります。

psycoは、実行時にコードを最適化するためのPythonモジュールです。これを使用すると、優れたパフォーマンスが得られ、C /C++パフォーマンスの上限としてそれを見積もることができます。最近のパフォーマンスは次のとおりです。

Make source text(length 10000000):  0:00:02.277000
Encode source text:  0:00:00.329000
Match at:  2517905
Match at:  494990
Match at:  450986
(450986, [1, -1, 1, -2, 1, -3, 1, -1, 1, -1, 1, -2, 1, -3, 1, -1], 2)
Target was:  1010010001010100100010
Source matched here:  1010010001010100100010
Search time:  0:00:04.325000

1000万文字をエンコードするのに約300ミリ秒かかり、それに対して3つのエンコードされた文字列を検索するのに約4秒かかります。C /C++ではエンコード時間が長くなるとは思いません。

于 2009-08-09T02:59:14.227 に答える
0

ビット配列の場合、ローリングサムを実行すると改善されると思います。パターンが長さの場合n、最初のnビットを合計し、パターンの合計と一致するかどうかを確認します。合計の最初のビットを常に格納します。次に、次のビットごとに、合計から最初のビットを減算して次のビットを加算し、合計がパターンの合計と一致するかどうかを確認します。これにより、パターン上の線形ループが保存されます。

BMアルゴリズムは、見た目ほど素晴らしいものではないようです。ここでは、ゼロと1の2つの可能な値しかないため、最初のテーブルはあまり役に立ちません。2番目の表が役立つかもしれませんが、それはBMHがほとんど価値がないことを意味します。

編集:睡眠不足の状態ではBMを理解できなかったので、このローリングサムを実装しただけで(本当に簡単でした)、検索が3倍速くなりました。「ローリングハッシュ」について言及してくれた人に感謝します。以前の17.3秒と比較して、5.45秒(およびシングルスレッド)で2つの30ビットパターンを321,750,000ビットで検索できるようになりました。

于 2009-08-09T03:49:06.273 に答える