布隆过滤器简介

147 阅读3分钟

更多精彩文章,欢迎关注作者微信公众号:码工笔记

为了高效地判别一个对象是否在一个集合中,最常用的应该是Hash。传统的Hash方法能够保证给出的结果一定是正确的,即没有误判;但在允许一定错误率的场景下,传统Hash占用的空间以及查找所用的时间并不是最优的。

文章[1]介绍了三种Hash方法并进行了理论分析:

  • 方法一:传统的Hash方法,槽位数等于key位数,无误判
  • 方法二:传统Hash方法,槽位数小于key位数,可能误判
  • 方法三:布隆过滤器,不分槽,每个key经哈希得到多个不同的bit地址并为其置1,可能误判

方法一:传统Hash方法,槽位数等于key位数

假设哈希表有如下配置参数:

  • N:占用总空间大小,N个bit
  • h:槽数,N bit 被分为 h 个槽
  • b+1b+1:槽大小,每个槽占用b+1b+1位(有1个bit标识槽是否为空)
  • n:hash表中所存key的个数,也即被占用的槽数
  • ϕ\phi:空槽占总槽数的比例,有:
ϕ=hnh=Nn(b+1)N\phi = \frac{h-n}{h} = \frac{N - n\cdot(b+1)}{N}

转换后可得:

N=n(b+1)1ϕN = \frac{n\cdot(b+1)}{1-\phi}

另定义以下变量:

  • T:拒绝流程中需要比对的比特数的期望值,也即hash计算出的槽中存放的并非目标值时,后续还需要比对的bit数
  • E:hash计算出的槽非空时,平均(期望值)需要比较E个bit才能确定槽中值的确不等于目标值 则有:
T=(1ϕ)(E+T)+ϕT = (1-\phi)(E+T) + \phi

拒绝流程是一个递归过程,如果发现槽中值并非目标值,则需要重新开始整个hash流程:

  • 槽非空情况下(概率为1ϕ1-\phi),平均需要比较E个bit才能识别出来槽中数据与目标识不同,然后按T的定义还需要再比较T个bit。
  • 槽空的情况下(概率为ϕ\phi),只需要比较一个bit即可。

下面考虑如何来估算 E,假设比较槽内值与目标值之间的差异时,不失一般性,假设其前xx个bit相等,第x+1x+1个bit不同,则有:

Ex=1(x+1)(12)x=3E\approx \sum_{x=1}^\infty(x+1)\cdot(\frac{1}{2})^x = 3

注:非空槽中值的标识位为定值,所以上式中是(12)x(\frac{1}{2})^{x}而不是(12)x+1(\frac{1}{2})^{x+1}

结合之前的公式则有:

T=(3ϕ)2T = (\frac{3}{\phi}) - 2

从而得到传统Hash方式的时间-空间关系式:

N=n(b+1)T+2T1N = n\cdot(b+1)\cdot\frac{T+2}{T-1}

方法二:传统Hash方法,槽位数小于key位数

假设其参数配置如下:

  • c:槽大小,每个槽占用c个bit
  • ϕ\phi':空槽占总槽数的比例 则参考方法一的推导同理可得:
ϕ=NncN\phi' = \frac{N' - n\cdot c}{N'}
T=(3ϕ)2T' = (\frac{3}{\phi'}) - 2
N=ncT+2T1N' = n \cdot c \cdot \frac{T'+2}{T'-1}

在这种hash方法下,其错误率PP'可使用下式计算:

P=(12)c1ϕP' = \frac{(\frac{1}{2})^{c-1}}{\phi'}

则有:

c=log2P+1+log2T+23c = -\log_2P' + 1 + \log_2\frac{T'+2}{3}
N=n(log2P+1+log2T+23)T+2T1N' = n \cdot (-\log_2P' + 1 + \log_2\frac{T'+2}{3}) \cdot \frac{T'+2}{T'-1}

方法三:布隆过滤器,不分槽

  • NN'':占用的总空间bit数
  • ϕ\phi'':空槽率
  • d:每个key经hash后占用的bit数 则有:
ϕ=(1dN)n16\phi'' = (1 - \frac{d}{N''})^n (16)

如果一个key实际未存储在布隆过滤器中,但由此key计算出的d个位置都是1,这时即发生了错判,错判概率为:

P=(1ϕ)dP'' = (1-\phi'')^d

假设dNd\ll N'',对(16)式两边取以2为底的对数,则有:

log2ϕ=loge(1dN)nlog2e=n(dN)log2e\log_2\phi'' = \log_e{(1 - \frac{d}{N''})^n} * \log_2e = -n \cdot (\frac{d}{N''}) \cdot \log_2e

则有:

N=n(log2P)log2elog2ϕlog2(1ϕ)N'' = n \cdot (-\log_2P'') \cdot \frac{\log_2e}{ \log_2\phi'' \cdot \log_2{(1-\phi'')}}

下面我们计算一下T'':

如果在一次失败的查询中比较了xx位,则前x1x-1位都是1,而第xx位为0,这种情况出现的概率为ϕ(1ϕ)x1\phi'' \cdot (1 - \phi'')^{x-1}

假设:P1P''\ll 1d1d \gg 1,有:

T=x=1xϕ(1ϕ)x1=1ϕT'' = \sum_{x=1}^{\infty}{x \cdot \phi'' \cdot (1 - \phi'')^{x-1}} = \frac{1}{\phi''}

则有:

N=n(log2P)log2elog2(1T)log2(11T)N'' = n \cdot (-\log_2P'') \cdot \frac{\log_2e}{\log_2{(\frac{1}{T''})} \cdot \log_2{(1 - \frac{1}{T''})} }

方法二 VS. 方法三

为去掉上述公式中 n 和 P 的影响,引入空间归一化度量值 S:

S=N(nlog2P)S = \frac{N}{(-n \cdot \log_2P)}

则有:

S=T+2T1(1+1+log2T+23log2P)S' = \frac{T'+2}{T'-1} \cdot (1 + \frac{1 + \log_2{\frac{T'+2}{3}}}{-\log_2{P'}})
S=log2elog21Tlog2(11T)S'' = \frac{\log_2e}{ \log_2{\frac{1}{T''}} \cdot \log_2{(1 - \frac{1}{T''})}}

可以看出 S>T+2T1S' > \frac{T'+2}{T'-1},并且有:

limP0S=T+2T1\lim_{P' \to 0}S' = \frac{T'+2}{T'-1}

两种方法时间-空间关系比较如下图示: bloomFilter.png

可见,布隆过滤器总体性能和空间更优。

参考资料

【1】Space/Time Trade-offs in Hash Coding wiht Allowable Errors