C++ BloomFilter布隆過(guò)濾器應(yīng)用及概念詳解
一、布隆過(guò)濾器概念
布隆過(guò)濾器是由布?。˙urton Howard Bloom)在1970年提出的 一種緊湊型的、比較巧妙的概率型數(shù)據(jù)結(jié)構(gòu),特點(diǎn)是高效地插入和查詢,可以用來(lái)告訴你 “某樣?xùn)|西一定不存在或者可能存在”,它是用多個(gè)哈希函數(shù),將一個(gè)數(shù)據(jù)映射到位圖結(jié)構(gòu)中。此種方式不僅可以提升查詢效率,也可以節(jié)省大量的內(nèi)存空間 .
位圖的優(yōu)點(diǎn)是節(jié)省空間,快,缺點(diǎn)是要求范圍相對(duì)集中,如果范圍分散,空間消耗上升,同時(shí)只能針對(duì)整型,字符串通過(guò)哈希轉(zhuǎn)化成整型,再去映射,對(duì)于整型沒(méi)有沖突,因?yàn)檎褪怯邢薜模成湮ㄒ坏奈恢?,但是?duì)于字符串來(lái)說(shuō),是無(wú)限的,會(huì)發(fā)生沖突,會(huì)發(fā)生誤判:此時(shí)的情況的是不在是正確的,在是不正確的,因?yàn)榭赡懿粊?lái)是不在的,但是位置跟別人發(fā)生沖突,發(fā)生誤判
此時(shí)布隆過(guò)濾器就登場(chǎng)了,可以降低誤判率:讓一個(gè)值映射多個(gè)位置,但是并不是消除誤判

可能還是會(huì)出現(xiàn)誤判:

雖然布隆過(guò)濾器還是會(huì)出現(xiàn)誤判,因?yàn)檫@個(gè)數(shù)據(jù)的比特位被其他數(shù)據(jù)所占,但是判斷一個(gè)數(shù)據(jù)不存在是準(zhǔn)確,不存在就是0!
布隆過(guò)濾器改進(jìn):映射多個(gè)位置,降低誤判率(位置越多,消耗也越多)
如果布隆過(guò)濾器長(zhǎng)度比較小,比特位很快會(huì)被占為1,誤判率自然會(huì)上升,所以布隆過(guò)濾器的長(zhǎng)度會(huì)影響誤判率,理論上來(lái)說(shuō),如果一個(gè)值映射的位置越多,則誤判的概率越小,但是并不是位置越多越好,空間也會(huì)消耗:大佬們自然也能夠想得到,所以有公式:

我們可以來(lái)估算一下,假設(shè)用 3 個(gè)哈希函數(shù),即K=3,ln2 的值我們?nèi)?0.7,那么 m 和 n 的關(guān)系大概是 m = n×k/ln2=4.2n ,也就是過(guò)濾器長(zhǎng)度應(yīng)該是插入元素個(gè)數(shù)的 4 -5倍
二、布隆過(guò)濾器應(yīng)用
不需要一定準(zhǔn)確的場(chǎng)景。比如游戲注冊(cè)時(shí)候的昵稱的判重:如果不在那就是不在,沒(méi)被使用,在的話可能會(huì)被誤判。
提高查找效率:客戶端中查找一個(gè)用戶的ID與服務(wù)器中的是否相同,在增加一層布隆過(guò)濾器提高查找效率:

三、布隆過(guò)濾器實(shí)現(xiàn)
布隆過(guò)濾器的插入元素可能是字符串,也可能是其他類型,只要提供對(duì)應(yīng)的哈希函數(shù)將該類型的數(shù)據(jù)轉(zhuǎn)換成整型就可以了。
一般情況下布隆過(guò)濾器都是用來(lái)處理字符串的,所以布隆過(guò)濾器可以實(shí)現(xiàn)為一個(gè)模板類,將模板參數(shù) T 的缺省類型設(shè)置為 string:
template <size_t N,size_t X = 5,class K=string,
class HashFunc1 = BKDRHash,
class HashFunc2 = APHash,
class HashFunc3 = DJBHash>
class BloomFilter
{
public:
private:
bitset<N * X> _bs;
};
這里布隆過(guò)濾器提供三個(gè)哈希函數(shù),由于布隆過(guò)濾器一般處理的是字符串類型的數(shù)據(jù),所以我們默認(rèn)提供幾個(gè)將字符串轉(zhuǎn)換成整型的哈希函數(shù):選取綜合評(píng)分最高的 BKDRHash、APHash 和 DJBHash這三種哈希算法:
struct BKDRHash
{
size_t operator()(const string& key)
{
size_t hash = 0;
for (auto ch : key)
{
hash *= 131;
hash += ch;
}
return hash;
}
};
struct APHash
{
size_t operator()(const string& key)
{
size_t hash = 0;
int i = 0;
for (auto ch : key)
{
if ((i & 1) == 0)
{
hash ^= ((hash << 7) ^ (ch) ^ (hash >> 3));
}
else
{
hash ^= (~((hash << 11) ^ (ch) ^ (hash >> 5)));
}
++i;
}
return hash;
}
};
struct DJBHash
{
size_t operator()(const string& key)
{
size_t hash = 5318;
for (auto ch : key)
{
hash += (hash << 5) + ch;
}
return hash;
}
};
1.插入
布隆過(guò)濾器復(fù)用bitset的 set 接口用于插入元素,插入元素時(shí),我們通過(guò)上面的三個(gè)哈希函數(shù)分別計(jì)算出該元素對(duì)應(yīng)的三個(gè)比特位,然后在位圖中設(shè)置為1即可:
void set(const K& key)
{
size_t hash1 = HashFunc1()(key) % (N * X);
size_t hash2 = HashFunc2()(key) % (N * X);
size_t hash3 = HashFunc3()(key) % (N * X);
_bs.set(hash1);
_bs.set(hash2);
_bs.set(hash3);
_bs.set(hash4);
}
2.查找
通過(guò)三個(gè)哈希函數(shù)分別算出對(duì)應(yīng)元素的三個(gè)哈希地址,得到對(duì)應(yīng)的比特位,然后去判斷這三個(gè)比特位是否都被設(shè)置成了1
如果出現(xiàn)一個(gè)比特位未被設(shè)置成1說(shuō)明該元素一定不存在,也就是如果一個(gè)比特位為0就是false;而如果三個(gè)比特位全部都被設(shè)置,則return true表示該元素已經(jīng)存在(注:可能會(huì)出現(xiàn)誤判)
bool test(const K& key)
{
size_t hash1 = HashFunc1()(key) % (N * X);
if (!_bs.test(hash1))
{
return false;
}
size_t hash2 = HashFunc2()(key) % (N * X);
if (!_bs.test(hash2))
{
return false;
}
size_t hash3 = HashFunc3()(key) % (N * X);
if (!_bs.test(hash3))
{
return false;
}
return true;
}
3.刪除
布隆過(guò)濾器一般沒(méi)有刪除,因?yàn)椴悸∵^(guò)濾器判斷一個(gè)元素是會(huì)存在誤判,此時(shí)無(wú)法保證要?jiǎng)h除的元素在布隆過(guò)濾器中,如果此時(shí)將位圖中對(duì)應(yīng)的比特位清0,就會(huì)影響到其他元素了:

這時(shí)候我們只需要在每個(gè)比特位加一個(gè)計(jì)數(shù)器,當(dāng)存在插入操作時(shí),在計(jì)數(shù)器里面進(jìn)行 ++,刪除后對(duì)該位置進(jìn)行 -- 即可

但是布隆過(guò)濾器的本來(lái)目的就是為了提高效率和節(jié)省空間,在每個(gè)比特位增加額外的計(jì)數(shù)器,空間消耗那就更多了
四、布隆過(guò)濾器優(yōu)缺
優(yōu)
\1. 增加和查詢?cè)氐臅r(shí)間復(fù)雜度為:O(K), (K為哈希函數(shù)的個(gè)數(shù),一般比較小),與數(shù)據(jù)量大小無(wú)關(guān)
\2. 哈希函數(shù)相互之間沒(méi)有關(guān)系,方便硬件并行運(yùn)算
\3. 布隆過(guò)濾器不需要存儲(chǔ)元素本身,在某些對(duì)保密要求比較嚴(yán)格的場(chǎng)合有很大優(yōu)勢(shì)
\4. 在能夠承受一定的誤判時(shí),布隆過(guò)濾器比其他數(shù)據(jù)結(jié)構(gòu)有這很大的空間優(yōu)勢(shì)
\5. 數(shù)據(jù)量很大時(shí),布隆過(guò)濾器可以表示全集,其他數(shù)據(jù)結(jié)構(gòu)不能
\6. 使用同一組散列函數(shù)的布隆過(guò)濾器可以進(jìn)行交、并、差運(yùn)算
缺
\1. 有誤判率,不能準(zhǔn)確判斷元素是否在集合中(補(bǔ)救方法:再建立一個(gè)白名單,存儲(chǔ)可能會(huì)誤判的數(shù)據(jù))
\2. 不能獲取元素本身
\3. 一般情況下不能從布隆過(guò)濾器中刪除元素
五、結(jié)語(yǔ)
給兩個(gè)文件,分別有100億個(gè)query,我們只有1G內(nèi)存,如何找到兩個(gè)文件交集?分別給出精確算法和近似算法?
近似算法:利用布隆過(guò)濾器,交集的就一定會(huì)進(jìn)去,但是可能會(huì)存在誤判:不同的也會(huì)進(jìn)去,這是近似
精準(zhǔn)算法:query一般是查詢指令,比如可能是網(wǎng)絡(luò)請(qǐng)求,或者是一個(gè)數(shù)據(jù)庫(kù)sql語(yǔ)句
100億個(gè)query,假設(shè)平均每個(gè)query是50byte,則100億個(gè)query那就是合計(jì)500GB
相同的query,是一定進(jìn)入相同編號(hào)的小文件,再對(duì)這些文件放進(jìn)內(nèi)存的兩個(gè)set中,編號(hào)相同的Ai和Bi小文件找交集即可

到此這篇關(guān)于C++ BloomFilter布隆過(guò)濾器應(yīng)用及概念詳解的文章就介紹到這了,更多相關(guān)C++ BloomFilter內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
VScode + keil開發(fā)環(huán)境搭建安裝使用過(guò)程
這篇文章主要介紹了VScode + keil開發(fā)環(huán)境搭建及安裝使用過(guò)程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-07-07
C++靜態(tài)庫(kù)與動(dòng)態(tài)庫(kù)文件的生成和使用教程
庫(kù)文件是計(jì)算機(jī)上的一類文件,可以簡(jiǎn)單的把庫(kù)文件看成一種代碼倉(cāng)庫(kù),它提供給使用者一些可以直接拿來(lái)用的變量、函數(shù)和類,下面這篇文章主要給大家介紹了關(guān)于C++靜態(tài)庫(kù)與動(dòng)態(tài)庫(kù)文件的生成和使用的相關(guān)資料,需要的朋友可以參考下2023-03-03
C語(yǔ)言實(shí)現(xiàn)二叉樹遍歷的迭代算法
這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)二叉樹遍歷的迭代算法,包括二叉樹的中序遍歷、先序遍歷及后序遍歷等,是非常經(jīng)典的算法,需要的朋友可以參考下2014-09-09
C語(yǔ)言的語(yǔ)法風(fēng)格與代碼書寫規(guī)范指南
這篇文章主要介紹了C語(yǔ)言的語(yǔ)法風(fēng)格與代碼書寫規(guī)范指南,文中主張了一些諸如變量和結(jié)構(gòu)體的命名規(guī)范等細(xì)節(jié)方面的問(wèn)題,需要的朋友可以參考下2016-02-02
關(guān)于虛函數(shù)實(shí)現(xiàn)多態(tài)的原理及分析
這篇文章主要介紹了C++中如何實(shí)現(xiàn)多態(tài)問(wèn)題,具有很好的參考價(jià)值,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02

