1. ビットマップ(ビットセット)
- ビットマップはデータの記録単位を1ビットとする のハッシュテーブル符号なし整数はキー値で、直接アドレッシング方式(ハッシュ衝突の問題はありません)を使用し、そのハッシュマッピング関数は次のとおりです。
- f ( key ) = key (key の存在状況は key bit で記録される) f(key)=key (key の存在状況は key bit で記録される)f (キー) _=key ( keyの存在状況はkey bitで記録されます)
- ビット 1 は、マッピングビットに対応するキーが存在します、ビットは0ですマッピングビットに対応するキーが存在しないことを示します
- STL のビットマップはコンテナーを適応させる
vector<char>
ために使用され、ビット操作を使用して
その機能インターフェイス キーの存在ステータスの記録を実現します。
基本的な実装:
//Size记录要存放的数据个数上限(非类型模板参数),即至少需要开辟Size个比特位的空间
template<size_t Size>
class bitset
{
public:
bitset()
{
_table.resize((Size / 8) + 1, 0);
}
//将第key个比特位设置为1,表示key存在于集合中
void set(size_t key)
{
//计算第key个比特位位于vector的第几个字节
size_t bytes = key / 8;
//计算第key个比特位位于某字节的第几个个比特位
size_t bits = key % 8;
//通过位运算将第key个比特位设置为1
_table[bytes] |= (1 << bits);
}
//将第key个比特位设置为0,表示将数据key从集合中删除
void reset(size_t key)
{
//计算第key个比特位位于vector的第几个字节
size_t bytes = key / 8;
//计算第key个比特位位于某字节的第几个个比特位
size_t bits = key % 8;
//通过位运算将第key个比特位设置为0
_table[bytes] &= ~(1 << bits);
}
//查询key是否存在于集合中
bool test(size_t key)
{
//计算第key个比特位位于vector的第几个字节
size_t bytes = key / 8;
//计算第key个比特位位于某字节的第几个个比特位
size_t bits = key % 8;
//通过位运算判断第key个比特位是否为1
return _table[bytes] & (1 << bits);
}
private:
std :: vector<char> _table;
};
- ビットマップは、コレクション内にキーワードが存在するかどうかのみを記録できますが、赤黒ツリーやハッシュ バケットと比較して、ビットマップはスペース効率と時間効率が高く、次の用途に非常に適しています。大量のデータの処理:
bitset<-1>
(-1 を符号なし整数に変換) 、このようなオブジェクトは512MB記録には使用できますが、約考えられるすべてのキー値- 実際の応用:
- 特定のデータがコレクション内にあるかどうかをすばやく確認する
- データの並べ替え + 重複排除
- 2 つの集合の共通部分、和集合などを見つけます。
- オペレーティング システムのディスク ブロック マーキング
- 文字列ハッシュ関数を使用すると、ビットマップを使用して記録できます。研究コレクションにおける文字列の存在状況ただし、異なる文字列のハッシュ衝突の可能性を減らすために、異なる文字列が同じキー値に対応する場合があります。文字列使える複数の異なる文字列ハッシュ関数ビットマップに複数回マッピングされる、このように設計されたビットマップはブルーム フィルターと呼ばれます
2. ブルームフィルター (bloomFilter)
- 同じ文字列を通過複数の異なる文字列ハッシュ関数に複数回マッピングされる同じビットマップしたがって、効果的にビットマップ内の文字列のハッシュ衝突の確率
基本的な実装:
- ビットセットを多重化することで実現:
//字符串哈希映射函数1
struct BKDRHash
{
size_t operator()(const string& s)
{
size_t hash = 0;
for (auto ch : s)
{
hash += ch;
hash *= 31;
}
return hash;
}
};
//字符串哈希映射函数2
struct APHash
{
size_t operator()(const string& s)
{
size_t hash = 0;
for (long i = 0; i < s.size(); i++)
{
size_t ch = s[i];
if ((i & 1) == 0)
{
hash ^= ((hash << 7) ^ ch ^ (hash >> 3));
}
else
{
hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));
}
}
return hash;
}
};
//字符串哈希映射函数3
struct DJBHash
{
size_t operator()(const string& s)
{
size_t hash = 5381;
for (auto ch : s)
{
hash += (hash << 5) + ch;
}
return hash;
}
};
// Size是最多不同key的个数
template<size_t Size,class Key = string,class Hash1 = BKDRHash,class Hash2 = APHash,class Hash3 = DJBHash>
class BloomFilter
{
public:
void set(const Key& key)
{
size_t len = Size * _factor;
//同一个字符串映射三次
size_t hash1 = Hash1()(key) % len;
_bs.set(hash1);
size_t hash2 = Hash2()(key) % len;
_bs.set(hash2);
size_t hash3 = Hash3()(key) % len;
_bs.set(hash3);
}
bool test(const Key& key)
{
size_t len = Size * _factor;
//只有三个哈希映射都相同才认为关键字是重复的
size_t hash1 = Hash1()(key) % len;
if (!_bs.test(hash1))
{
return false;
}
size_t hash2 = Hash2()(key) % len;
if (!_bs.test(hash2))
{
return false;
}
size_t hash3 = Hash3()(key) % len;
if (!_bs.test(hash3))
{
return false;
}
return true;
}
private:
static const size_t _factor = 6;
//由于一个key要占用三个比特位,因此需要额外开辟_factor倍数的空间
bitset<Size * _factor> _bs;
};
- ブルームフィルターの適用:
- ブルームフィルター要素自体は保存されません、厳格なデータ機密性が必要な場合に大きな利点があります。
- ある程度の誤った判断が許容されるシナリオでは、ブルーム フィルターは他のデータ構造よりも優れています。時間とスペースの効率化
- データ量が多い場合には、ブルーム フィルターはデータの完全なセットを表すことができます、他のデータ構造はできません (メモリによる制限)
- 同じハッシュ関数のセットを使用するブルーム フィルターは、交差、和集合、および差分の演算を実行できます。
- ゲーム内でのニックネームの有無等の判断重複データフィルタリングのシナリオブルームフィルターがよく使われる
3. ハッシュセグメンテーションのアイデア
- ハッシュ化の考え方は、大量のデータを扱うためのアイデア—文字列が 100 億あり、コンピュータのメモリが 1G しかない場合、最も頻繁に出現する文字列を見つけるアルゴリズムをどのように設計すればよいでしょうか。
-
まず、データセットに対してハッシュセグメンテーションを実行し、それを次のように分割します。N個のサブファイルに分割( N 個のサブファイル0~N-1の番号が付けられています)の分割方法は、文字列ハッシュ関数を使用して
Hasn()
各文字列のキー値を取得し、次のマッピング関係に従って各文字列を番号 i に対応するサブファイルに分類します。 -
i = H ハッシュ (キー) mod N i =ハッシュ(キー)\mod N私=ハッシュ(キー)_ _ _ _モッドN
-
なぜなら同一の文字列は同じサブファイルに分類する必要がありますしたがって、各サブファイルをメモリにロードし、統計にマップを使用します (一部のサブファイルが大きすぎる場合は、同じ方法で (異なる文字列ハッシュ関数を使用して) ハッシュ セグメンテーションを実行し続けることができます)。
-
- 上記のハッシュ セグメンテーション方法は、次の問題にも適用できます。既存のファイル A とファイル B には、それぞれ 100 億の文字列が格納されており、コンピュータには 1G のメモリしか使用できない場合、2 つのファイルの共通部分を取得するにはどうすればよいでしょうか。
-
効率的な解決策: ファイル A とファイル B を別々にハッシュ分割します。
-
なぜなら同一の文字列は同じ番号のサブファイルに分類する必要があります、つまりサブファイルAi和Biそれらをペアでメモリにロードし、set を使用して共通の要素を見つけます
-