Bloom Filter的中文翻譯叫做布隆過濾器,是1970年由布隆提出的。它實際上是一個很長的二進制向量和一系列隨機映射函數。布隆過濾器可以用於檢索一個元素是否在一個集合中。它的優點是空間效率和查詢時間都遠遠超過一般的算法,缺點是有一定的誤識別率和刪除困難。如文章標題所述,本文只是做簡單介紹,屬於科普文章。
應用場景
在正式介紹Bloom Filter算法之前,先來看看什么時候需要用到Bloom Filter算法。
1. HTTP緩存服務器、Web爬蟲等
主要工作是判斷一條URL是否在現有的URL集合之中(可以認為這里的數據量級上億)。
對於HTTP緩存服務器,當本地局域網中的PC發起一條HTTP請求時,緩存服務器會先查看一下這個URL是否已經存在於緩存之中,如果存在的話就沒有必要去原始的服務器拉取數據了(為了簡單起見,我們假設數據沒有發生變化),這樣既能節省流量,還能加快訪問速度,以提高用戶體驗。
對於Web爬蟲,要判斷當前正在處理的網頁是否已經處理過了,同樣需要當前URL是否存在於已經處理過的URL列表之中。
2. 垃圾郵件過濾
假設郵件服務器通過發送方的郵件域或者IP地址對垃圾郵件進行過濾,那么就需要判斷當前的郵件域或者IP地址是否處於黑名單之中。如果郵件服務器的通信郵件數量非常大(也可以認為數據量級上億),那么也可以使用Bloom Filter算法。
幾個專業術語
這里有必要介紹一下False Positive和False Negative的概念(更形象的描述可以閱讀第4條參考)。
False Positive中文可以理解為“假陽性”,形象的一點說就是“誤報”,后面將會說道Bloom Filter存在誤報的情況,現實生活中也有誤報,比如說去體檢的時候,醫生告訴你XXX檢測是陽性,而實際上是陰性,也就是說誤報了,是假陽性,殺毒軟件誤報也是同樣的概念。
False Negative,中文可以理解為“假陰性”,形象的一點說是“漏報”。醫生告訴你XXX檢測為陰性,實際上你是陽性,你是有病的(Sorry, it’s just a joke),那就是漏報了。同樣殺毒軟件也存在漏報的情況。
Bloom Filter算法
好了,終於要正式介紹Bloom Filter算法了。
初始狀態下,Bloom Filter是一個m位的位數組,且數組被0所填充。同時,我們需要定義k個不同的hash函數,每一個hash函數都隨機的將每一個輸入元素映射到位數組中的一個位上。那么對於一個確定的輸入,我們會得到k個索引。
插入元素:經過k個hash函數的映射,我們會得到k個索引,我們把位數組中這k個位置全部置1(不管其中的位之前是0還是1)
查詢元素:輸入元素經過k個hash函數的映射會得到k個索引,如果位數組中這k個索引任意一處是0,那么就說明這個元素不在集合之中;如果元素處於集合之中,那么當插入元素的時候這k個位都是1。但如果這k個索引處的位都是1,被查詢的元素就一定在集合之中嗎?答案是不一定,也就是說出現了False Positive的情況(但Bloom Filter不會出現False Negative的情況)
在上圖中,當插入x、y、z這三個元素之后,再來查詢w,會發現w不在集合之中,而如果w經過三個hash函數計算得出的結果所得索引處的位全是1,那么Bloom Filter就會告訴你,w在集合之中,實際上這里是誤報,w並不在集合之中。
False Positive Rate
Bloom Filter的誤報率到底有多大?下面在數學上進行一番推敲。假設HASH函數輸出的索引值落在m位的數組上的每一位上都是等可能的。那么,對於一個給定的HASH函數,在進行某一個運算的時候,一個特定的位沒有被設置為1的概率是![]()
那么,對於所有的k個HASH函數,都沒有把這個位設置為1的概率是
如果我們已經插入了n個元素,那么對於一個給定的位,這個位仍然是0的概率是
那么,如果插入n個元素之后,這個位是1的概率是
如果對一個特定的元素存在誤報,那么這個元素的經過HASH函數所得到的k個索引全部都是1,概率也就是
根據常數e的定義,可以近似的表示為:
關於誤報
有時候誤報對實際操作並不會帶來太大的影響,比如對於HTTP緩存服務器,如果一條URL被誤以為存在與緩存服務器之中,那么當取數據的時候自然會無法取到,最終還是要從原始服務器當中獲取,之后再把記錄插入緩存服務器,幾乎沒有什么不可以接受的。
對於安全軟件,有着“另可錯報,不可誤報”的說法,如果你把一個正常軟件誤判為病毒,對使用者來說不會有什么影響(如果用戶相信是病毒,那么就是刪除這個文件罷了,如果用戶執意要執行,那么后果也只能由用戶來承擔);如果你把一個病毒漏判了,那么對用戶造成的后果是不可設想的……更有甚者,誤報在某種程度上能讓部分用戶覺得你很專業……
最優的哈希函數個數
既然Bloom Filter要靠多個哈希函數將集合映射到位數組中,那么應該選擇幾個哈希函數才能使元素查詢時的錯誤率降到最低呢?這里有兩個互斥的理由:如果哈希函數的個數多,那么在對一個不屬於集合的元素進行查詢時得到0的概率就大;但另一方面,如果哈希函數的個數少,那么位數組中的0就多。為了得到最優的哈希函數個數,我們需要根據上一小節中的錯誤率公式進行計算。
先用p和f進行計算。注意到f = exp(k ln(1 − e−kn/m)),我們令g = k ln(1 − e−kn/m),只要讓g取到最小,f自然也取到最小。由於p = e-kn/m,我們可以將g寫成
根據對稱性法則可以很容易看出當p = 1/2,也就是k = ln2· (m/n)時,g取得最小值。在這種情況下,最小錯誤率f等於(1/2)k ≈ (0.6185)m/n。另外,注意到p是位數組中某一位仍是0的概率,所以p = 1/2對應着位數組中0和1各一半。換句話說,要想保持錯誤率低,最好讓位數組有一半還空着。
需要強調的一點是,p = 1/2時錯誤率最小這個結果並不依賴於近似值p和f。同樣對於f’ = exp(k ln(1 − (1 − 1/m)kn)),g’ = k ln(1 − (1 − 1/m)kn),p’ = (1 − 1/m)kn,我們可以將g’寫成
同樣根據對稱性法則可以得到當p’ = 1/2時,g’取得最小值。
位數組的大小
下面我們來看看,在不超過一定錯誤率的情況下,Bloom Filter至少需要多少位才能表示全集中任意n個元素的集合。假設全集中共有u個元素,允許的最大錯誤率為є,下面我們來求位數組的位數m。
假設X為全集中任取n個元素的集合,F(X)是表示X的位數組。那么對於集合X中任意一個元素x,在s = F(X)中查詢x都能得到肯定的結果,即s能夠接受x。顯然,由於Bloom Filter引入了錯誤,s能夠接受的不僅僅是X中的元素,它還能夠є (u - n)個false positive。因此,對於一個確定的位數組來說,它能夠接受總共n + є (u - n)個元素。在n + є (u - n)個元素中,s真正表示的只有其中n個,所以一個確定的位數組可以表示
個集合。m位的位數組共有2m個不同的組合,進而可以推出,m位的位數組可以表示
個集合。全集中n個元素的集合總共有
個,因此要讓m位的位數組能夠表示所有n個元素的集合,必須有
即:
上式中的近似前提是n和єu相比很小,這也是實際情況中常常發生的。根據上式,我們得出結論:在錯誤率不大於є的情況下,m至少要等於n log2(1/є)才能表示任意n個元素的集合。
上一小節中我們曾算出當k = ln2· (m/n)時錯誤率f最小,這時f = (1/2)k = (1/2)mln2 / n。現在令f≤є,可以推出
這個結果比前面我們算得的下界n log2(1/є)大了log2 e ≈ 1.44倍。這說明在哈希函數的個數取到最優時,要讓錯誤率不超過є,m至少需要取到最小值的1.44倍。
總結
在計算機科學中,我們常常會碰到時間換空間或者空間換時間的情況,即為了達到某一個方面的最優而犧牲另一個方面。Bloom Filter在時間空間這兩個因素之外又引入了另一個因素:錯誤率。在使用Bloom Filter判斷一個元素是否屬於某個集合時,會有一定的錯誤率。也就是說,有可能把不屬於這個集合的元素誤認為屬於這個集合(False Positive),但不會把屬於這個集合的元素誤認為不屬於這個集合(False Negative)。在增加了錯誤率這個因素之后,Bloom Filter通過允許少量的錯誤來節省大量的存儲空間。
自從Burton Bloom在70年代提出Bloom Filter之后,Bloom Filter就被廣泛用於拼寫檢查和數據庫系統中。近一二十年,伴隨着網絡的普及和發展,Bloom Filter在網絡領域獲得了新生,各種Bloom Filter變種和新的應用不斷出現。可以預見,隨着網絡應用的不斷深入,新的變種和應用將會繼續出現,Bloom Filter必將獲得更大的發展。
Counting Bloom Filter
從前面對Bloom Filter的介紹可以看出,標准的Bloom Filter是一種很簡單的數據結構,它只支持插入和查找兩種操作。在所要表達的集合是靜態集合的時候,標准Bloom Filter可以很好地工作,但是如果要表達的集合經常變動,標准Bloom Filter的弊端就顯現出來了,因為它不支持刪除操作。
Counting Bloom Filter的出現解決了這個問題,它將標准Bloom Filter位數組的每一位擴展為一個小的計數器(Counter),在插入元素時給對應的k(k為哈希函數個數)個Counter的值分別加1,刪除元素時給對應的k個Counter的值分別減1。Counting Bloom Filter通過多占用幾倍的存儲空間的代價,給Bloom Filter增加了刪除操作。下一個問題自然就是,到底要多占用幾倍呢?
我們先計算第i個Counter被增加j次的概率,其中n為集合元素個數,k為哈希函數個數,m為Counter個數(對應着原來位數組的大小):
上面等式右端的表達式中,前一部分表示從nk次哈希中選擇j次,中間部分表示j次哈希都選中了第i個Counter,后一部分表示其它nk – j次哈希都沒有選中第i個Counter。因此,第i個Counter的值大於j的概率可以限定為:
上式第二步縮放中應用了估計階乘的斯特林公式:
在Bloom Filter概念和原理一文中,我們提到過k的最優值為(ln2)m/n,現在我們限制k ≤ (ln2)m/n,就可以得到如下結論:
如果每個Counter分配4位,那么當Counter的值達到16時就會溢出。這個概率為:
這個值足夠小,因此對於大多數應用程序來說,4位就足夠了。
參考文檔
http://zh.wikipedia.org/wiki/Bloom_filter
http://en.wikipedia.org/wiki/Bloom_filter
http://www.cnblogs.com/yuyijq/archive/2012/02/08/2343374.html
http://simon.blog.51cto.com/80/73395/
A. Broder and M. Mitzenmacher. Network applications of bloom filters: A survey. Internet Mathematics, 1(4):485–509, 2005.
M. Mitzenmacher. Compressed Bloom Filters. IEEE/ACM Transactions on Networking 10:5 (2002), 604—612.
www.cs.jhu.edu/~fabian/courses/CS600.624/slides/bloomslides.pdf
http://my.oschina.net/kiwivip/blog/133498














