雜湊表擴充套件—布隆過濾器(Bloom Filter)
1.概念:
如果想判斷一個元素是不是在一個集合裡,一般想到的是將所有元素儲存起來,然後通過比較確定。連結串列,樹等等資料結構都是這種思路. 但是隨著集合中元素的增加,我們需要的儲存空間越來越大,檢索速度也越來越慢。不過世界上還有一種叫作雜湊表(又叫雜湊表,Hash table)的資料結構。它可以通過一個Hash函式將一個元素對映成一個位陣列(Bit Array)中的一個點。這樣一來,我們只要看看這個點是不是 1 就知道可以集合中有沒有它了。這就是布隆過濾器的基本思想。
它的優點是空間效率和查詢時間都遠遠超過一般的演算法,缺點是有一定的誤識別率(假正例False positives,即Bloom Filter報告某一元素存在於某集合中,但是實際上該元素並不在集合中)和刪除困難,但是沒有識別錯誤的情形(即假反例False negatives,如果某個元素確實沒有在該集合中,那麼Bloom Filter 是不會報告該元素存在於集合中的,所以不會漏報)。
2.實現原理:
直觀的說,bloom演算法類似一個hash set,用來判斷某個元素(key)是否在某個集合中。和一般的hash set不同的是,這個演算法無需儲存key的值,對於每個key,只需要k個位元位,每個儲存一個標誌,用來判斷key是否在集合中。
演算法:
1). 首先需要k個hash函式,每個函式可以把key雜湊成為1個整數
2). 初始化時,需要一個長度為range位元的陣列,每個位元位初始化為0
3). 某個key加入集合時,用k個hash函式計算出k個雜湊值,並把陣列中對應的位元位置為1
4). 判斷某個key是否在集合時,用k個hash函式計算出k個雜湊值,並查詢陣列中對應的位元位,如果所有的位元位都是1,認為在集合中。
3.程式碼實現:
採用3個hash函式計算雜湊值。
布隆結構設計:
typedef char* KeyType;
typedef size_t(*HASH_FUNC)(KeyType str);
typedef struct BloomFilter
{
BitMap _bm;
HASH_FUNC _Hashfunc1;
HASH_FUNC _Hashfunc2;
HASH_FUNC _Hashfunc3;
}BloomFilter;
hash函式:
static size_t BKDRHash(KeyType str)
{
unsigned int seed = 131; // 31 131 1313 13131 131313
unsigned int hash = 0;
while (*str )
{
hash = hash * seed + (*str++);
}
return (hash & 0x7FFFFFFF);
}
size_t DEKHash(KeyType str)
{
if(!*str) // 這是由本人新增,以保證空字串返回雜湊值0
return 0;
register size_t hash = 1315423911;
while (size_t ch = (size_t)*str++)
{
hash = ((hash << 5) ^ (hash >> 27)) ^ ch;
}
return hash;
}
size_t FNVHash(KeyType str)
{
if(!*str) // 這是由本人新增,以保證空字串返回雜湊值0
return 0;
register size_t hash = 2166136261;
while (size_t ch = (size_t)*str++)
{
hash *= 16777619;
hash ^= ch;
}
return hash;
}
bloom演算法實現函式:
void BloomFilterInit(BloomFilter *bf,size_t range) //初始化
{
BitMapInit(&bf->_bm,range);
bf->_Hashfunc1 = BKDRHash;
bf->_Hashfunc2 = FNVHash;
bf->_Hashfunc3 = DEKHash;
}
void BloomFilterSet(BloomFilter *bf,KeyType key)//標記相應位
{
assert(bf);
BitMapSet(&bf->_bm,bf->_Hashfunc1(key)%bf->_bm._range);
BitMapSet(&bf->_bm,bf->_Hashfunc2(key)%bf->_bm._range);
BitMapSet(&bf->_bm,bf->_Hashfunc3(key)%bf->_bm._range);
}
int BloomFilterTest(BloomFilter *bf,KeyType key)
{
assert(bf);
if (BitMapTest(&bf->_bm,bf->_Hashfunc1(key)%bf->_bm._range))
return -1;
if (BitMapTest(&bf->_bm,bf->_Hashfunc2(key)%bf->_bm._range))
return -1;
if (BitMapTest(&bf->_bm,bf->_Hashfunc3(key)%bf->_bm._range))
return -1;
return 0;
}
void BloomFilterDestory(BloomFilter *bf) //銷燬
{
BitMapDestory(&bf->_bm);
}
演算法測試案例及執行結果:
void TestBlooomFilter()
{
BloomFilter bf;
BloomFilterInit(&bf,-1);
BloomFilterSet(&bf,"123.5.3.6");
BloomFilterSet(&bf,"123.5.3.8");
BloomFilterSet(&bf,"123.5.3.6");
BloomFilterSet(&bf,"123.5.3.7");
BloomFilterSet(&bf,"123.5.3.4");
BloomFilterSet(&bf,"123.5.3.6");
BloomFilterSet(&bf,"123.5.3.8");
BloomFilterSet(&bf,"123.5.3.8");
BloomFilterSet(&bf,"123.5.3.6");
printf("ip is exist? %d\n",BloomFilterTest(&bf,"123.5.3.6"));
printf("ip is exist? %d\n",BloomFilterTest(&bf,"123.5.3.7"));
printf("ip is exist? %d\n",BloomFilterTest(&bf,"123.5.3.8"));
printf("ip is exist? %d\n",BloomFilterTest(&bf,"123.5.3.4"));
printf("ip is exist? %d\n",BloomFilterTest(&bf,"123.5.3.1"));
BloomFilterDestory(&bf);
}
0 代表存在 ,-1代表不存在。
程式碼中呼叫了點陣圖相關函式程式碼: 點陣圖相關部分知識在上篇博文中有詳細解釋。
#define _CRT_SECURE_NO_WARNINGS 1
#include"BitMap.h"
void BitMapInit(BitMap *bm,size_t range) //初始化
{
assert(bm);
bm->_bits = NULL;
bm->_range = range;
bm->_bits = (size_t *)malloc(sizeof(char)*bm->_range/8 +1);
assert(bm->_bits);
memset(bm->_bits,0,sizeof(char)*bm->_range/8 +1);
}
void BitMapSet(BitMap *bm,size_t x)//標記相應位
{
size_t num = x>>5;
size_t bit = x%32;
bm->_bits[num] |=(1<<bit);
}
int BitMapTest(BitMap *bm,size_t x)
{
size_t num = x>>5;
size_t bit = x%32;
if ((1<<bit)&bm->_bits[num])
return 0;
return -1;
}
void BitMapDestory(BitMap *bm)
{
free(bm->_bits);
bm->_bits = NULL;
bm->_range = 0;
}
4.布隆過濾器的實際用例[1]
Google 著名的分散式資料庫 Bigtable 使用了布隆過濾器來查詢不存在的行或列,以減少磁碟查詢的IO次數。
Squid 網頁代理快取伺服器在 cache digests 中使用了也布隆過濾器。
Venti 文件儲存系統也採用布隆過濾器來檢測先前儲存的資料。
SPIN 模型檢測器也使用布隆過濾器在大規模驗證問題時跟蹤可達狀態空間。
Google Chrome瀏覽器使用了布隆過濾器加速安全瀏覽服務。
在很多Key-Value系統中也使用了布隆過濾器來加快查詢過程,如 Hbase,Accumulo,eveldb,一般而言,Value 儲存在磁碟中,訪問磁碟需要花費大量時間,然而使用布隆過濾器可以快速判斷某個Key對應的Value是否存在,因此可以避免很多不必要的磁碟IO操作,只是引入布隆過濾器會帶來一定的記憶體消耗,下圖是在Key-Value系統中布隆過濾器的典型使用:
5.布隆過濾器相關擴充套件[1]
Counting filters
基本的布隆過濾器不支援刪除(Deletion)操作,但是 Counting filters 提供了一種可以不用重新構建布隆過濾器但卻支援元素刪除操作的方法。在Counting filters中原來的位陣列中的每一位由 bit 擴充套件為 n-bit 計數器,實際上,基本的布隆過濾器可以看作是隻有一位的計數器的Counting filters。原來的插入操作也被擴充套件為把 n-bit 的位計數器加1,查詢操作即檢查位陣列非零即可,而刪除操作定義為把位陣列的相應位減1,但是該方法也有位的算術溢位問題,即某一位在多次刪除操作後可能變成負值,所以位陣列大小 m 需要充分大。另外一個問題是Counting filters不具備伸縮性,由於Counting filters不能擴充套件,所以需要儲存的最大的元素個數需要提前知道。否則一旦插入的元素個數超過了位陣列的容量,false positive的發生概率將會急劇增加。當然也有人提出了一種基於 D-left Hash 方法實現支援刪除操作的布隆過濾器,同時空間效率也比Counting filters高。
Data synchronization
Byers等人提出了使用布隆過濾器近似資料同步。
Bloomier filters
Chazelle 等人提出了一個通用的布隆過濾器,該布隆過濾器可以將某一值與每個已經插入的元素關聯起來,並實現了一個關聯陣列Map。與普通的布隆過濾器一樣,Chazelle實現的布隆過濾器也可以達到較低的空間消耗,但同時也會產生false positive,不過,在Bloomier filter中,某 key 如果不在 map 中,false positive在會返回時會被定義出的。該Map 結構不會返回與 key 相關的在 map 中的錯誤的值。
參考資料
[1] https://www.cnblogs.com/liyulong1982/p/6013002.html
相關文章
- 布隆過濾器(Bloom Filter)過濾器OOMFilter
- 布隆過濾器 Bloom Filter過濾器OOMFilter
- Bloom Filter 布隆過濾器OOMFilter過濾器
- 布隆過濾器(Bloom Filter)詳解過濾器OOMFilter
- Xor過濾器:比布隆Bloom過濾器更快,更小過濾器OOM
- 快取問題(二) 布隆過濾器(Bloom Filter) 介紹和原理快取過濾器OOMFilter
- 布隆過濾器過濾器
- 雜湊表擴充套件—點陣圖套件
- 雜湊擴充套件攻擊套件
- 淺談布隆過濾器過濾器
- Redis-布隆過濾器Redis過濾器
- 大白話布隆過濾器過濾器
- Guava的布隆過濾器Guava過濾器
- C++雜湊應用-點陣圖/布隆過濾器/海量資料處理C++過濾器
- 布隆過濾器 與 Redis BitMap過濾器Redis
- victoriaMetrics庫之布隆過濾器過濾器
- Redis 應用-布隆過濾器Redis過濾器
- PHP實現布隆過濾器PHP過濾器
- Redis 中的布隆過濾器Redis過濾器
- 5分鐘掌握布隆過濾器過濾器
- 還有人不懂布隆過濾器嗎?過濾器
- 從快取穿透聊到布隆過濾器快取穿透過濾器
- 演算法(3)---布隆過濾器原理演算法過濾器
- 布隆過濾器-使用場景的思考過濾器
- Redis詳解(十三)------ Redis布隆過濾器Redis過濾器
- Redis布隆過濾器分析與總結Redis過濾器
- 布隆過濾器的原理及應用過濾器
- 品味布隆過濾器的設計之美過濾器
- LevelDB 學習筆記1:布隆過濾器筆記過濾器
- 詳解布隆過濾器原理與實現過濾器
- 實現布隆過濾器的三種方式過濾器
- 面試官問:什麼是布隆過濾器?面試過濾器
- Redis快取穿透解決方案--布隆過濾器Redis快取穿透過濾器
- 詳解布隆過濾器的原理和實現過濾器
- 布隆過濾器解決快取穿透問題過濾器快取穿透
- 布隆過濾器實戰【防止快取擊穿】過濾器快取
- 那些有趣的演算法之布隆過濾器演算法過濾器
- Filter過濾器Filter過濾器