Bloom Filter 介紹(Bloom Filters by Example)

基礎

Bloom filter 是一個數據結構,它可以用來判斷某個元素是否在集合內,具有運行快速,內存佔用小的特點。
而高效插入和查詢的代價就是 Bloom Filter 是一個概率數據結構: 它可以告訴我們一個元素絕對不在集合內或者可能在集合內。下面是一個簡單的示例

                             
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14

表中的每一個空格表示一個比特,下面的數字是它的索引值。如果要向 Bloom filter 添加一個元素,只需要簡單的對這個元素應用幾次哈希,然後將向量中的對應比特設置爲1。
假設現在有一個字符串 s,有兩個哈希函數 h1 和 h2,h1(s) = 5, h2(s) = 9,則在上表中的 5 和 9 的位置會被設置爲 1。

          1       1          
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14


爲了測試一個元素是否在集合中,你可以字符串應用同樣的哈希函數,然後看這些值是否被設置。如果沒有,則說明元素不在集合內;而如果有,你只能確定該元素可能在集合裏,因爲也可能是其他元素或者是其他元素的組合設置了這些比特位,但是可以通過對這個概率的控制使得 Bloom filter 達到可用的程度。

這些就是 Bloom filter 的基礎知識。

高級話題

哈希函數

Bloom filter 裏的哈希函數需要是彼此獨立的,正規的,分佈式的。同時,它們需要儘可能快的運行速度(例如 sha1 之類的密碼學哈希就不是一個特別好的選擇)。
快速簡單的哈希函數有 murmur,fnv系列和 Jenkins 哈希。

Double Hashing

同時,你不需要同時選取兩個或多個不同的哈希函數。而是使用 double Hashing 來創建無窮多的哈希函數。給定兩個彼此獨立的哈希函數 hashahashb,可以通過如下的哈希函數創建一個新的哈希函數:
hashi(x, m) = (hasha(x) + i * hashb(x)) mod m

Bloom filter 應該設計爲多大?

Bloom filter 的一個優良特性就是可以修改過濾器的錯誤率。一個大的過濾器會擁有比一個小的過濾器更低的錯誤率。
錯誤率會近似於(1 - e-kn/m)k,所以你只需要先確定可能插入的數據集的容量大小,然後再對km進行參數調優。
而這帶來了一個顯而易見的問題。

應該使用多少個哈希函數?

Bloom filter 使用的哈希函數越多運行速度就會越慢。但是如果哈希函數過少,又會遇到誤判率高的問題。
在創建一個 Bloom filter 的時候需要確定k的值,也就是說你需要提前圈定n的變動範圍。而一旦你這樣做了,你依然需要確定m(總比特數)和k(哈希函數的個數)的值。
似乎這是一個十分困難的優化問題,但幸運的是,對於給定的mn,有一個函數可以幫我們確定最優的k值:(m/n)ln(2)
所以可以通過以下的步驟來確定 Bloom filter 的大小:
1. 確定n的變動範圍
2. 確定m的值
3. 計算k的最優值
4. 對於給定的n, m, k計算錯誤率。如果這個錯誤率不能接收,那麼回到第二步,否則結束

Bloom filter 的時間複雜度和空間複雜度

對於一個mk值確定的 Bloom filter,插入和測試操作的時間複雜度都是 O(k)。這意味着每次你想要插入一個元素或者查詢一個元素是否在集合中,只需要使用k個哈希函數對這個元素求值,然後將對應的比特位標記或者檢查對應的比特位。
相比之下,Bloom filter 的空間複雜度更難以概述,它取決於你可以忍受的錯誤率。同時也取決於輸入元素的範圍,如果這個範圍是有限的,那麼一個確定的比特向量就可以很好的解決問題。如果你甚至不能很好的估計輸入元素的範圍,那麼你最好選擇一個哈希表或者一個可拓展的 Bloom filter。

更多鏈接

  1. 中文版 Github 鏈接
  2. Bloom Filters by Examples
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章