# Bloom Filter
## 方法介紹
### 一、什么是Bloom Filter
Bloom Filter,被譯作稱布隆過濾器,是一種空間效率很高的隨機數據結構,Bloom filter可以看做是對bit-map的擴展,它的原理是:
- 當一個元素被加入集合時,通過K個Hash函數將這個元素映射成一個位陣列(Bit array)中的K個點,把它們置為1**。檢索時,我們只要看看這些點是不是都是1就(大約)知道集合中有沒有它了:
- 如果這些點有任何一個0,則被檢索元素一定不在;
- 如果都是1,則被檢索元素很可能在。
其可以用來實現數據字典,進行數據的判重,或者集合求交集。
但Bloom Filter的這種高效是有一定代價的:在判斷一個元素是否屬于某個集合時,有可能會把不屬于這個集合的元素誤認為屬于這個集合(false positive)。因此,Bloom Filter不適合那些“零錯誤”的應用場合。而在能容忍低錯誤率的應用場合下,Bloom Filter通過極少的錯誤換取了存儲空間的極大節省。
#### 1.1、集合表示和元素查詢
下面我們具體來看Bloom Filter是如何用位數組表示集合的。初始狀態時,Bloom Filter是一個包含m位的位數組,每一位都置為0。

為了表達S={x<sub>1</sub>, x<sub>2</sub>,…,x<sub>n</sub>}這樣一個n個元素的集合,Bloom Filter使用k個相互獨立的哈希函數(Hash Function),它們分別將集合中的每個元素映射到{1,…,m}的范圍中。對任意一個元素x,第i個哈希函數映射的位置h<sub>i</sub>(x)就會被置為1(1≤i≤k)。注意,如果一個位置多次被置為1,那么只有第一次會起作用,后面幾次將沒有任何效果。在下圖中,k=3,且有兩個哈希函數選中同一個位置(從左邊數第五位,即第二個“1“處)。

在判斷y是否屬于這個集合時,我們對y應用k次哈希函數,如果所有h<sub>i</sub>(y)的位置都是1(1≤i≤k),那么我們就認為y是集合中的元素,否則就認為y不是集合中的元素。下圖中y<sub>1</sub>就不是集合中的元素(因為y1有一處指向了“0”位)。y<sub>2</sub>或者屬于這個集合,或者剛好是一個false positive。

#### 1.2、錯誤率估計
前面我們已經提到了,Bloom Filter在判斷一個元素是否屬于它表示的集合時會有一定的錯誤率(false positive rate),下面我們就來估計錯誤率的大小。在估計之前為了簡化模型,我們假設kn<m且各個哈希函數是完全隨機的。當集合S={x<sub>1</sub>, x<sub>2</sub>,…,x<sub>n</sub>}的所有元素都被k個哈希函數映射到m位的位數組中時,這個位數組中某一位還是0的概率是:
^{kn}\\approx e^{-kn/m})
其中1/m表示任意一個哈希函數選中這一位的概率(前提是哈希函數是完全隨機的),(1-1/m)表示哈希一次沒有選中這一位的概率。要把S完全映射到位數組中,需要做kn次哈希。某一位還是0意味著kn次哈希都沒有選中它,因此這個概率就是(1-1/m)的kn次方。令p = e<sup>-kn/m</sup>是為了簡化運算,這里用到了計算e時常用的近似:
^{-x}=e)
令ρ為位數組中0的比例,則ρ的數學期望E(ρ)= p’。在ρ已知的情況下,要求的錯誤率(false positive rate)為:
^k\\approx(1-p')^k\\approx(1-p)^k)
(1-ρ)為位數組中1的比例,(1-ρ)<sup>k</sup>就表示k次哈希都剛好選中1的區域,即false positive rate。上式中第二步近似在前面已經提到了,現在來看第一步近似。p’只是ρ的數學期望,在實際中ρ的值有可能偏離它的數學期望值。M. Mitzenmacher已經證明<sup>[2]</sup> ,位數組中0的比例非常集中地分布在它的數學期望值的附近。因此,第一步的近似得以成立。分別將p和p’代入上式中,得:
^{kn}\\right)^k=(1-p')^k)
^k=(1-p)^k)
相比p’和f’,使用p和f通常在分析中更為方便。
#### 1.3、最優的哈希函數個數
既然Bloom Filter要靠多個哈希函數將集合映射到位數組中,那么應該選擇幾個哈希函數才能使元素查詢時的錯誤率降到最低呢?這里有兩個互斥的理由:如果哈希函數的個數多,那么在對一個不屬于集合的元素進行查詢時得到0的概率就大;但另一方面,如果哈希函數的個數少,那么位數組中的0就多。為了得到最優的哈希函數個數,我們需要根據上一小節中的錯誤率公式進行計算。
先用p和f進行計算。注意到f = exp(k ln(1 ? e<sup>?kn/m</sup>)),我們令g = k ln(1 ? e<sup>?kn/m</sup>),只要讓g取到最小,f自然也取到最小。由于p = e<sup>-kn/m</sup>,我們可以將g寫成
\\ln(1-p))
根據對稱性法則可以很容易看出當p = 1/2,也就是k = ln2· (m/n)時,g取得最小值。在這種情況下,最小錯誤率f等于(1/2)<sup>k</sup>≈ (0.6185)<sup>m/n</sup>。另外,注意到p是位數組中某一位仍是0的概率,所以p = 1/2對應著位數組中0和1各一半。換句話說,要想保持錯誤率低,最好讓位數組有一半還空著。
需要強調的一點是,p = 1/2時錯誤率最小這個結果并不依賴于近似值p和f。同樣對于f’ = exp(k ln(1 ? (1 ? 1/m)<sup>kn</sup>)),g’ = k ln(1 ? (1 ? 1/m)<sup>kn</sup>),p’ = (1 ? 1/m)<sup>kn</sup>,我們可以將g’寫成
}\\ln(p')\\ln(1-p'))
同樣根據對稱性法則可以得到當p’ = 1/2時,g’取得最小值。
#### 1.4、位數組的大小
下面我們來看看,在不超過一定錯誤率的情況下,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個,所以一個確定的位數組可以表示
}^n)
個集合。m位的位數組共有2<sup>m</sup>個不同的組合,進而可以推出,m位的位數組可以表示
}^n)
個集合。全集中n個元素的集合總共有

個,因此要讓m位的位數組能夠表示所有n個元素的集合,必須有
}^n\\geq C_{u}^n)
即:
}^n}\\approx\\log_2\\frac{C_{u}^n}{C_{\\epsilon u}^n}\\geq\\log_2\\epsilon^{-n}=n\\log_2(1/\\epsilon))
上式中的近似前提是n和?u相比很小,這也是實際情況中常常發生的。根據上式,我們得出結論:在錯誤率不大于?的情況下,m至少要等于n log<sub>2</sub>(1/?)才能表示任意n個元素的集合。
上一小節中我們曾算出當k = ln2· (m/n)時錯誤率f最小,這時f = (1/2)<sup>k</sup>= (1/2)<sup>mln2 / n</sup>。現在令f≤?,可以推出
}{\\ln 2}=n\\log_2\\log_2(1/\\epsilon))
這個結果比前面我們算得的下界n log<sub>2</sub>(1/?)大了log<sub>2</sub>e≈ 1.44倍。這說明在哈希函數的個數取到最優時,要讓錯誤率不超過?,m至少需要取到最小值的1.44倍。
## 問題實例
**1、給你A,B兩個文件,各存放50億條URL,每條URL占用64字節,內存限制是4G,讓你找出A,B文件共同的URL。如果是三個乃至n個文件呢?**
**分析**:如果允許有一定的錯誤率,可以使用Bloom filter,4G內存大概可以表示340億bit。將其中一個文件中的url使用Bloom filter映射為這340億bit,然后挨個讀取另外一個文件的url,檢查是否與Bloom filter,如果是,那么該url應該是共同的url(注意會有一定的錯誤率)。”
- 程序員如何準備面試中的算法
- 第一部分 數據結構
- 第一章 字符串
- 1.0 本章導讀
- 1.1 旋轉字符串
- 1.2 字符串包含
- 1.3 字符串轉換成整數
- 1.4 回文判斷
- 1.5 最長回文子串
- 1.6 字符串的全排列
- 1.10 本章習題
- 第二章 數組
- 2.0 本章導讀
- 2.1 尋找最小的 k 個數
- 2.2 尋找和為定值的兩個數
- 2.3 尋找和為定值的多個數
- 2.4 最大連續子數組和
- 2.5 跳臺階
- 2.6 奇偶排序
- 2.7 荷蘭國旗
- 2.8 矩陣相乘
- 2.9 完美洗牌
- 2.15 本章習題
- 第三章 樹
- 3.0 本章導讀
- 3.1 紅黑樹
- 3.2 B樹
- 3.3 最近公共祖先LCA
- 3.10 本章習題
- 第二部分 算法心得
- 第四章 查找匹配
- 4.1 有序數組的查找
- 4.2 行列遞增矩陣的查找
- 4.3 出現次數超過一半的數字
- 第五章 動態規劃
- 5.0 本章導讀
- 5.1 最大連續乘積子串
- 5.2 字符串編輯距離
- 5.3 格子取數
- 5.4 交替字符串
- 5.10 本章習題
- 第三部分 綜合演練
- 第六章 海量數據處理
- 6.0 本章導讀
- 6.1 關聯式容器
- 6.2 分而治之
- 6.3 simhash算法
- 6.4 外排序
- 6.5 MapReduce
- 6.6 多層劃分
- 6.7 Bitmap
- 6.8 Bloom filter
- 6.9 Trie樹
- 6.10 數據庫
- 6.11 倒排索引
- 6.15 本章習題
- 第七章 機器學習
- 7.1 K 近鄰算法
- 7.2 支持向量機
- 附錄 更多題型
- 附錄A 語言基礎
- 附錄B 概率統計
- 附錄C 智力邏輯
- 附錄D 系統設計
- 附錄E 操作系統
- 附錄F 網絡協議
- sift算法
- sift算法的編譯與實現
- 教你一步一步用c語言實現sift算法、上
- 教你一步一步用c語言實現sift算法、下
- 其它
- 40億個數中快速查找
- hash表算法
- 一致性哈希算法
- 倒排索引關鍵詞不重復Hash編碼
- 傅里葉變換算法、上
- 傅里葉變換算法、下
- 后綴樹
- 基于給定的文檔生成倒排索引的編碼與實踐
- 搜索關鍵詞智能提示suggestion
- 最小操作數
- 最短摘要的生成
- 最長公共子序列
- 木塊砌墻原稿
- 附近地點搜索
- 隨機取出其中之一元素