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個(你可以取個整)。
這是一個令人愉快的結果。