Bloom Filter:海量数据的HashSet

浏览: 1281

Bloom Filter一般用于数据的去重计算,近似于HashSet的功能;但是不同于Bitmap(用于精确计算),其为一种估算的数据结构,存在误判(false positive)的情况。

1. 基本原理

Bloom Filter能高效地表征数据集合S={x1,x2,...,xn},判断某个数据是否属于这个集合。其基本思想如下:用长度为m的位数组A来存储集合信息,同时是有k个独立的hash函数hi(1≤i≤k)将数据映射到位数组空间。具体流程如下:

  1. 将长度为m的位数组全置为0;
  2. 对于数据x∈S,依次计算其k个hash函数值hi(x)=w,且1≤i≤k,1≤w≤mhi(x)=w,且1≤i≤k,1≤w≤m,将位数组中的第a位bit置为1,即A[w]=1.


当查询数据y是否属于集合S时,计算其k个hash函数值,如果hi(y))对应的位数组均为1,则数据y属于集合S;反之,则不属于。


2. 相关计算

在上述判断中,可能存在误判(false positive, FP),比如某数的kk个hash函数值可能属于集合S中某几个数k个hash函数值组成的集合。显然,误判率跟集合大小n、位数组大小m、hash函数的个数k有关;在其他条件不变的情况下,若nn越大(m越小,或k越多),则误判率越高。误判率估算公式如下:

Clipboard Image.png

在实际的场景中,常常是已知集合大小nn,预设误判率PfpPfp,需要计算位数组大小mm、hash函数的个数kk。通过一系列的数学推导,可得到如下公式:

Clipboard Image.png

详细的数学推导可参看相关文档。

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] A. Broder and M. Mitzenmacher, Network applications of bloom filters: A survey.
[2] 张俊林, 《大数据日知录》.

推荐 0
本文由 Treant 创作,采用 知识共享署名-相同方式共享 3.0 中国大陆许可协议 进行许可。
转载、引用前需联系作者,并署名作者且注明文章出处。
本站文章版权归原作者及原出处所有 。内容为作者个人观点, 并不代表本站赞同其观点和对其真实性负责。本站是一个个人学习交流的平台,并不用于任何商业目的,如果有任何问题,请及时联系我们,我们将根据著作权人的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。

0 个评论

要回复文章请先登录注册