効率よく見つのバイナリ文字列の低いハミング距離に大きなセット
-
29-10-2019 - |
質問
問題点:
最大(~100万リストの符号なし32ビット整数を表す符号なし32ビット整数の入力値、最大 ハミング距離, 戻り、すべてのリストのメンバー内の指定されたハミング距離を入力値とします。
実際のデータ構造の一覧表は、要求性能に挙をメモリ内の溶液に、コスト構築、データ構造を二の次とし、低コストへクエリのデータ構造は非常に重要です。
例:
For a maximum Hamming Distance of 1 (values typically will be quite small)
And input:
00001000100000000000000001111101
The values:
01001000100000000000000001111101
00001000100000000010000001111101
should match because there is only 1 position in which the bits are different.
11001000100000000010000001111101
should not match because 3 bit positions are different.
私の思いか
の縮退の場合ハミング距離は0、利用のソート、またはソートされたリストなバイナリ検索のための特定の入力値とします。
の場合ハミング距離だけでは最初の1、フリップの各ビットの入力を繰り返し、上記の32ます。
したいので効率的なスキャン全体のリストを発見しリストのメンバーとハミング距離>1.
解決
質問: ハミング距離d(x、y)について何を知っていますか?
答え:
- それは非陰性です:d(x、y)≥0
- 同一の入力の場合はゼロのみです:d(x、y)=0⇔x= y
- それは対称です:d(x、y)= d(y、x)
- それは従います 三角形の不等式, 、d(x、z)≤d(x、y) + d(y、z)
質問: なぜ私たちは気にするのですか?
答え: それは、ハミング距離がaであることを意味するからです メトリック のために メトリック空間. 。メトリックスペースをインデックス作成するためのアルゴリズムがあります。
また、一般的に「空間インデックス」のアルゴリズムを調べることができます。あなたのスペースはユークリッドではなく、それが は メトリック空間。この主題に関する多くの本は、ハミング距離などのメトリックを使用した文字列のインデックス作成をカバーしています。
脚注: 固定幅の文字列のハミング距離を比較している場合、アセンブリまたはプロセッサの内在性を使用して、大幅なパフォーマンス改善を得ることができる場合があります。たとえば、GCC(マニュアル) これをして:
static inline int distance(unsigned x, unsigned y)
{
return __builtin_popcount(x^y);
}
その後、GCCにSSE4Aを使用してコンピューターをコンパイルしていることを通知すれば、それはいくつかのオペコードに削減されるべきだと思います。
編集: 多くの情報源によると、これは通常のマスク/シフト/追加コードよりも遅くなることがあります。ベンチマークは、私のシステムでは、cバージョンのgccのアウトパフォーマンスがあることを示しています __builtin_popcount
約160%。
補遺: 私は自分自身の問題に興味があったので、線形検索、BKツリー、VPツリーの3つの実装をプロファイルしました。 VPおよびBKツリーは非常に似ていることに注意してください。 BKツリーのノードの子供は、それぞれが木の中心から固定された距離にあるポイントを含む木の「貝殻」です。 VPツリーのノードには2人の子供がいます。1人はノードの中心を中心とした球体内のすべてのポイントを含み、もう1人は外のすべてのポイントを含む子供が含まれています。したがって、VPノードは、多くのより細かいものではなく、2つの非常に厚い「シェル」を備えたBKノードと考えることができます。
結果は私の3.2 GHz PCでキャプチャされ、アルゴリズムは複数のコアを利用しようとはしません(簡単なはずです)。 100mの擬似ランダム整数のデータベースサイズを選択しました。結果は、距離1..5の平均1000クエリ、6..10の100クエリと線形検索です。
- データベース:100mの擬似ランダム整数
- テスト数:距離1..5、100、100、距離6..10および線形
- 結果:クエリヒットの平均#(非常に近似)
- 速度:1秒あたりのクエリ数
- カバレッジ:クエリごとに調べたデータベースの平均割合
-- BK Tree -- -- VP Tree -- -- Linear -- Dist Results Speed Cov Speed Cov Speed Cov 1 0.90 3800 0.048% 4200 0.048% 2 11 300 0.68% 330 0.65% 3 130 56 3.8% 63 3.4% 4 970 18 12% 22 10% 5 5700 8.5 26% 10 22% 6 2.6e4 5.2 42% 6.0 37% 7 1.1e5 3.7 60% 4.1 54% 8 3.5e5 3.0 74% 3.2 70% 9 1.0e6 2.6 85% 2.7 82% 10 2.5e6 2.3 91% 2.4 90% any 2.2 100%
あなたのコメントで、あなたは言った:
さまざまなルートノードを持つBKツリーの束を生成し、それらを広げることで、BKツリーを改善できると思います。
これがまさにVPツリーがBKツリーよりも(わずかに)優れている理由だと思います。 「浅い」ではなく「深い」ため、より少ないポイントとのより細かい比較を使用するのではなく、より多くのポイントと比較します。私は、違いが高次元空間でより極端であると思われます。
最終的なヒント:ツリー内の葉のノードは、線形スキャンのために整数のフラット配列である必要があります。小さなセット(おそらく1000ポイント以下)の場合、これはより速く、メモリ効率が高くなります。
他のヒント
私は2のビットセットで入力番号を表すソリューションを書きました32 ビットなので、特定の数値が入力にあるかどうかをO(1)に確認できます。次に、クエリ数と最大距離の場合、その距離内ですべての数値を再帰的に生成し、ビットセットに対してそれらを確認します。
たとえば、最大距離5の場合、これは242825数です(和d = 0〜5 {32 dを選択します})。比較のために、たとえば、Dietrich EPPのVP-Treeソリューションは、1億の数の22%、つまり2200万の数字を超えています。
Dietrichのコード/ソリューションを基礎として使用して、ソリューションを追加して彼と比較しました。最大距離10までの速度で、1秒あたりのクエリの速度が次のとおりです。
Dist BK Tree VP Tree Bitset Linear
1 10,133.83 15,773.69 1,905,202.76 4.73
2 677.78 1,006.95 218,624.08 4.70
3 113.14 173.15 27,022.32 4.76
4 34.06 54.13 4,239.28 4.75
5 15.21 23.81 932.18 4.79
6 8.96 13.23 236.09 4.78
7 6.52 8.37 69.18 4.77
8 5.11 6.15 23.76 4.68
9 4.39 4.83 9.01 4.47
10 3.69 3.94 2.82 4.13
Prepare 4.1s 21.0s 1.52s 0.13s
times (for building the data structure before the queries)
わずかな距離の場合、ビットセットソリューションは4つの中で最も速いです。質問の著者エリックは、最大の関心距離はおそらく4-5であるとコメントしました。当然のことながら、私のビットセットソリューションは、より広い距離で遅くなり、線形検索よりも遅くなります(距離32の場合、2を通過します。32 数字)。しかし、距離9の場合、それはまだ簡単に導きます。
また、Dietrichのテストを変更しました。上記の結果はそれぞれ、アルゴリズムを少なくとも3つのクエリとできるだけ多くのクエリを15秒で解決させるためです(1、2、4、8、16などのクエリでラウンドを行います。合計で合格)。それはかなり安定しているので、たった1秒間も同様の数字を取得します。
私のCPUはI7-6700です。 私のコード(ディートリッヒに基づく)はここにあります (少なくとも今のところそこにあるドキュメントを無視して、それについて何をすべきかわからないが、 tree.c
すべてのコードと私が含まれています test.bat
私がコンパイルして走った方法を示しています(私はディートリッヒの旗を使用しました Makefile
)). 私のソリューションへのショートカット.
1つの注意事項:私のクエリの結果には数字が1回しか含まれていないため、入力リストに重複番号が含まれている場合、それが望まれる場合と望まれない場合があります。問題著者のエリックの場合、重複はありませんでした(以下のコメントを参照)。いずれにせよ、このソリューションは、入力に重複がないか、クエリの結果に複製を望んでいない、または必要としない人に適している可能性があります(純粋なクエリの結果は、終わりの手段にすぎない可能性が高いと思います。他のコードの一部は、数字を他のものに変換します。たとえば、ハッシュがその数字であるファイルのリストに番号をマッピングするマップ。
共通のアプローチ(少なくとも共通じていないに分けプログラムにより、ビット列複数チャンクは、クエリはこのチャンクのための完全一致としてのプレフィルター。すファイルを作成しますし、多くのファイルとしていチャンク(例えば4)各チャンクpermuted前後に並べ替えるには、ファイルです。利用できるバイナリの検索もできるので拡大で検索の上下のマッチングチャンクのための特典です。
それをビット単位のハミング距離を計算は、返される結果すべき小さなサブセットの全体のデータセットである。このデータファイルまたはSQLます。
うおさらい:と言いてチームの32ビットの文字列DBまたはファイルおよびすべてのハッシュ内には3ビットハミング距離以下の"クエリ"ビット文字列:
テーブルを作成するつの列:それぞれ8ビット文字列としてはint)スライスの32ビットのハッシュ,islice1-4.またはご利用の場合、ファイルをファイルについては、これ.このスライスを持つ"islice"フロントの"行"
スライス検索プログラムにより、ビット列と同じ方法でqslice1-4.
クエリをこのテーブル等の
qslice1=islice1 or qslice2=islice2 or qslice3=islice3 or qslice4=islice4
.ここで毎に文字列内にある7ビット8 - 1
のクエリ文字列です。を使用する場合は、ファイル、バイナリ検索にそれぞれの国のそれぞれの時代のpermutedファイルと同じ。各返されるビット文字列を計算し、正確なハミング距離をペアとすクエリービット文字列の再構築に向指数側のビットから文字列のスライスのいずれかからのDBからpermutedファイル)
このステップ4では満たすペアワイズハミング計算の全体テーブルは非常に効率的です。さらに、やすいシャードファイル型のファイルが必要である速度で平行度.
現在のコースこのケースではただいていますが、自己の参加のソートは、すべての値を内部での距離です。同じような質問をするとともに、もの作品はまぁいいの拡大を上下から順列をファイルまたはリスト)を開始チャンクの計算とハミング距離を伴う。
場合を走るメモリの代わりにファイル、100M32ビットの文字列データセットが大きくなり、4GBにサンプルがあります。そのためのpermutedのリストの要約16GB+のアプリです。もう優れた成果のメモリマップドファイルの代わりに、以下のRAMを同様のサイズのデータセット
がオープンソースの実装が可能です。最高のスペースがまぁ、一行 SimhashによるMoz, C++が64ビットの文字列は32ビット.
この限定happing距離アプローチした最初の記述によるAFAIK モーゼCharikar その"simhash"の将来性のある 論文 に対応するGoogle 特許:
- 近似最近接検索でハミング空間
[...]
与えられたビットベクトルを構成するdビット毎に、お客様に満足していただける N=O(n1/(1+))ランダムに組みのビット.各 ランダムに換σを維持しておりソートするためには、σの このビットのベクトル、際の字句のビットpermuted によるσ.定のクエリービットベクトルq、おおよその 最近隣るには、次のように:
各permutation σは、バイナリ検索O σに 二つのビットベクトルの最寄りのq(際の字句順で得られたビットpermutedによるσ).現在の検索の各 の選別受注O σ検討要素を上下 の位置によって返されるバイナリ検索のための 最長長さの接頭辞に一致する。
Monika Henziger 拡大はこの女紙 "見複写のウェブページ:大規模な評価アルゴリズム":
3.3の結果のためのアルゴリズムC
お仕切られたビットの文字列の各ページに12は非 重複する4バイトの作成20B枚、計算のC-類似性のすべてのページが少なくとも一つの 作品に共通している。このアプローチはあなたのすべて ペアのページを差11、すなわち、C-類似373, だが内部的に大きな違いがある。
この中でも説明されています紙 検出近の重複をWebクロール によるGurmeetシンManku,Arvindジ、AnishダSarma:
- のハミング距離の問題
定義:指集f-ビットの指紋採取 クエリを指紋Fるかどうか、既存の指紋 からFに最kビット.(バッチモードバージョン 上記の問題として設定のクエリを指紋 の代わりに、単一クエリを指紋)
[...]
勘:考えてソートされたテーブルの2d-f-ビット真にランダム指紋.点での最大ビットd ます。上のこれらのd-ビットの数量 "ほとんどカウンター"という感じで(a)かなりの数の2dビット 組み合わせが存在し、(b)非常に少数のd-ビットの組み合わせ 重複しています。一方、少なくとも重要なf−d ビットが"ほぼランダムに".
現在選択dなど|d−d|小さな整数です。以降 のテーブルがソートされ、シングルプローブですべて把握する指紋を合わせたF d最も重要なビット位置があります。以降|d−d|表明するものではなく、数などの試合も 期待されます。各マッチングの指紋できま 簡単に出場合とは異なりますF多kビットの位置 やません(これらの違いう制限されることに f−d最大のビット位置).
手続き上記を探し、既存の 指紋認証するものとは異なるF kビットの位置、 制限されることになり、少なくとも重要なf−dのビット F.この数多い。すべて にすれば十分小さな番号の追加 ソートテーブルとして正式につ、次のセクションで説明します。
注意:掲載し、同様の回答を 関連DBのみ質問
指定されたハミング距離内で、元のリストのあらゆる可能なバリエーションを事前に計算し、ブルームフィルターに保存できます。これにより、速い「いいえ」が得られますが、必ずしも「はい」についての明確な答えが得られません。
はいの場合、ブルームフィルターの各位置に関連付けられたすべての元の値のリストを保存し、それらを一度に1つずつ通過します。スピード /メモリのトレードオフのために、ブルームフィルターのサイズを最適化します。
それがすべて正確に機能するかどうかはわかりませんが、燃えるランタイムラムを持っていて、事前コンピューションで非常に長い時間を費やすことをいとわない場合、良いアプローチのように思えます。
リストを並べ替えてから、ハミング距離内のさまざまな値でその並べ替えられたリストでバイナリ検索を行うのはどうですか?
この問題を解決するための1つの可能なアプローチは、 分離セットのデータ構造. 。アイデアは、同じセットでハミング距離<= kを持つマージリストメンバーです。これがアルゴリズムの概要です。
それぞれのため リストメンバー 可能な限り計算します 価値 ハミング距離で<= k。 k = 1の場合、32の値(32ビット値の場合)があります。 K = 2、32 + 32*31/2値の場合。
計算するごとに 価値, 、元の入力にあるかどうかをテストします。このチェックを行うには、サイズ2^32またはハッシュマップの配列を使用できます。
場合 価値 元の入力にある、 リストメンバー.
- 変数で実行された組合運用の数を保持します。
n分離セット(nは入力の要素の数)でアルゴリズムを開始します。ユニオンオペレーションを実行するたびに、1つの分離セットの数を減らすことができます。アルゴリズムが終了すると、分離セットのデータ構造は、ハミング距離<= kのすべての値を分離セットにグループ化します。この分離セットのデータ構造は、計算できます ほとんど 線形時間.