18

プログラムでは、次の形式のクエリに効率的に答える必要があります。

文字列のセットとAクエリ文字列を指定すると、q がのサブシーケンスであるようなqすべてが返されます。s ∈ As

たとえば、givenA = {"abcdef", "aaaaaa", "ddca"}とexactが返されq = "acd"ます。"abcdef"


これまでに検討した内容は以下のとおりです。

  1. 可能な文字ごとに、それが表示されるすべての文字列/場所のソート済みリストを作成します。クエリを実行するには、関連する文字のリストをインターリーブし、それをスキャンして、文字列境界内で一致するものを探します。

    限られた数の異なる文字によって返されるリストが非常に密になるため、これはおそらく文字ではなく単語の場合により効率的です。

  2. n 接頭辞qが持つ可能性のあるそれぞれについて、一致するすべての文字列のリストを保存します。n現実的には 3 に近いかもしれません。それよりも長いクエリ文字列の場合、最初のリストをブルート フォースします。

    これは少しスピードアップするかもしれませんが、いくつかの n サブシーケンスが のすべての文字列の近くに存在することは容易に想像できますA。つまり、最悪の場合は、セット全体を総当たり攻撃するのと同じです。


上記のタスクを大規模な s に対して効率的に実行するのに役立つ可能性のあるデータ構造、アルゴリズム、または前処理のトリックを知っていますAか? (私sの場合は100文字前後になります)


更新:一部の人々は、LCS を使用してqが のサブシーケンスであるかどうかを確認することを提案していsます。これは、次のような単純な関数を使用して実行できることを思い出してください。

def isSub(q,s):
  i, j = 0, 0
  while i != len(q) and j != len(s):
    if q[i] == s[j]:
      i += 1
      j += 1
    else:
      j += 1
  return i == len(q)

更新 2:qの性質とAその要素について詳しく説明するよう求められました。できるだけ一般的に機能するものを好みますが、A長さは約 10^6 で、挿入をサポートする必要があると思います。要素sは短くなり、平均の長さは 64 になります。クエリqは 1 ~ 20 文字のみで、ライブ検索に使用されるため、クエリ「ab」はクエリ「abc」の直前に送信されます。繰り返しますが、上記のソリューションをできるだけ使用しないことをお勧めします。

更新 3:ルックアップを使用したデータ構造をO(n^{1-epsilon})使用すると、OVP を解決したり、SETH の推測を反証したりすることができるようになると思いました。それがおそらく私たちの苦しみの理由です。唯一のオプションは、推測を反証するか、近似を使用するか、データセットを利用することです。クワドレットと試行は、さまざまな設定で最後に実行されると思います。

4

6 に答える 6

7

テスト

このスレッドには 4 つの主な提案がありました。

  1. Shivam Kalra は、 のすべての文字列に基づいてオートマトンを作成することを提案しましたA。このアプローチは文献でわずかに試みられており、通常は "Directed Acyclic Subsequence Graph" (DASG) という名前で行われています。

  2. J Random Hacker は、クエリ文字列内のすべての 'n choose 3' トリプレットに私の 'プレフィックス リスト' のアイデアを拡張し、それらすべてをヒープを使用してマージすることを提案しました。

  3. メモ「データベースでの効率的なサブシーケンス検索」で、Rohit Jain、Mukesh K. Mohania、および Sunil Prabhakar は、いくつかの最適化を伴うトライ構造を使用し、ツリーを再帰的に検索してクエリを検索することを提案しています。彼らはまた、トリプレットのアイデアに似た提案をしています。

  4. 最後に、 の各要素のインデックスを格納することで最適化することを wanghq が提案した「素朴な」アプローチがありAます。

何に継続的に取り組む価値があるかをよりよく理解するために、上記の 4 つのアプローチを Python で実装し、2 つのデータ セットでそれらのベンチマークを行いました。実装はすべて、C または Java で適切に実装されていれば、数倍速くなる可能性があります。「trie」および「naive」バージョンで提案された最適化は含めていません。

テスト 1

A私のファイルシステムからのランダムなパスで構成されています。平均長 7 のq100 個のランダムな文字列です。アルファベットが大きい (そして Python が遅い) ため、方法 3 でのみデュプレットを使用できました。[a-z]

Aサイズ の関数としての秒単位の構築時間:施工時間

Aサイズ の関数としての秒単位のクエリ時間:クエリ時間

テスト 2

A[a-b]長さ 20のランダムにサンプリングされた文字列で構成されます。平均長 7 のq100 個のランダムな文字列です。アルファベットが小さいため、方法 3 にクワドレットを使用できます。[a-b]

Aサイズ の関数としての秒単位の構築時間:ここに画像の説明を入力

Aサイズ の関数としての秒単位のクエリ時間:ここに画像の説明を入力

結論

両対数プロットは少し読みにくいですが、データから次の結論を引き出すことができます。

  • オートマトンのクエリは非常に高速ですが (一定時間)、作成して保存することはできません|A| >= 256。より詳細な分析により、時間とメモリのバランスが改善されるか、残りの方法に適用できるいくつかのトリックが得られる可能性があります。

  • dup-/trip-/quadlet メソッドは、私の trie 実装の約 2 倍、「単純な」実装の 4 倍高速です。n^3j_random_hacker によって提案されたのではなく、マージに一定量のリストのみを使用しました。メソッドをより適切に調整することは可能かもしれませんが、一般的には期待外れでした。

  • 私のトライ実装は一貫して単純なアプローチよりも約 2 倍優れています。より多くの前処理 (「このサブツリーの次の 'c' はどこにあるのか」など) を組み込むか、おそらくトリプレット メソッドとマージすることで、これが今日の勝者のように思えます。

  • パフォーマンスが大幅に低下する場合、単純な方法は非常に少ないコストで比較的うまく機能します。

于 2013-08-12T14:55:04.533 に答える
3

ご指摘のとおり、A のすべての文字列にサブシーケンスとして q が含まれている可能性があります。その場合、O(|A|) よりもうまくいくとは思えません。(そうは言っても、A の各文字列 i に対して (q, A[i]) でLCSを実行するのにかかった時間よりも良い結果が得られるかもしれませんが、ここではそれについては触れません。)

TTBOMK この質問に答える魔法のような迅速な方法はありません (接尾辞ツリーが魔法のように、 subsequences の代わりに部分文字列を含む対応する質問に答える魔法の迅速な方法です)。それにもかかわらず、ほとんどのクエリに対する回答のセットが平均して小さいと予想される場合は、これらのクエリを高速化する方法を検討する価値があります (小さなサイズの回答を生成するもの)。

ヒューリスティック (2) の一般化に基づいてフィルタリングすることをお勧めします。データベース シーケンス A[i] にサブシーケンスとして q が含まれている場合、q のすべてのサブシーケンスも含まれている必要があります。(残念ながら逆方向は当てはまりません!)したがって、いくつかの小さなk、たとえばあなたが提案する3の場合、長さkの文字列sごとに、sを含むデータベースシーケンスのリストを示すリストの配列を作成することで前処理できますサブシーケンス。つまり、c[s] には、サブシーケンスとして s を含むデータベース シーケンスの ID 番号のリストが含まれます。各リストを番号順に並べて、後で高速交差点を有効にします。

各クエリ q の基本的な考え方 (すぐに改善します) は次のとおりです。サブシーケンスとして q を含む可能性がある A のシーケンス。次に、この (できれば小さい) 交差の可能なシーケンス A[i] ごとに、q を使用して O(n^2) LCS 計算を実行し、実際に q が含まれているかどうかを確認します。

いくつかの観察:

  1. サイズが m と n の 2 つの並べ替えられたリストの交差は、O(m+n) 時間で見つけることができます。r リストの共通部分を見つけるには、任意の順序で r-1 回のペアごとの共通部分を実行します。交点を取っても小さいか同じサイズのセットしか生成できないため、最初にリストの最小のペアを交差させ、次に次に小さいリストのペアを交差させることで時間を節約できます (これには必ず最初の操作の結果が含まれます)。 . 特に、リストをサイズの大きい順にソートし、常に次のリストと「現在の」交差と交差します。
    • 実際には、各 r リストの最初の要素 (シーケンス番号) をヒープ データ構造に追加し、最小値を繰り返し引き出して、次の値でヒープを補充する別の方法で交差を見つける方が高速です。最新の最小値が由来するリスト。これにより、シーケンス番号のリストが減少しない順序で生成されます。すべての r セットのメンバーになることはできないため、連続して r 回未満しか表示されない値は破棄できます。
  2. k 文字列 s が c[s] に少数のシーケンスしかない場合、ある意味で を識別します。ほとんどのデータセットでは、すべての k 文字列が同じように識別できるわけではありません。これを有利に使用できます。前処理の後、次の 3 つの理由から、一定数 (または全体の一定の割合) を超えるシーケンスを持つすべてのリストを破棄することを検討してください。
    • 彼らは保管するのに多くのスペースを取ります
    • クエリ処理中に交差するのに多くの時間がかかります
    • それらを交差させても、通常、交差全体があまり縮小されません
  3. q のすべての k サブシーケンスを考慮する必要はありません。これにより最小の交差が生成されますが、リストのマージ (|q| k を選択) が必要であり、これらの k サブシーケンスのほんの一部を使用して、ほぼ同じくらい小さい交差を生成することも可能です。たとえば、q のすべて (またはいくつか) の k 部分文字列を試すことに制限することができます。さらなるフィルターとして、c[s] のシーケンス リストがある値を下回る k サブシーケンスのみを検討します。(注: しきい値がすべてのクエリで同じである場合は、そのようなリストをすべてデータベースから削除することもできます。これは、同じ効果があり、スペースを節約できるためです。)
于 2013-08-05T14:53:40.267 に答える
2

1つの考え;
q が短くなる傾向がある場合は、A と q をセットに減らすとよいでしょうか?
したがって、この例では、{ (a,b,c,d,e,f), (a), (a,c,d) } に派生します。任意の q の候補を検索することは、元の問題よりも高速であるはずです (これは実際には推測であり、どの程度正確かはわかりません。おそらくそれらを並べ替えて、類似のものをブルーム フィルターで「グループ化」しますか?)、ブルートフォースを使用して誤検知を取り除きます。
A 文字列が長い場合、出現に基づいて文字を一意にすることができるため、{(a1,b1,c1,d1,e1,f1),(a1,a2,a3,a4,a5,a6), (a1、c1、d1、d2)}。「ddca」を検索する場合、2 番目の d と 2 番目の d のみを一致させたいため、これで問題ありません。アルファベットのサイズは大きくなり (ブルームまたはビットマップ スタイルの操作には適していません)、新しい A を取得するたびに異なりますが、誤検知の量は減少します。

于 2013-08-09T22:15:11.610 に答える