认识布隆过滤器

百度百科关于布隆过滤器的概念:
布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。
基本概念:
如果想要判断一个元素是不是在一个集合里,一般想到的是将所有元素保存起来,然后通过比较确定。链表,树等等数据结构都是这种思路. 但是随着集合中元素的增加,我们需要的存储空间越来越大,检索速度也越来越慢(O(n),O(logn))。不过世界上还有一种叫作散列表(又叫哈希表,Hash table)的数据结构。它可以通过一个Hash函数将一个元素映射成一个位阵列(Bit array)中的一个点。这样一来,我们只要看看这个点是不是1就可以知道集合中有没有它了。这就是布隆过滤器的基本思想。

Hash面临的问题就是冲突。假设Hash函数是良好的,如果我们的位阵列长度为m个点,那么如果我们想将冲突率降低到例如 1%, 这个散列表就只能容纳m / 100个元素。显然这就不叫空间效率了(Space-efficient)了。解决方法也简单,就是使用多个Hash,如果它们有一个说元素不在集合中,那肯定就不在。如果它们都说在,虽然也有一定可能性它们在说谎,不过直觉上判断这种事情的概率是比较低的。
布隆过滤器的应用举例,如果不安全网页黑名单包含100亿个黑名单网页,每个网页的url最多占用64B,现在想要实现一种网页过滤系统,可以根据网页的URL判断该网页是否在黑名单上,请设计该系统。
允许有万分之一的错误率,使用的空间不能超过30GB。
分析:如果把黑名单中所有的URL通过数据库或哈希表保存下来,就可以对每条URL进行查询,但是每个URL有64B,100亿个,所以至少需要640GB空间,不满足要求。
以后碰到类似网页黑名单、垃圾邮件过滤系统、爬虫的网址判重系统等题目,又看到可以允许一定程度的失误率,但是对空间要求比较严格,那么很有可能是面试官希望你具备布隆过滤器的知识。一个布隆过滤器代表一个集合并可以精确判断一个元素是否在集合中。注意,知识精确代表和精确判断,到底多精确呢?完全取决于你的设计,但想做到完全正确是不可能的。布隆过滤器的优势在于使用很少的空间就可以将准确率提高到很高的程度。该结构由burton howard bloom于1970年提出。
首先介绍哈希函数(散列函数)的概念。哈希函数的输入域可以是非常大的范围,比如,任意一个字符串,但是输出域是固定范围,假设为SS,并具有如下性质:
1,典型的哈希函数都具有无限的输入值域
2,当给哈希函数传入相同的输入值时,返回值一样
3,当给哈希函数传入不同的输入值时,返回值可能一样,也可能不一样,这是当然的,因为输出域统一是S,所以会有不同的输入值对应在S中的一个元素上。
4,最重要的性质是很多不同的输入值所得到的返回值会均匀地分布在S上。
1-3性质是哈希函数的基本性质,4性质是评价一个哈希函数优劣的关键,不同输入值所得到的所有返回值越均匀地分布在S上,哈希函数越优秀,并且这种均匀分布与输入值出现的规律无关。比如,”aaa1”、”aaa2”、”aaa3”三个输入比较类似,但经过优秀的哈希函数就会返回相差非常大的值。读者只用记住哈希函数的性质即可,有兴趣的可以了解一些哈希函数经典的实现,比如MD5算法和SHA1算法,但了解这些算法的细节不在准备代码面试的范围中。如果一个优秀的哈希函数能够做到很多不同的输入值所得到的返回值非常均匀地分布在0~m-1的空间上,那么将所有的返回值对m取余(%m),可以认为所有的返回值也会均匀地分布在0~m-1范围上。这是显而易见的。
接下来介绍什么是布隆过滤器,假设有一个长度为m的bit类型数组,即数组中的每一个位置只占一个bit,我们知道,一个bit为只有0和1两种状态。
再假设一共有k个哈希函数,这些哈希函数的输出域S都大于或等于m,并且这些哈希函数都足够优秀,彼此之间也完全独立。那么对同一个输入对象(假设是一个字符串记为URL),经过k个哈希函数算出来的结果也是独立的,可能相同也可能不同,但彼此独立。对算出来的每一个结果都对m取余(%m),然后在bit array上把相应的位置设置为1(涂黑)。
我们把bit类型的数组记为bitmap,至此,一个输入对象对bitmap的影响过程就结束了,也就是bitmap中的一些位置会被描黑。接下来按照该方法处理所有的输入对象,每个对象都可能把bitmap中的一些位置描黑,也可能遇到已经涂黑的位置,遇到已经涂黑的位置让其继续为黑即可。处理完所有的输入对象之后,可能bitmap中已经有相当多的位置被涂黑。至此,一个布隆过滤器生成完毕,这个布隆过滤器代表之前所有输入对象组成的集合。
那么在检查阶段时,如何检查一个对象是否是之前的某一个输入对象呢?假设一个对象为a,想检查它是否是之前的输入对象,就把a通过k各哈希函数算出k个值,然后把k个值取余(%m),就得到在[0,m-1]范围上的k个值。接下来在bitmap上看这些位置是不是都为黑。如果有一个不为黑,说明a一定不在这个集合里;如果都为黑,说明a在这个集合里,但可能有误判。再解释的具体一点,如果a的确是输入对象,那么在生成布隆过滤器时,bitmap中相应的k个位置一定已经涂黑了,所以在检查阶段,a一定不会被漏过,这个不会产生误判,会产生误判的是,a明明不是输入对象,但如果在生成布隆过滤器的阶段因为输入对象过多,而bitmap过小,则会导致bitmap绝大多数的位置都已经被描黑。那么在检查a时,可能a对应的k个位置都是黑的,从而错误地认为a是输入对象。通俗的说,布隆过滤器就是”宁可错杀三千,绝不放过一个”。
那么布隆过滤器怎么实现呢?我们可能注意到,如果bitmap的大小m相比于输入对象的个数n过小,失误率会变大。接下来先介绍根据n的大小和我们想达到的失误率p,如何确定布隆过滤器的大小m和哈希函数的个数k,最后是布隆过滤器的失误率分析。下面以本题为例进行说明。
黑名单中样本的个数为100亿个,记为n;失误率不能超过0.01%,记为p;每个样本的大小为64B,这个信息不回影响布隆过滤器的大小,只和选择哈希函数有关,一般的哈希函数都可以接收64B的输入对象,所以使用布隆过滤器还有一个好处是不用顾忌单个样本的大小,它丝毫不会影响布隆过滤器的大小。
所以n=100亿,p=0.01%,布隆过滤器的大小m由以下公式确定:
m=-(n * lnp) / (ln2ln2) (ln2的平方)。
根据公式算出m=19.19n,向上取整为20n,即需要2000亿个bit,也就是25GB,哈希函数的个数由以下公式确定:
k= ln2 * m/n = 0.7 * m / n
计算出哈希函数的个数为k=14个。
然后用25GB的bitmap再单独实现14个哈希函数,根据如上描述生成布隆过滤器即可。
因为我们在确定布隆过滤器的大小过程中选择了向上取整,所以还要用如下公式确定布隆过滤器真实失误率为:0.006%,这比0.01%还低,哈希函数本身不占什么空间,所以使用的空间就是bitmap的大小(即25GB),服务器的内存都可以达到这个级别,所以要求达标。之后的判断阶段如上文的描述。

猜你喜欢

转载自blog.csdn.net/u010075989/article/details/81110668