一、布隆过滤器可以用来做什么
布隆过滤器可用来判定一个元素是否属于一个集合,比如在一个大的集合A中,是否存在值a。由于hash碰撞(两个不同输入值的hash值相同)的原因,在判定a是否存在于A中时可能会有误判。如果判定结果是a不存在于A中,a肯定是不在A中;如果判定结果是存在,这时可能是因为与a的hash值相同其他元素存在于A中,而a并不存在。
关于布隆过滤器的使用场景,大多是用来判定“是否需要继续执行读取磁盘等效率低的操作”。比如,Google 的BitTable 和Apach HBase,都使用布隆过滤器判断查询的数据是否存在,来确定是否需要继续读取磁盘。再比如,用爬虫抓取网页时,有些网页会相互链接或者多个网页含有同一网页链接,所以可以使用布隆过滤器判断url是否爬取过了,来确定是否继续发起该url的访问。
二、布隆过滤器是怎么实现和使用的
1、如何实现
布隆过滤器由两个部分组成:一个位数组(只有0、1值)和一组散列函数。布隆过滤器在刚初始化时,数组中的值都是0,假设数组长度是10:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
添加元素时,将元素作为输入提供给散列函数。 每个散列函数都将输出一个数字作为数组索引,将索引对应位置值更新为1。 比如,将字符串“hello”传递给两个散列函数f1,f2,这两个散列函数给出索引0和4,我们将位数组中的相应位设置为1:
[1, 0, 0, 0, 1, 0, 0, 0, 0, 0]
2、如何使用
当查询一个元素是否存在于集合时,也先将元素传给两个散列函数,获得两个索引后,检查数组中相应位的值:
- 如果两个值中有0,即可判定该元素不在集合中。所以,不一定需要检查所有函数返回位的值,如果发现至少有一个值是0,那么即可判定该元素不在集合中。
- 如果两个值都是1,只可判定为“该元素可能在集合中”,因为散列函数可能会产生碰撞。比如我们使用两个函数获取“bloom”的索引可能为1和9,获取“filter”的索引可能为5和7,而此时再去查询“word”,会因为1和9已被“bloom”和“filter”已经设置为1而产出碰撞。因此,我们不能100%确定查询的元素在集合中。
三、为什么布隆过滤器效率比较高
时间复杂度
- 添加元素时,由于不需要迭代位数组,而是简单的设置索引位的值,所以操作所花费的时间仅取决于散列函数的个数。对于对于k个哈希函数的布隆过滤器,添加元素的时间复杂度为O(k) 。
- 查询元素时,对于k个哈希函数的布隆过滤器,只需要在位数组中检查的索引数量有一个不变的上界,所以查询元素的时间复杂度也为O(k)。
空间复杂度
由于不需要存储元素,只需依赖一定长度的位数组判断是否存在,并且数组长度的大小不也取决于集合中元素的多少,可以在误判率变大或效率变低的代价下减少存储(位数组)。
四、布隆过滤器有哪些缺点
主要缺点是有一定的误判率,所以随着存入集合的元素的增加,误判率也随之增加。误判率大小和三个指标有关:位数组长度m、集合长度n、散列函数个数k,其之间关系可以参考文献 ,该文献证明了对于给定的m、n,当 k = ln(2)* m/n 时误判率是最小的。