Bloom Filter

来源:互联网 发布:淘宝网安全套 编辑:程序博客网 时间:2024/06/02 04:35

一.简介

1.       布隆过滤器 (Bloom Filter)是由Burton Howard Bloom1970年提出实际上是一个很长的二进制向量和一系列随机映射函数

2.       用于判断一个元素是否在集合中。在垃圾邮件过滤的黑白名单方法、爬虫(Crawler)的网址判重模块中等等经常被用到。哈希表也能用于判断元素是否在集合中,但是布隆过滤器只需要哈希表的1/81/4的空间复杂度就能完成同样的问题。

3.       它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。

二.基本概念

1.       如果想判断一个元素是不是在一个集合里,一般想到的是将所有元素保存起来,然后通过比较确定。链表,树等等数据结构都是这种思路.,但是元素越来越多,存储空间的需求也越来越大,检索速度慢,所以可以利用散列表(哈希)的数据结构。通过一个Hash函数映射到一个位阵列的一个点。

2.       1亿个不重复的正整数(大致范围已知),但是只有1G的内存可用,如何判断该范围内的某个数是否出现在这1亿个数中?最常用的处理办法是利用位图,1*108/1024*1024*8=11.9,也只需要申请12M的内存。(位图主要用于处理整型值一类的问题,而且要求无重复)

3.       如果是一亿个邮件地址,如何判断邮件地址是否在1亿个地址之中?这时可以使用Hash表,但是哈希表不能避免发生碰撞,假设一个邮件只占8个字节,为了保证Hash表的碰撞率,使装填因子在0.5左右,至少需要2*8*10^8/1024*1024*1024 =1.5G存储空间,因此可以使用另外一种数据结构:Bloom Filter.

三.原理:

1.       布隆过滤器需要的是一个位数组(这个和位图有点类似)和k个映射函数(和Hash表类似),在初始状态时,对于长度为m的位数组array,它的所有位都被置为0,如下图所示:

                                                                

2.      对于有n个元素的集合S={s1,s2......sn},通过k个映射函数{f1,f2,......fk},将集合S中的每个元素sj(1<=j<=n)映射为k个值{g1,g2......gk},然后再将位数组array中相对应的array[g1],array[g2]......array[gk]置为1

                                                

3.       如果要查找某个元素item是否在S中,则通过映射函数{f1,f2.....fk}得到k个值{g1,g2.....gk},然后再判断array[g1],array[g2]......array[gk]是否都为1,若全为1,则itemS中,否则item不在S.这个就是布隆过滤器的实现原理.

4.      误判的发生有这个可能:就是集合中的若干个元素通过映射之后得到的数值恰巧包括g1,g2,.....gk,那么这种情况下可能会造成误判,但是这个概率很小,一般在万分之一以下:

5.      布隆过滤器的误判率和这k个映射函数的设计有关

6.      布隆过滤器是不允许删除元素的,因为若删除一个元素,可能会发生漏判的情况。不过有一种布隆过滤器的变体Counter Bloom Filter,可以支持删除元素

四.应用

1.       布隆过滤器在很多场合能发挥很好的效果,比如:网页URL的去重,垃圾邮件的判别,集合重复元素的判别,查询加速(比如基于key-value的存储系统)等

2.       .有两个URL集合A,B,每个集合中大约有1亿个URL,每个URL64字节,有1G的内存,如何找出两个集合中重复的URL.  

很显然,直接利用Hash表会超出内存限制的范围.这里给出两种思路:  

第一种:如果不允许一定的错误率的话,只有用分治的思想去解决,将A,B两个集合中的URL分别存到若干个文件中{f1,f2...fk}{g1,g2....gk}中,然后取f1g1的内容读入内存,将f1的内容存储到hash_map当中,然后再取g1中的url,若有相同的url,则写入到文件中,然后直到g1的内容读取完毕,再取g2...gk.然后再取f2的内容读入内存...依次类推,知道找出所有的重复url.  

第二种:如果允许一定错误率的话,则可以用布隆过滤器的思想.  2.在进行网页爬虫时,其中有一个很重要的过程是重复URL的判别,如果将所有的url存入到数据库中,当数据库中URL的数量很多时,在判重时会造成效率低下,此时常见的一种做法就是利用布隆过滤器,还有一种方法是利用berkeley db来存储urlBerkeley db是一种基于key-value存储的非关系数据库引擎,能够大大提高url判重的效率.

五.例子:

 

#include <string.h>#include <bitset>#include <iostream>#define MAX 2<<24using namespace std;//简化n,p 生成m的过程,直接初始化为2<<24大小bitset<MAX> bloomSet;//哈希函数的7个种子int seeds[7] = {3,7,11,13,31,37,61};//Hash Valueint getHashValue(string str,int n){int result = 0;int i = 0;for(i = 0;i<str.size();i++){result = seeds[n]*result+(int)str[i];if(result > 2<<24)result %= 2<<24;}return result;}//判断是否在布隆过滤器中bool isInBloomSet(string str){int i;for(i = 0;i<7;i++){int hash = getHashValue(str,i);if(bloomSet[hash] == 0)return false;}return true;}//add Elementsvoid addToBloomSet(string str){int i;for(int i=0;i<7;i++){int hash = getHashValue(str,i);bloomSet.set(hash,1);}}//init the BloomSetvoid initBloomSet(){addToBloomSet("www.baidu.com");addToBloomSet("www.cnblogs.com");addToBloomSet("www.google.com");}int main(int argc, char *argv[]){   int n;   initBloomSet();   while(scanf("%d",&n)==1){   string str;   while(n--){   cin >> str;   if(isInBloomSet(str)){   cout <<"YES"<<endl;   }else{   cout <<"NO"<<endl;   }   }   }     //system("PAUSE");   return 0;}