⭐ブログのホームページ: ️CS セミのホームページ
⭐フォロー歓迎: いいね、お気に入り、メッセージを残す
⭐ コラム シリーズ: 高度な C++
⭐ コード リポジトリ: 高度な C++
ホーム 人々が更新するのは簡単ではありません。あなたの「いいね!」と関心は私にとって非常に重要です。友達、いいね、フォローしてください。あなたのサポートが私の創作の最大の動機です。友達は質問するためにプライベートメッセージを送信することを歓迎します。家族メンバーの皆さん、忘れないでくださいいいね&集めて+フォロー! ! !
ハッシュ アプリケーション - ブルーム フィルター
1. ブルームフィルターの提案
Douyin を視聴すると、おすすめの広告やおすすめの Douyin コンテンツが頻繁に表示されます。では、このおすすめはどのように実現されるのでしょうか?もちろん、サーバーは、ユーザーが閲覧したすべての履歴記録を記録するために使用されます。レコメンド システムがニュースを推奨する際、各ユーザーの履歴記録を除外し、すでに存在する記録も除外します。この方法でのみ可能です。次に、非常に難しい質問がもう 1 つあります。それを迅速に見つけるにはどうすればよいでしょうか?検索を実現するには次の 3 つの方法があります。
最初の方法: ハッシュ テーブルまたはハッシュ バケットを使用しますが、ハッシュ テーブルの使用はスペースの無駄が多すぎることがわかります。
2 番目のアプローチ: ビットマップの概念を使用してユーザー レコードを保存しますが、欠点は、整数は保存できるが、文字列などの文字列型を処理できないことです。
3 番目の方法: 次に、最初のメソッドのハッシュ テーブルと 2 番目のメソッドのビットマップを組み合わせるだけです。これにより、スペースが節約されるだけでなく、文字列なども格納されます。文字列型の値。
2. ブルームフィルターの概念
ブルーム フィルターは、1970 年にバートン ハワード ブルームによって提案されたコンパクトで賢い確率的データ構造です。効率的な挿入とクエリが特徴です。複数のハッシュ関数を使用して、「存在してはいけないもの、または存在する可能性があるもの」を伝えることができます。データをビットマップ構造にマッピングします。この方法では、クエリの効率が向上するだけでなく、メモリ領域も大幅に節約できます。
ブルーム フィルターは本質的にビットマップの拡張と変形であり、誤検知率を効果的に減らすことができます。誤検知率を減らす方法は、データがビットマップにマッピングされるときに、ブルーム フィルターが複数のハッシュ関数を使用してビットマップにマッピングすることです。は複数のビットにマッピングされます。データがビットマップ内にあるかどうかを判断するときは、これらのハッシュ関数に基づいて対応するビットを計算する必要があります。これらのビットがすべて 1 に設定されている場合は、データが存在すると判断され、それ以外の場合は、データが存在すると判断されます。データが存在しないと判断されます。複数のハッシュ関数を使用して複数のビットにマッピングすると、誤判定率を効果的に低減できるため、以下の QQ ニックネームを使用して説明します。
データが 3 ビットにマッピングされていると仮定します。Zhang San がこれら 3 ビットを最初にマッピングしたことがわかります。このビットマップに Li Si が含まれているかどうかを判断するとき、最初に 3 ビットをマッピングし、最初の 2 つのマッピングが同じビットであることがわかります。 John Doe としての位置を指定すると、ハッシュ関数の競合が発生しますが、最後のハッシュ関数がビットマップの最後のビットにマッピングされ、最後のビットが 0 である場合、ビットマップにニックネーム John Doe が存在しないと判断されるため、ハッシュ関数の競合が発生します。ジョン・ドゥというニックネームを使用してください。
しかし、ニックネームをどんどん挿入すると、誤判定率がますます高くなり、ハッシュの競合が増えます。下の図を見てみましょう。完全な誤判定が発生しています: (Wang Wu Mingming には表示されません)ビットマップを作成しましたが、誤った判断が発生しました。これは、Wang Wu というニックネームの 3 ビットがハッシュ競合に達したためです。2 つのニックネーム「Zhang San」と「Li Si」が既に占有しているため、ニックネーム「Wang Wu」が置き換えられました)
1. ブルームフィルターの特徴
ブルーム フィルターがビットマップ内にデータが存在しないと判断した場合、そのデータはビットマップ内の少なくとも 1 つのビット 0 にマッピングされる必要があるため、非常に正確です。
ブルーム フィルターがビットマップにデータが存在すると判断した場合、誤った判断が行われている可能性があるため、データは不正確になります。他のニックネームが新しいデータのビットを占有している可能性があり、新しいニックネームは実際には使用されていません。ビットマップに存在します。
2. 誤判定率をどう抑えるか
判定条件1:ビットマップのサイズ。ビットマップが小さい場合、ブルーム フィルターはすぐにビットマップのすべてのビットを 1 に設定します。このとき、ブルーム フィルターの誤検知率は非常に高くなります。そのため、ビットマップが長いほど、ブルーム フィルターが誤検知される可能性が高くなります。誤判断率が低くなります。
判定条件2:ハッシュ関数の数。ハッシュ関数の数が多ければすぐにブルームフィルタの全ビットでビットマップのビットが1になりますが、ハッシュ関数の数が少なすぎると誤判定率も高くなります。
したがって、ビットマップのサイズとハッシュ関数の数に応じて、誰かが次の式を要約しました。
このうち、k はハッシュ関数の数、m はブルーム フィルターの長さ、n は挿入された要素の数、p は誤検知率です。ここで、k=3、ln2 を 0.7 とすると、m と n の関係は m=4*n になります。つまり、ブルーム フィルターの長さは、挿入された要素の長さの 4 倍になります。
3. ブルームフィルターの実装
ブルーム フィルターはテンプレート クラスを実装できるため、挿入されたビットマップ (ブルーム フィルター) は文字列だけでなく、他のタイプも使用できます。一般に、ブルーム フィルターは文字列の処理に使用されます。そこで、ここでテンプレート パラメーター K のデフォルトのタイプを設定できます。文字列に。
//布隆过滤器
template<size_t N, class K = string, class Hash1 = BKDRHash, class Hash2 = APHash, class Hash3 = DJBHash>
class bloomfilter
{
public:
//...
private:
bitset<N> _bs;
};
同時に、文字列を整数に変換する上位 3 つのアルゴリズムは次のように動作します。
1. 3 種類の文字列を整数に変換する方法を導入します。
struct BKDRHash
{
size_t operator()(const string& s)
{
// BKDR
size_t value = 0;
for (auto ch : s)
{
value *= 31;
value += ch;
}
return value;
}
};
struct APHash
{
size_t operator()(const string& s)
{
size_t hash = 0;
for (long i = 0; i < s.size(); i++)
{
if ((i & 1) == 0)
{
hash ^= ((hash << 7) ^ s[i] ^ (hash >> 3));
}
else
{
hash ^= (~((hash << 11) ^ s[i] ^ (hash >> 5)));
}
}
return hash;
}
};
struct DJBHash
{
size_t operator()(const string& s)
{
size_t hash = 5381;
for (auto ch : s)
{
hash += (hash << 5) + ch;
}
return hash;
}
};
文字列を整数に変換するためにここで選択されたハッシュ関数は、テスト後の総合スコアが最も高い BKDRHash、APHash、DJBHash です。これら 3 つのハッシュ アルゴリズムは、さまざまなシナリオでハッシュ競合が発生する可能性が最も低くなります。現時点では、これら 3 つのハッシュ関数を単独で使用した場合の衝突の確率は比較的小さいですが、同時に使用すると衝突の確率はさらに小さくなります。
2. ブルームフィルターの挿入
つまり、これら 3 つの関数を通じて 3 つの異なるビットが計算され、整数に変換され、ビットマップにマッピングされます。要素を挿入するときは、3 つのハッシュ関数を通じて要素に対応する 3 ビットを計算し、ビットマップ内のこれら 3 ビットを 1 に設定する必要があります。
void Set(const K& key)
{
// 计算出key对应的三个位
size_t i1 = Hash1()(key) % N;
size_t i2 = Hash2()(key) % N;
size_t i3 = Hash3()(key) % N;
// 置1
_bs.set(il);
_bs.set(i2);
_bs.set(i3);
}
3. ブルームフィルターの検索
検出では、3 つのハッシュ関数を使用して要素に対応する 3 ビットを計算し、ビットマップ内のこれら 3 ビットが 1 に設定されているかどうかを判断するだけで済みます。
これら 3 ビットのいずれかが設定されていない限り、その要素は存在してはいけないことを意味します。
3 ビットがすべて設定されている場合、true を返すと要素は存在しますが、誤った判断が生じる可能性があります。
bool Test(const K& key)
{
size_t i1 = Hash1()(key) % N;
if (_bs.test(il) == false)
{
return false;
}
size_t i2 = Hash2()(key) % N;
if (_bs.test(i2) == false)
{
return false;
}
size_t i3 = Hash3()(key) % N;
if (_bs.test(i3) == false)
{
return false;
}
// 三个都存在,可能导致误判
return true;
}
4. ブルームフィルターの削除
1 つの要素が削除されると他の要素が影響を受ける可能性があるため、ブルーム フィルターは削除を直接サポートできません。
上の図に示すように、データ「John Doe」を削除すると、3 つの 1 がすべて 0 に設定され、結果として John Doe の 2 つが 0 に設定されます。張三のデータおかしくないですか?
削除をサポートする方法: ブルーム フィルターの各ビットを小さなカウンターに拡張し、要素を挿入するときに k カウンター (k 個のハッシュ関数によって計算されたハッシュ アドレス) に 1 を追加し、要素を削除します。 の場合、k カウンターは 1 減分され、削除されます。数倍のストレージスペースを占有するため、操作量が増加します。
4. ブルームフィルターのメリット
- 要素の追加とクエリの時間計算量は O(K) (K はハッシュ関数の数であり、一般に小さい) であり、データ量とは関係ありません。< a i=1>
- ハッシュ関数は相互に関係がなく、ハードウェアの並列操作を容易にします。
- ブルーム フィルターは要素自体を保存する必要がないため、厳格な機密性要件がある状況では大きな利点があります。
- 特定の誤った判断に耐えることができる場合、ブルーム フィルターは他のデータ構造に比べてスペース面で大きな利点があります。
- データ量が多い場合、ブルーム フィルターはセット全体を表すことができますが、他のデータ構造では表すことができません。
- 同じハッシュ関数のセットを使用するブルーム フィルターは、交差、和集合、および差分の演算を実行できます。
5. ブルームフィルターのデメリット
- 偽陽性率がある、つまり偽陽性 (False Position) がある、つまり要素がコレクション内にあるかどうかを正確に判断できない (救済方法: 誤って判断される可能性のあるデータを保存するホワイトリストを作成する)
- 要素自体を取得できません
- 通常、ブルーム フィルターから要素を削除することはできません。
- カウント方法を使用して削除する場合、カウントのラップアラウンドの問題が発生する可能性があります。