本文纯属个人见解,是对前面学习的总结,如有描述不正确的地方还请高手指正~
一.简介
1. 布隆过滤器 (Bloom Filter)是由Burton Howard Bloom于1970年提出,实际上是一个很长的二进制量向和一系列随机映射数函。
2. 用于断判一个元素是不是在集合中。在垃圾邮件过滤的黑白名单方法、爬虫(Crawler)的址网判重块模中等等经常被用到。哈希表也能用于断判元素是不是在集合中,但是布隆过滤器只要需哈希表的1/8或1/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,则item在S中,否则item不在S中.这个就是布隆过滤器的实现道理.
4. 误判的产生有这个可能:就是集合中的若干个元素通过映射以后到得的数值恰巧包含g1,g2,.....gk,那么这类况情下可能会形成误判,但是这个概率很小,一般在万分之一以下:
5. 布隆过滤器的误判率和这k个映射数函的计划有关
6. 布隆过滤器是不许允删除元素的,因为若删除一个元素,可能会产生漏判的况情。不过有一种布隆过滤器的变体Counter Bloom Filter,可以持支删除元素,
四.用应
1. 布隆过滤器在很多场所能施展很好的效果,比如:网页URL的去重,垃圾邮件的别判,集合重复元素的别判,查询减速(比如基于key-value的存储系统)等
2. .有两个URL集合A,B,个每集合中大约有1亿个URL,个每URL占64字节,有1G的内存,如何找出两个集合中重复的URL.
很显然,直接用利Hash表会出超内存制限的围范.这里给出两种路思:
第一种:如果不许允必定的错误率的话,只有效分治的想思去处理,将A,B两个集合中的URL分离存到若干个文件中{f1,f2...fk}和{g1,g2....gk}中,然后取f1和g1的内容读入内存,将f1的内容存储到hash_map当中,然后再取g1中的url,有若雷同的url,则写入到文件中,然后直到g1的内容取读毕完,再取g2...gk.然后再取f2的内容读入内存...次依类推,道知找出有所的重复url.
第二种:如果许允必定错误率的话,则可以用布隆过滤器的想思. 2.在停止网页爬虫时,其中有一个很要重的程过是重复URL的别判,如果将有所的url存入到数据库中,当数据库中URL的量数很多时,在判重时会形成率效低下,此时罕见的一种做法就是用利布隆过滤器,还有一种方法是用利berkeley db来存储url,Berkeley db是一种基于key-value存储的非关系数据库引擎,可以大大提高url判重的率效.
五.例子:
#include <string.h> #include <bitset> #include <iostream> #define MAX 2<<24 using namespace std; //简化n,p 成生m的程过,直接初始化为2<<24小大 bitset<MAX> bloomSet; //哈希数函的7个子种 int seeds[7] = {3,7,11,13,31,37,61}; //Hash Value int 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 Elements void addToBloomSet(string str){ int i; for(int i=0;i<7;i++){ int hash = getHashValue(str,i); bloomSet.set(hash,1); } } //init the BloomSet void 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; }
文章结束给大家分享下程序员的一些笑话语录:
一个合格的程序员是不会写出 诸如 “摧毁地球” 这样的程序的,他们会写一个函数叫 “摧毁行星”而把地球当一个参数传进去。