海量数据处理利器之布隆过滤器
看见了海量数据去重,找到停留时间最长的IP等问题,有博友提到了Bloom Filter,我就查了查,不过首先想到的是大叔,下面就先看看大叔的风采。
一、布隆过滤器概念引入
(Bloom Filter)是由布隆(Burton Howard Bloom)在1970年提出的。它实际上是由一个很长的二进制向量和一系列随机映射函数组成,布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率(假正例False positives,即Bloom Filter报告某一元素存在于某集合中,但是实际上该元素并不在集合中)和删除困难,但是没有识别错误的情形(即假反例False negatives,如果某个元素确实在该集合中,那么Bloom Filter 是不会报告该元素不存在于集合中的,所以不会漏报)。
下面从简单的排序谈到BitMap算法,再谈到数据去重问题,谈到大数据量处理利器:布隆过滤器。
- 对无重复的数据进行排序
给定数据(2,4,1,12,9,7,6)如何对它排序?
方法1:基本的排序方法包括冒泡,快排等。
方法2:使用BitMap算法
方法1就不介绍了,方法2中所谓的BitMap是一个位数组,跟平时使用的数组的唯一差别在于操作的是位。首先是开辟2个字节大小的位数组,长度为12(该长度由上述数据中最大的数字12决定的),然后,读取数据,2存放在位数组中下标为1的地方,值从0改为1,4存放在下标为3的地方,值从0改为1….最后,读取该位数组,得到排好序的数据是:(1,2,4,6,7,9,12)。
比较方法1和方法2的差别:方法2中,排序需要的时间复杂度和空间复杂度很依赖与数据中最大的数字比如12,因此空间上讲需要开2个字节大小的内存,时间上需要遍历完整个数组。当数据类似(1,1000,10万)只有3个数据的时候,显然用方法2,时间复杂度和空间复杂度相当大,但是当数据比较密集时该方法就会显示出来优势。
- 对有重复的数据进行判重
数据(2,4,1,12,2,9,7,6,1,4)如何找出重复出现的数字?
首先是开辟2个字节大小的位数组,长度为12(该长度由上述数据中最大的数字12决定的,当读取完12后,当读取2的时候,发现数组中的值是1,则判断出2是重复出现的。
二、布隆过滤器原理
布隆过滤器需要的是一个位数组(这个和位图有点类似)和k个映射函数(和Hash表类似),在初始状态时,对于长度为m的位数组array,它的所有位都被置为0。对于有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;如果要查找某个元素item是否在S中,则通过映射函数{f1,f2…..fk}得到k个值{g1,g2…..gk},然后再判断array[g1],array[g2]……array[gk]是否都为1,若全为1,则item在S中,否则item不在S中。这个就是布隆过滤器的实现原理。
当然有读者可能会问:即使array[g1],array[g2]……array[gk]都为1,能代表item一定在集合S中吗?不一定,因为有这个可能:就是集合中的若干个元素通过映射之后得到的数值恰巧包括g1,g2,…..gk,那么这种情况下可能会造成误判,但是这个概率很小,一般在万分之一以下。
很显然,布隆过滤器的误判率和这k个映射函数的设计有关,到目前为止,有很多人设计出了很多高效实用的hash函数。尤其要注意的是,布隆过滤器是不允许删除元素的(实际就是因为多个str可能都应设在同一点,而判断str存在的话是所有映射点都存在,所以不能删除),因为若删除一个元素,可能会发生漏判的情况。不过有一种布隆过滤器的变体Counter Bloom Filter,可以支持删除元素,感兴趣的读者可以查阅相关文献资料。
三、布隆过滤器False positives 概率推导
假设 Hash 函数以等概率条件选择并设置 Bit Array 中的某一位,m 是该位数组的大小,k 是 Hash 函数的个数,那么位数组中某一特定的位在进行元素插入时的 Hash 操作中没有被置位为1的概率是:
;那么在所有 k 次 Hash 操作后该位都没有被置 “1” 的概率是:
;如果我们插入了 n 个元素,那么某一位仍然为 “0” 的概率是:
因而该位为 “1”的概率是:
;现在检测某一元素是否在该集合中。标明某个元素是否在集合中所需的 k 个位置都按照如上的方法设置为 “1”,但是该方法可能会使算法错误的认为某一原本不在集合中的元素却被检测为在该集合中(False Positives),该概率由以下公式确定:
。
其实上述结果是在假定由每个 Hash 计算出需要设置的位(bit) 的位置是相互独立为前提计算出来的,不难看出,随着 m (位数组大小)的增加,假正例(False Positives)的概率会下降,同时随着插入元素个数 n 的增加,False Positives的概率又会上升,对于给定的m,n,如何选择Hash函数个数 k 由以下公式确定:
;此时False Positives的概率为:
;而对于给定的False Positives概率 p,如何选择最优的位数组大小 m 呢,
;该式表明,位数组的大小最好与插入元素的个数成线性关系,对于给定的 m,n,k,假正例概率最大为:
。
四、布隆过滤器应用
布隆过滤器在很多场合能发挥很好的效果,比如:网页URL的去重,垃圾邮件的判别,集合重复元素的判别,查询加速(比如基于key-value的存储系统)等,下面举几个例子:
- 有两个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。
第二种:如果允许一定错误率的话,则可以用布隆过滤器的思想。
- 在进行网页爬虫时,其中有一个很重要的过程是重复URL的判别,如果将所有的url存入到数据库中,当数据库中URL的数
量很多时,在判重时会造成效率低下,此时常见的一种做法就是利用布隆过滤器,还有一种方法是利用berkeley db来存储url,Berkeley db是一种基于key-value存储的非关系数据库引擎,能够大大提高url判重的效率。
布隆过滤器主要运用在过滤恶意网址用的,将所有的恶意网址建立在一个布隆过滤器上,然后对用户的访问的网址进行检测,如果在恶意网址中那么就通知用户。这样的话,我们还可以对一些常出现判断错误的网址设定一个白名单,然后对出现判断存在的网址再和白名单中的网址进行匹配,如果在白名单中,那么就放行。当然这个白名单不能太大,也不会太大,布隆过滤器错误的概率是很小的。
五、布隆过滤器简单Java实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | package a;import java.util.BitSet;/* * 存在的问题 * DEFAULT_LEN长度设置为多少合适呢? * 我发现result和DEFAULT_LEN有关,不应该啊,没发现原因 */public class BloomFilterTest { //30位,表示2^2^30种字符 static int DEFAULT_LEN = 1<<30; //要用质数 static int[] seeds = {3,5,7,11,17,31}; static BitSet bitset = new BitSet(DEFAULT_LEN); static MyHash[] myselfHash = new MyHash[seeds.length]; public static void main(String[] args) { String str = "791909235@qq.com"; //生成一次就够了 for(int i=0; i<seeds.length; i++) { myselfHash[i] = new MyHash(DEFAULT_LEN, seeds[i]); } bitset.clear(); for(int i=0; i<myselfHash.length; i++) { bitset.set(myselfHash[i].myHash(str),true); } boolean flag = containsStr(str); //System.out.println("========================"); System.out.println(flag); } private static boolean containsStr(String str) { // TODO Auto-generated method stub if(null==str) return false; for(int i=0; i<seeds.length; i++) { if(bitset.get(myselfHash[i].myHash(str))==false) return false; } return true; }}class MyHash { int len; int seed; public MyHash(int len, int seed) { super(); this.len = len; this.seed = seed; } public int myHash(String str) { int len = str.length(); int result = 0; //这的len就是str的len,不是成员变量的len for(int i=0; i<len; i++) { //System.out.println(seed+"oooooooooooo"); result = result*seed + str.charAt(i); //System.out.println(result); //长度就是1<<24,如果大于这个数 感觉结果不准确 //<0就是大于了0x7ffffff if(result>(1<<30) || result<0) { //System.out.println("-----"+(1<<30)); System.out.println(result+"myHash数据越界!!!"); break; } } return (len-1)&result; }} |
打赏:为祖国开源事业添砖加瓦

wordpress导航菜单的链接支持弹出新页面
c++绝对是拯救了世界,特别是程序员
Linux 磁盘坏道检测和修复
centos里mysql无法用localhost连接的解决方法
php扩展开发
IP多播
因特网的路由选择协议
ICMP协议
ARP协议
- 最新文章
-
4/24 同一程序执行多次就是多个进程/开启子进程的两种方式/进程的内存空间相互硬件隔离/父进程等待子进程结束/进程对象的其他属性/僵尸进程与孤儿进程
4/9 XML/RE/shevle 模块
4/8 包/常用模块
4/2 三元表达式/函数递归/匿名函数/内置函数
3/29 迭代器/生成器/生成器之yield的表达式形式/面向过程的编程思想
3/29 函数 装饰器
用C#定义两个类MyClass和Program。在MyClass类中定义一个int型的字段x,并为MyClass类添加一个不带参数的构造函数,在构造函数中将x赋值为10。在Program类中定义一个MyClass类的对象并将其实例化,再把x的值输出。
用C#设计一个 Windows应用程序,在该程序定义平面图形抽象类和其派生类圆、矩形和三角形。该程序实现的功能包括:输入相应图形的参数,如矩形的长和宽,单击相应的按钮,枓据输入參数创建图形类并输出该图形的面积。程序运行结果如图所示
用C# 设计一个 Windows应用程序,在该程序中首先构造一个学生基本类,再分别构造小学生、中学生、大学生等派生类,当输入相关数据,单击不同的按钮(小学生、中学生、大学生)将分别创建不同的学生对象,并输出当前学生总人数,该学生的姓名、学生类型和平均成绩。
用C#设计控制台应用程序,输入若干学生的英语和数学成绩,求出总分,并按总分从高到低排序。要求设计一个学生类 Student,所有学生对象存放在一个 Student对象数组中,通过一个方法对其按照总分进行降序排序,最后输出排序后的结果
- 热门文章
-
用C#设计控制台应用程序,计算矩形的周长和面积。要求在程序中定义一个类,其中包含两个字段用于存储矩形的长和宽,包含带参构造函数完成对字段的初始化,包含两个方法用于计算矩形的面积和周长。主函数中,输入矩形长和宽,实例化一个矩形,能够输出矩形的周长和面积。
用C#设计一个 Windows应用程序,在该程序中定义一个学生类和班级类,以处理每个学生的学号、姓名、语文、数学和英语3门课程的期末考试成绩
用C#设计一个简单的Windows应用程序,在文本框中输入两个点的坐标值,单击“计算“时显示两点之间的距离。如图所示:要求定义一个 Point类,包括 (1)两个私有字段表示两个坐标值。 (2)一个构造函数通过传入的参数对坐标值初始化。 (3)两个只读属性对坐标值的读取。 (4)一个方法包含一个Point类对象作为形参,计算该对象和自己的距离。
如何使用C#创建一个控制台应用程序,并输出“编程词典(珍藏版)”软件的启动页
C#设计一个Windows应用程序,输入一行字符,检索是否存在重复的二字词汇,输出重复的次数
如何使用C#创建一个控制台应用程序,并输出“编程词典(珍藏版)”软件的启动页
广州企业信息查询网站
Centos下让i386的32位程序运行在X86-64位系统中
IE6/IE7下:inline-block解决方案(转)
常见php框架的速度测试
