前言
布隆过滤器(Bloom Filter)是非常经典的,以空间换时间的算法。布隆过滤器由布隆在 1970 年提出。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。
布隆过滤器的原理
布隆过滤器的实现原理是一个超大的位数组和几个哈希函数。假设位数组的长度为 m,哈希函数的个数为 k
解析上图,具体的操作流程:
- 假设集合里面有 3 个元素 {x, y, z},哈希函数的个数为 3。
- 首先将位数组进行初始化,初始化状态的维数组的每个位都设置位 0。
- 对于集合里面的每一个元素,将元素依次通过 3 个哈希函数进行映射,每次映射都会产生一个哈希值,这个值对应位数组上面的一个点,然后将位数组对应的位置标记为 1。
- 查询某元素是否存在集合中的时,用同样的方法将 W 通过哈希映射到位数组上的 3 个点。如果 3 个点中任意一个点不为 1,则可以判断该元素一定不存在集合中。反之,如果 3 个点都为 1,则该元素可能存在集合中。
注意:此处不能判断该元素是否一定存在集合中,可能存在一定的误判率,这一点从图中就能得知:假设某个元素通过映射对应下标为 4、5、6 这 3 个点。虽然这 3 个点都为 1,但是很明显这 3 个点是不同元素经过哈希得到的位置,因此这种情况说明元素虽然不在集合中,也可能对应的都是 1,这是误判率存在的原因。
假阳性率
所谓假阳性率就是本来在集合中不存在的元素,被判定为存在的概率。
假阳性是BF最大的痛点,因此有必要权衡,比如计算一下假阳性的概率。为了简单一点,就假设我们的哈希函数选择位数组中的比特时,都是等概率的。当然在设计哈希函数时,也应该尽量满足均匀分布。
- 在位数组长度m的BF中插入一个元素,它的其中一个哈希函数会将某个特定的比特置为1。因此,在插入元素后,该比特仍然为0的概率是: 1 − 1 m 1-{1 \over m} 1−m1
- 现有k个哈希函数,并插入n个元素,自然就可以得到该比特仍然为0的概率是: ( 1 − 1 m ) k n (1-{1 \over m})^{kn} (1−m1)kn
- 反过来讲,它已经被置为1的概率就是 1 − ( 1 − 1 m ) k n 1- (1-{1 \over m})^{kn} 1−(1−m1)kn
- 也就是说,如果在插入n个元素后,我们用一个不在集合中的元素来检测,那么被误报为存在于集合中的概率(也就是所有哈希函数对应的比特都为1的概率)为: ( 1 − [ 1 − 1 m ] k n ) k (1- [1-{1 \over m}]^{kn})^k (1−[1−m1]kn)k
- 当n比较大时,根据重要极限公式,可以近似得出假阳性率: ( 1 − e − k n / m ) k (1- e^{-kn/m})^k (1−e−kn/m)k
因此,可以得出如下结论,在哈希函数的个数k一定的情况下:
- 位数组长度m越大,假阳性率越低
- 已插入元素的个数n越大,假阳性率越高
如何选择适合业务的 k 和 m 值呢,直接贴一个公式:
如何设计m,k呢
下图是网友根据不同的m,k算出的假阳率,我们可以根据需要去选择