intro
布隆过滤器是一种很有意思的数据结构,它的用途是检测某个元素是否在一个集合中。
首先,有一个数组,它的元素全部是0,然后共有m个坑:
我现在有一个集合S={x,y,z},对于每一个元素,通过3个hash函数,将其打到数组上,打中的位置设置为1。
比如x,三次hash后,数组上就会有3个位置变为1(蓝色线条)。
至于hash函数,你可以认为它对x做了处理,最后模上数组的长度得到一个下标。
注意,元素hash后可能打中同一个坑,这点不必惊奇。当m的值(也就是数组的长度)越来越大,这种情况的概率就会越来越小。
现在我来了一个w,我要问:w在不在S中?
将w也三次hash一下:
- 如果有打中0的情形,那么,它肯定不在S中。
- 如果打中的全部是1,那么它很有可能在S中,也就是说,可以判定它在S中,并带有一定的错误概率。
more general
我们的根本目的是减少错误概率。
现在考虑一般情况。
- 数组的长度为m
- 集合为S={x1,x2,…,xn},有n个元素
- hash函数有k个:h1,h2,…,hk,0≤hi(xj)<m(1≤i≤k,1≤j≤n),换句话说,每个元素xi的每次hash的下标都落在数组内
- hash函数产生的下标是等概率均匀分布的,不是说全部挤在前面或者某一个地方
好,现在我们考虑一个元素(比如x1)的插入(占坑)。
经过一次hash后,某个坑为1的概率为:
m1
某个坑为0的概率是:
1−m1
k个hash函数过后,某个坑依旧为0的概率是:
(1−m1)k
因为m→+∞lim(1−m1)−m=e,所以
m→∞lim(1−m1)k=m→∞lim[(1−m1)−m]−mk=e−mk
我们会假设数组的长度m无穷大,所以上面的式子是成立的。
完成了一个元素的插入后,现在我插入 n个元素。
n个元素插入后,某个坑依旧为0的概率是:
e−mnk
于是某个坑为1的概率是:
1−e−mnk
现在我来了一个元素y,y并不在S中。
y经过 k个hash函数 后,全部打到了标记为1的坑,这个概率是:
(1−e−mnk)k
好了,我们找到了最终的函数。
目标:使f=(1−e−mnk)k
最小。
f=eln(1−e−mnk)k=ekln(1−e−mnk)
令
g=kln(1−e−mnk)
问题转化为求g的最小值。
∂k∂g=ln(1−e−mnk)+(1−e−mnk1)(−e−mnk)(−mn)(k)
令 ∂k∂g=0
同时,令
e−mnk=p
于是
mn=−klnp
那么
ln(1−p)+(1−p1)(p)(−klnp)(k)=ln(1−p)−1−pplnp=0
整理一下:
(1−p)ln(1−p)=plnp
得到
p=21
于是
k=nmln2
k,m,n满足k=nmln2能够使得f最小。
也就是说,如果数组长度比上元素个数为8的话(nm=8),那么hash函数的个数最好有8ln2≈5.45个(你可以取个整)。
这是一个令人愉快的结果。