Bloom Filter概念和原理

一提到元素查找,我們會很自然的想到HashMap。通過將哈希函數作用於key上,我們得到了哈希值,基於哈希值我們可以去表裏的相應位置獲取對應的數據。除了存在哈希衝突問題之外,HashMap一個很大的問題就是空間效率低。引入Bloom Filter則可以很好的解決空間效率的問題。

原理

Bloom Filter是一種空間效率很高的隨機數據結構,Bloom filter 可以看做是對bit-map 的擴展,布隆過濾器被設計爲一個具有N的元素的位數組A(bit array),初始時所有的位都置爲0。

當一個元素被加入集合時,通過K個Hash函數將這個元素映射成一個位陣列(Bit array)中的K個點,把它們置爲1。檢索時,我們只要看看這些點是不是都是1就(大約)知道集合中有沒有它了。

  • 如果這些點有任何一個 0,則被檢索元素一定不在;
  • 如果都是 1,則被檢索元素很可能在。

添加元素

要添加一個元素,我們需要提供k個哈希函數。每個函數都能返回一個值,這個值必須能夠作爲位數組的索引(可以通過對數組長度進行取模得到)。然後,我們把位數組在這個索引處的值設爲1。例如,第一個哈希函數作用於元素I上,返回x。類似的,第二個第三個哈希函數返回y與z,那麼:
A[x]=A[y]=A[z] = 1

查找元素

查找的過程與上面的過程類似,元素將會被會被不同的哈希函數處理三次,每個哈希函數都返回一個作爲位數組索引值的整數,然後我們檢測位數組在x、y與z處的值是否爲1。如果有一處不爲1,那麼就說明這個元素沒有被添加到這個布隆過濾器中。如果都爲1,就說明這個元素在布隆過濾器裏面。當然,會有一定誤判的概率。

算法優化

通過上面的解釋我們可以知道,如果想設計出一個好的布隆過濾器,我們必須遵循以下準則:

  • 好的哈希函數能夠儘可能的返回寬範圍的哈希值。
  • 位數組的大小(用m表示)非常重要:如果太小,那麼所有的位很快就都會被賦值爲1,這樣就增加了誤判的機率。
  • 哈希函數的個數(用k表示)對索引值的均勻分配也很重要。

計算m的公式如下:
m = - nlog p / (log2)^2
這裏p爲可接受的誤判率。

計算k的公式如下:
k = m/n log(2)
這裏k=哈希函數個數,m=位數組個數,n=待檢測元素的個數(後面會用到這幾個字母)。

哈希算法

哈希算法是影響布隆過濾器性能的地方。我們需要選擇一個效率高但不耗時的哈希函數,在論文《更少的哈希函數,相同的性能指標:構造一個更好的布隆過濾器》中,討論瞭如何選用2個哈希函數來模擬k個哈希函數。首先,我們需要計算兩個哈希函數h1(x)與h2(x)。然後,我們可以用這兩個哈希函數來模仿產生k個哈希函數的效果:
gi(x) = h1(x) + ih2(x)
這裏i的取值範圍是1到k的整數。

Google Guava類庫使用這個技巧實現了一個布隆過濾器,哈希算法的主要邏輯如下:

long hash64 = ...;
int hash1 = (int) hash64;
int hash2 = (int) (hash64 >>> 32);

for (int i = 1; i <= numHashFunctions; i++) {
  int combinedHash = hash1 + (i * hash2);
  // Flip all the bits if it's negative (guaranteed positive number)
  if (combinedHash < 0) {
    combinedHash = ~combinedHash;
  }
}

Guava中的Bloom Filter使用示例:

int expectedInsertions = ...; //待檢測元素的個數
double fpp = 0.03; //誤判率(desired false positive probability)
BloomFilter<CharSequence> bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.forName("UTF-8")), expectedInsertions,fpp);

實現

https://github.com/lsyf/bloomfilter

優點

它的優點是空間效率和查詢時間都遠遠超過一般的算法,布隆過濾器存儲空間和插入/查詢時間都是常數O(k)。另外, 散列函數相互之間沒有關係,方便由硬件並行實現。布隆過濾器不需要存儲元素本身,在某些對保密要求非常嚴格的場合有優勢。

缺點

布隆過濾器的缺點和優點一樣明顯,誤算率是其中之一。

另外,一般情況下不能從布隆過濾器中刪除元素。我們很容易想到把位數組變成整數數組,每插入一個元素相應的計數器加 1,這樣刪除元素時將計數器減掉就可以了。然而要保證安全地刪除元素並非如此簡單。首先我們必須保證刪除的元素的確在布隆過濾器裏面,而這一點單憑這個過濾器是無法保證的。

參考來源:
[1] https://blog.csdn.net/jiaomeng/article/details/1495500

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章