[整理]Bloom Filter概念及应用
在日常生活中,包括在设计计算机软件时,我们经常要判断一个元素是否在一个集合中。最直接的方法就是将集合中全部的元素存在计算机中,遇到一个新元素时,将它和集合中的元素直接比较即可。一般来讲,计算机中的集合是用哈希表(hash table)来存储的。它的好处是快速准确,缺点是费存储空间。布隆过滤器只需要哈希表 1/8 到 1/4 的大小就能解决同样的问题。因为hash如果要work就要避免冲突, 要避免冲突就需要很大的bucket空间(bit). 而Bloom的优点时允许冲突, 但他通过增加hash函数的数量, 来减小同时冲突的概率, 所以可以用更小的空间. 而且hash table的实现往往用的是指针array, 用于指向集合元素, 而bloom的实现用的是bitarray, 因为你不需要得到这个集合元素, 只是知道他有没有。
Indeed, a Bloom filter with just one hash function is equivalent to ordinary hashing
应用1. Bloom Filter 判断元素是否属于某集合
初始状态时,Bloom Filter是一个包含m位的位数组,每一位都置为0。
为了表达S={x1, x2,…,xn}这样一个n个元素的集合,Bloom Filter使用k个相互独立的哈希函数(Hash Function),它们分别将集合中的每个元素映射到{1,…,m}的范围中。对任意一个元素x,第i个哈希函数映射的位置hi(x)就会被为1(1≤i≤k)。注意,如果一个位置多次被置为1,那么只有第一次会起作用,后面几次将没有任何效果。在下图中,k=3,且有两个哈希函数选中同一个位置(从左边数第五位)。
在判断y是否属于这个集合时,我们对y应用k次哈希函数,如果所有hi(y)的位置都是1(1≤i≤k),那么我们就认为y是集合中的元素,否则就认为y不是集合中的元素。下图中y1就不是集合中的元素。y2或者属于这个集合,或者刚好是一个false positive。
应用2. Bloom Filter 判断文本相似性
将Bloom filter技术引入到相似数据检测中,可以弥补shingle中应用特征集交集计算文件相似性所导致的高计算开销,在性能与相似性匹配精度之间取得平衡. 在副本同步消除冗余的技术中,系统应用bloom filters 进行文件相似性检测,其中每个文件被看作是bloom filter中定义的一个集合,它的元素是文件按照CDC算法所划分出块的指纹值.有相同指纹值集合的文件由相同的bloom filter表示.
Bloom filter算法的核心思想也是着眼于文件特征的降维,它使用Bloom filter数据结构来表示特征值。Bloom filter是一个空间效率很高的数据结构,它由一个位数组和一组hash映射函数组成。Bloom filter的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。Bloom filter构造方法如下:
(1) 构造一个m位的bloom filter数据结构bf,并将所有位初始为0;
(2) 选定两个hash函数作为映射函数,分别为hash1,hash2;
(3) 对每一个shingle[文件按照CDC算法所划分出块的指纹值],分别应用hash1和hash2,并对bf相应比特位置1;
(4) 输出bf作为文件特征值。
这样,两个文件相似性计算就转换成两个bloom filter的相似性计算【可以使用Hamming距离、Cosine、Overlap、Dice、Jaccard等方法来度量】,越相似的文件在它们的bloom filter中有更多共同的1。由于Bloom filter具有有限的误识别率的特性,相似性算法精确度取决于Bloom filter的大小,越大则精确度越高,同时存储空间消耗也越大。
dot(x, y)
Cosine_sim(x, y) = -----------------
sqrt(|x|.|y|)
dot (x, y)
Overlap_sim(x, y) = -----------------
min(|x|, |y|)
dot(x, y)
Dice_sim(x, y) = -----------------
|x| + |y|
dot(x, y)
Jaccard_sim(x, y) = ------------------------
|x| + |y| - dot(x, y)
其中,dot(x, y) = Σx[i].y[i],在这里相当于两个Bloom filter数据结构中同时为1的位数;|x|表示bloom filter数据结构中为1的位数。相似性计算函数如下:
view plainprint?
static double bloom_sim(BLOOM *bloom1, BLOOM *bloom2)
{
int i, r1, r2;
int c1 = 0, c2 = 0, comm = 0;
double sim;
for (i = 0; i < BLOOM_ARRAY_SZ; i++) {
r1 = bloom_check(bloom1, 1, i);
r2 = bloom_check(bloom2, 1, i);
if (r1 && r2) {
comm++;
c1++;
c2++;
} else {
if (r1) {
c1++;
}
if (r2) {
c2++;
}
}
}
/* similarity measures */
//sim = comm/(sqrt(c1) * sqrt(c2)); /* Cosine */
//sim = comm/1.0/(c1 + c2 - comm); /* Jaccard */
//sim = comm*2.0/(c1 + c2); /* Dice */
sim = comm*1.0/(c1<c2?c1:c2); *="" overlap="" <br=""> return sim;
}
应用举例:
假定我们存储一亿个电子邮件地址,我们先建立一个十六亿二进制(比特),即两亿字节的向量,然后将这十六亿个二进制全部设置为零。对于每一个电子邮件地址 X,我们用八个不同的随机数产生器(F1,F2, ...,F8) 产生八个信息指纹(f1, f2, ..., f8)。再用一个随机数产生器 G 把这八个信息指纹映射到 1 到十六亿中的八个自然数 g1, g2, ...,g8。现在我们把这八个位置的二进制全部设置为一。当我们对这一亿个 email 地址都进行这样的处理后。一个针对这些 email 地址的布隆过滤器就建成了。(见下图)
现在,让我们看看如何用布隆过滤器来检测一个可疑的电子邮件地址 Y 是否在黑名单中。我们用相同的八个随机数产生器(F1, F2, ..., F8)对这个地址产生八个信息指纹 s1,s2,...,s8,然后将这八个指纹对应到布隆过滤器的八个二进制位,分别是 t1,t2,...,t8。如果 Y 在黑名单中,显然,t1,t2,..,t8 对应的八个二进制一定是一。这样在遇到任何在黑名单中的电子邮件地址,我们都能准确地发现。
参考:http://www.cnblogs.com/fxjwind/archive/2011/08/30/2159221.html
参考:http://dlnaeye.com/?paged=2
参考:http://blog.csdn.net/jiaomeng/article/details/1495500
参考:数学之美