Bloom Filter:海量數據的HashSet


Bloom Filter一般用於數據的去重計算,近似於HashSet的功能;但是不同於Bitmap(用於精確計算),其為一種估算的數據結構,存在誤判(false positive)的情況。

1. 基本原理

Bloom Filter能高效地表征數據集合\(S = \lbrace x_1 ,x_2 ,...,x_n \rbrace\),判斷某個數據是否屬於這個集合。其基本思想如下:用長度為\(m\)的位數組\(A\)來存儲集合信息,同時是有\(k\)個獨立的hash函數\(h_i(1\le i \le k)\)將數據映射到位數組空間。具體流程如下:

  1. 將長度為\(m\)的位數組全置為0;
  2. 對於數據\(x \in S\),依次計算其\(k\)個hash函數值\(h_i(x)=w,且1\le i \le k, 1 \le w \le m\),將位數組中的第\(a\)位bit置為1,即A[w]=1.

當查詢數據\(y\)是否屬於集合\(S\)時,計算其\(k\)個hash函數值,如果\(h_i(y)\)對應的位數組均為1,則數據\(y\)屬於集合\(S\);反之,則不屬於。

2. 相關計算

在上述判斷中,可能存在誤判(false positive, FP),比如某數的\(k\)個hash函數值可能屬於集合\(S\)中某幾個數\(k\)個hash函數值組成的集合。顯然,誤判率跟集合大小\(n\)、位數組大小\(m\)、hash函數的個數\(k\)有關;在其他條件不變的情況下,若\(n\)越大(\(m\)越小,或\(k\)越多),則誤判率越高。誤判率估算公式如下:

\[P_{fp} \approx (1-e^{-kn/m})^k \]

在實際的場景中,常常是已知集合大小\(n\),預設誤判率\(P_{fp}\),需要計算位數組大小\(m\)、hash函數的個數\(k\)。通過一系列的數學推導,可得到如下公式:

\[m= - \frac{n\ln P_{fp}}{(\ln 2)^2} \]

\[k=\frac{m}{n}\ln 2 \]

詳細的數學推導可參看相關文檔。

3. 實戰

Bloom Filter的Java實現有Guava、stream-lib,Scala實現有breezebloom-filter-scala。采用breeze庫的Distinct Count實現如下:

import breeze.util.BloomFilter

val bf = BloomFilter.optimallySized[Int](5, 0.01)
val arr = Array(1, 3, 4, 5, 1, 2, 6, 3, 1)
var cnt = 0
arr.foreach { t =>
  bf.contains(t) match {
    case false => cnt += 1; bf.+=(t)
    case _ =>
  }
}
println(arr.distinct.length) // 6
println(cnt) // 6

從上面的Scala代碼中,不難發現:在Distinct Count計算過程中,需要定義一個global變量,逐一用於對每個不屬於集合元素進行計算。顯然,在分布式計算中,這種方法不太適用;因為global變量沒法做到實時的傳遞更新。因此,另一種估算算法HyperLogLog,擁有優秀的可加性、易於並行化,在大數據的場景下應用廣泛——Spark、Kylin中的近似Distinct Count便是基於此。

4. 參考資料

[1] Broder, Andrei, and Michael Mitzenmacher. "Network Applications of Bloom Filters: A Survey." Internet Mathematics 1.4 (2011): 485-509.
[2] 張俊林, 《大數據日知錄》.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM