點陣圖和布隆過濾器
給四十億個不重複的無符號整數,再給你一個無符號整數,如何快速
判斷一個數是否在這四十億個數中?
1byte=8bit;
1kB=1024byte;
1MB=1024KB;
1GB=1024MB;
1GB大約有10億多個位元組,而40億個無符號整數有160億個位元組,所以要存放下40億
個無符號整數大約需要16GB空間,然而我們的電腦記憶體沒有這麼大,我們需要想一種方法
來解決這個問題?
對於這種問題我們可以使用點陣圖,我們沒有必要用四個位元組來存放一個數,因為我們
只用判斷一個數是否在這40億個數中,我們可以把一個數存放在一個位元位上,1表示
存在,0表示不存在,這樣這40億個數就需要大約500M空間,當要判斷的時候我們
可以算出這個數在bitmap中的下標,1表示存在,0表示不存在,這樣我們就完成這個問題了。
//點陣圖
//點陣圖
#pragma once
class BitMap
{
public:
BitMap(size_t range)
{
_Bitmap.resize(range / 8+1, 0); //設定點陣圖大小
}
void Set(size_t value)//置1
{
size_t index = value >> 3;
size_t pos = value % 8;
_Bitmap[index] |= (1 << pos);
}
void Reset(size_t value) //置0
{
size_t index = value >> 3;
size_t pos = value % 8;
_Bitmap[index] &= (~(1 << pos));
}
bool Test(size_t value)
{
size_t index = value >> 3;
size_t pos = value % 8;
return _Bitmap[index] & (1 << pos);
}
protected :
vector<char> _Bitmap;
};
void TestBitMap()
{
BitMap bt(-1);
bt.Set(1);
bt.Set(12);
bt.Set(123);
bt.Set(1234);
bt.Set(12345);
cout << bt.Test(0) << endl;
cout << bt.Test(1) << endl;
cout << bt.Test(12) << endl;
cout << bt.Test(123) << endl;
cout << bt.Test(1234) << endl;
cout << bt.Test(12345) << endl;
cout << bt.Test(321) << endl;
bt.Reset(1234);
bt.Reset(12345);
cout << endl;
cout << bt.Test(0) << endl;
cout << bt.Test(1) << endl;
cout << bt.Test(12) << endl;
cout << bt.Test(123) << endl;
cout << bt.Test(1234) << endl;
cout << bt.Test(12345) << endl;
cout << bt.Test(321) << endl;
}
點陣圖只能用來快速判斷一個整數是否在一堆整數中,如果我們想要判斷一個字串
是否在一堆字串裡,那麼點陣圖就做不到了,因此布隆過濾器就出現了。
布隆過濾器:布隆過濾器其實是結合了點陣圖與雜湊表,先將字串用字串雜湊演算法
對映到雜湊表中,但是由於雜湊衝突,我們可以一個字串使用多個不同的字串雜湊
演算法同時對映在這個雜湊表中,判斷一個字串是否在這堆字串中,我們可以算出
這個字串的位置,當且僅當這個字串每個對映位置都是1才表示存在,只要有一個
位置為0,就表示不存在;
#pragma once
#include "BitMap.h"
struct __HashFunc1
{
size_t BKDRHash(const char *str)//雜湊演算法
{ //這裡的雜湊演算法都是取至連結當中
register size_t hash = 0;
while (size_t ch = (size_t)*str++)
{
hash = hash * 131 + ch;
}
return hash;
}
size_t operator()(const string& s)
{
return BKDRHash(s.c_str());
}
};
struct __HashFunc2
{
size_t SDBMHash(const char *str)
{
register size_t hash = 0;
while (size_t ch = (size_t)*str++)
{
hash = 65599 * hash + ch;
}
return hash;
}
size_t operator()(const string& s)
{
return SDBMHash(s.c_str());
}
};
struct __HashFunc3
{
size_t RSHash(const char *str)
{
register size_t hash = 0;
size_t magic = 63689;
while (size_t ch = (size_t)*str++)
{
hash = hash * magic + ch;
magic *= 378551;
}
return hash;
}
size_t operator()(const string& s)
{
return RSHash(s.c_str());
}
};
struct __HashFunc4
{
size_t APHash(const char *str)
{
register size_t hash = 0;
size_t ch;
for (long i = 0; ch = (size_t)*str++; i++)
{
if ((i & 1) == 0)
{
hash ^= ((hash << 7) ^ ch ^ (hash >> 3));
}
else
{
hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));
}
}
return hash;
}
size_t operator()(const string& s)
{
return APHash(s.c_str());
}
};
struct __HashFunc5
{
size_t JSHash(const char *str)
{
if (!*str) // 這是由本人新增,以保證空字串返回雜湊值0
return 0;
register size_t hash = 1315423911;
while (size_t ch = (size_t)*str++)
{
hash ^= ((hash << 5) + ch + (hash >> 2));
}
return hash;
}
size_t operator()(const string& s)
{
return JSHash(s.c_str());
}
};
template<class K=string
,class HashFunc1=__HashFunc1
, class HashFunc2=__HashFunc2
, class HashFunc3=__HashFunc3
,class HashFunc4=__HashFunc4
,class HashFunc5=__HashFunc5>
class BloomFilter
{
public:
BloomFilter(size_t size)
:_bitmap(size * 5)
, range(size * 5)
{}
void Setb(const K& key)
{
size_t hash1 = HashFunc1()(key) % range;
size_t hash2 = HashFunc2()(key) % range;
size_t hash3 = HashFunc3()(key) % range;
size_t hash4 = HashFunc4()(key) % range;
size_t hash5 = HashFunc5()(key) % range;
//為了減少誤判,提高精度,一個數對映到5個位置
_bitmap.Set(hash1);
_bitmap.Set(hash2);
_bitmap.Set(hash3);
_bitmap.Set(hash4);
_bitmap.Set(hash5);
}
bool Test(const K& key)
{
size_t hash1 = HashFunc1()(key) % range;
size_t hash2 = HashFunc2()(key) % range;
size_t hash3 = HashFunc3()(key) % range;
size_t hash4 = HashFunc4()(key) % range;
size_t hash5 = HashFunc5()(key) % range;
if (_bitmap.Test(hash1) == false)
return false;
if (_bitmap.Test(hash2) == false)
return false;
if (_bitmap.Test(hash3) == false)
return false;
if (_bitmap.Test(hash4) == false)
return false;
if (_bitmap.Test(hash5) == false)
return false;
return true;
}
protected:
BitMap _bitmap;
size_t range;
};
void TestBloomFilter()
{
BloomFilter<> bf(1024);
bf.Setb("abcd");
bf.Setb("1234");
bf.Setb("qwer");
bf.Setb("zxcv");
bf.Setb("mlop");
cout << bf.Test("0123") << endl;
cout << bf.Test("abcd") << endl;
cout << bf.Test("1234") << endl;
cout << bf.Test("zxcv") << endl;
cout << bf.Test("mlop") << endl;
cout << bf.Test("4569") << endl;
}
上邊這個布隆演算法節省空間但是不支援刪除演算法,因為上邊那個演算法有可能一個位置
映射了幾個數,刪除了一個數可能會影響到別的數;
如果我們想要使用刪除演算法,我們可以使用引用計數的方法,那麼存放一個數的位置
就不能用一個位元位了,而是可以用一個無符號整數來存放,當刪除一個數的時候,
如果它對映到的每個位置都大於0,就表明這個數存在,那麼就讓這幾個數同時減1;
因為這個演算法為了使用刪除演算法,浪費了一些空間,適用於少量的資料
#pragma once
struct __HashFunc1
{
size_t BKDRHash(const char *str)
{
register size_t hash = 0;
while (size_t ch = (size_t)*str++)
{
hash = hash * 131 + ch;
}
return hash;
}
size_t operator()(const string& s)
{
return BKDRHash(s.c_str());
}
};
struct __HashFunc2
{
size_t SDBMHash(const char *str)
{
register size_t hash = 0;
while (size_t ch = (size_t)*str++)
{
hash = 65599 * hash + ch;
}
return hash;
}
size_t operator()(const string& s)
{
return SDBMHash(s.c_str());
}
};
struct __HashFunc3
{
size_t RSHash(const char *str)
{
register size_t hash = 0;
size_t magic = 63689;
while (size_t ch = (size_t)*str++)
{
hash = hash * magic + ch;
magic *= 378551;
}
return hash;
}
size_t operator()(const string& s)
{
return RSHash(s.c_str());
}
};
struct __HashFunc4
{
size_t APHash(const char *str)
{
register size_t hash = 0;
size_t ch;
for (long i = 0; ch = (size_t)*str++; i++)
{
if ((i & 1) == 0)
{
hash ^= ((hash << 7) ^ ch ^ (hash >> 3));
}
else
{
hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));
}
}
return hash;
}
size_t operator()(const string& s)
{
return APHash(s.c_str());
}
};
struct __HashFunc5
{
size_t JSHash(const char *str)
{
if (!*str) // 這是由本人新增,以保證空字串返回雜湊值0
return 0;
register size_t hash = 1315423911;
while (size_t ch = (size_t)*str++)
{
hash ^= ((hash << 5) + ch + (hash >> 2));
}
return hash;
}
size_t operator()(const string& s)
{
return JSHash(s.c_str());
}
};
template<class K = string
, class HashFunc1 = __HashFunc1
, class HashFunc2 = __HashFunc2
, class HashFunc3 = __HashFunc3
, class HashFunc4 = __HashFunc4
, class HashFunc5 = __HashFunc5>
class BoolmFilter
{
public:
BoolmFilter(size_t range)
{
_bitmap.resize(range * 5, 0);
}
void Setb(const K& value)
{
size_t range = _bitmap.size();
size_t hash1 = HashFunc1()(value) % range;
size_t hash2 = HashFunc2()(value) % range;
size_t hash3 = HashFunc3()(value) % range;
size_t hash4 = HashFunc4()(value) % range;
size_t hash5 = HashFunc5()(value) % range;
_bitmap[hash1]++;
_bitmap[hash2]++;
_bitmap[hash3]++;
_bitmap[hash4]++;
_bitmap[hash5]++;
}
bool Reset(const K& value)
{
size_t range = _bitmap.size();
size_t hash1 = HashFunc1()(value) % range;
size_t hash2 = HashFunc2()(value) % range;
size_t hash3 = HashFunc3()(value) % range;
size_t hash4 = HashFunc4()(value) % range;
size_t hash5 = HashFunc5()(value) % range;
if (_bitmap[hash1] == 0
|| _bitmap[hash2] == 0
|| _bitmap[hash3] == 0
|| _bitmap[hash4] == 0
|| _bitmap[hash5] == 0)
{
return false; //如果這幾個位置任意一個為0表示不存在不能刪除
}
_bitmap[hash1]--;
_bitmap[hash2]--;
_bitmap[hash3]--;
_bitmap[hash4]--;
_bitmap[hash5]--;
return true;
}
bool Test(const K& value)
{
size_t range = _bitmap.size();
size_t hash1 = HashFunc1()(value) % range;
size_t hash2 = HashFunc2()(value) % range;
size_t hash3 = HashFunc3()(value) % range;
size_t hash4 = HashFunc4()(value) % range;
size_t hash5 = HashFunc5()(value) % range;
if (_bitmap[hash1] != 0
&& _bitmap[hash2] != 0
&& _bitmap[hash3] != 0
&& _bitmap[hash4] != 0
&& _bitmap[hash5] != 0)
{
return true;
}
return false;
}
protected:
vector<size_t> _bitmap; //存放無符號整形
};
void TestBloomFilter()
{
BoolmFilter<> bf(1024);
bf.Setb("abcd");
bf.Setb("1234");
bf.Setb("qwer");
bf.Setb("zxcv");
bf.Setb("mlop");
cout << bf.Test("0123") << endl;
cout << bf.Test("abcd") << endl;
cout << bf.Test("1234") << endl;
cout << bf.Test("zxcv") << endl;
cout << bf.Test("mlop") << endl;
cout << bf.Test("4569") << endl;
bf.Reset("zxcv");
bf.Reset("mlop");
cout << endl;
cout << bf.Test("0123") << endl;
cout << bf.Test("abcd") << endl;
cout << bf.Test("1234") << endl;
cout << bf.Test("zxcv") << endl;
cout << bf.Test("mlop") << endl;
cout << bf.Test("4569") << endl;
}