布隆過濾器及其數學推導

布隆過濾器

昨天忽然看到了一個布隆過濾器的介紹和一些用法,感受很新奇,也頗有意思,恰好趁着週末來寫一篇博客。數據庫

什麼是布隆過濾器

布隆過濾器?難道是這個?E起來,而後阻擋地方的飛行道具並減小傷害?數組

NO!NO!NO!固然不是這個,一篇技術博客怎麼會扯到遊戲呢?來來來,讓咱們先看一下布隆的E技能。markdown

堅如盤石 E 消耗法力:30/35/40/45/50冷卻時間:18/16/14/12/10 布隆朝一個方向舉起盾牌,持續3/3.25/3.5/3.75/4秒,並使來自目標方向的第一次攻擊變得無效。布隆還將攔截敵方的飛行道具,並將它們摧毀,減小30/32.5/35/37.5/40%的後續傷害。在舉盾期間,布隆得到10%移動速度加成。

首先,咱們設想一個場景,在某次開發中,你被要求你的網站的用戶名不能重複,你應該怎麼作?第一感受固然就是將用戶名存到數據庫中間,而後當用戶註冊的時候,查看數據庫是否存在這個用戶名便可。但是,當你的網站用戶量很大的時候好比說一億,查詢數據庫毋庸置疑是一個耗時的操做,這個時候,你忽然想到了哈希表,由於你知道哈希表的查詢時間複雜度是O(1),但是咱們能夠算一下,一個用戶名爲四個漢字,一個漢字佔用兩個字節(Unicode狀況下),那麼一共有八億個字節。一共佔用763M的內存(這個裏面不包括對象佔用的空間,也不包括哈希表中浪費的空間),而實際狀況佔用的空間會比這個多得多。函數

那麼有什麼好方法解決這個問題呢?這個就是咱們要講的布隆過濾器。首先咱們以布隆的技能來形象的解釋下布隆過濾器的優缺點:網站

  • 並使來自目標方向的第一次攻擊變得無效:若是布隆過濾器判斷數據不存在則數據絕對不存在。
  • 布隆還將攔截敵方的飛行道具:這個就是布隆過濾器的特色,數據先通過布隆過濾器,查詢數據是否已經存在。若是布隆過濾器判斷用戶名不存在/或者存在,數據纔可以繼續向下走。
  • 減小30/32.5/35/37.5/40%的後續傷害:在前面的判斷中,能夠判斷數據絕對不存在,可是若是判斷數據存在,則數據也可能不存在
  • 技能加點是不能取消的:布隆過濾器只能插入數據,而不能刪除數據。

原理簡介

布隆過濾器的原理和哈希表的原理有點相似,一樣須要使用hash函數,可是在布隆過濾器中,須要使用多個hash函數。布隆過濾器的原理仍是比較簡單的。ui

咱們有一個數組bitArray,對,就是一個位數組,長度位m。只存0和1那種。此時咱們有一個key,和k個hash函數,所以咱們能夠獲得k個key被hash事後的數。而後咱們分別對hash事後的值取餘(對m取餘)獲得x,而後將bitArray中x位置置爲1。spa

原理圖片以下所示(圖片來源code

在前面咱們介紹過布隆過濾器若是判斷數據存在,實際上數據也可能不存在。若是將布隆過濾器應用於垃圾郵件過濾系統,則就會出現「寧肯錯殺一千,也決不放過一個」的這種狀況。那麼爲何會形成這種狀況呢?實際上,這就和哈希表中哈希衝突的狀況同樣,由於可能會出現兩個key值通過k個hash函數以後,取餘以後的結果是同樣的。因此,在布隆過濾器中可能會出現誤判,因此有一個概念叫作誤算率。htm

數學推導

上面咱們知道布隆過濾器中,有一個誤算率,固然咱們是想將誤判下降到最小(key的數量和數組bitArray的長度都是肯定的)。so,讓咱們用數學公式來推導一下。

首先咱們有n個key,bitArray的長度位m,hash函數的個數是k,失誤率是p(通常很小),推導以下:

  1. 誤判的機率:

    若是hash函數足夠優秀(每個key都等機率的分配到數組中的某一個位置)。對於一個hash函數來講,bitArray中某個位置被置1的機率是\(\frac{1}{m}\),則不被置1的機率是\(1-\frac{1}{m}\),由於咱們有k個hash函數,因此在k個hash函數中,某個位置不被置1的機率是:
    \[ (1-\frac{1}{m})^k \]
    由於插入了n個key,某個位置置1的機率是:(不被置1的機率是\((1-\frac{1}{m})^{kn}\)
    \[ 1-(1-\frac{1}{m})^{kn} \]
    若是咱們此時去查詢某個key是否存在,出現誤判(也就是說在bitArray中k個位置都出現了1)的機率是:
    \[ [1-(1-\frac{1}{m})^{kn}]^{k} \]

  2. 選擇最小的誤判機率:

    根據數學知識咱們知道:

\[ lim_{n\rightarrow+\infty}(1+x)^{\frac{1}{x}} = e \]

​ 因此:
\[ [1-(1-\frac{1}{m})^{kn}]^{k} = [1-(1-\frac{1}{m})^{-m\frac{-kn}{m}}]^{k} = [1-e^{\frac{-kn}{m}}]^k \]
​ 而後令\(a=e^{\frac{-n}{m}}\) ,所以機率是:
\[ f(k)=(1-a^{k})^k \]
咱們須要求得即是\(f(k)\)的最小值。對\(f(k)\)進行變換求導:
\[ \begin{align}&f(k) = (1-a^k)^k \\&lnf(k) = kln(1-a^k) \\&而後進行求導\\ &\frac{1}{f(k)}f'(k) = ln(1-a^k) - \frac{ka^klna}{1-a^k}\\&\because a=e^{\frac{-n}{m}} \\&\therefore a < 1\\&\therefore 0< f(k)=(1-a^{k})^k <1\\&令f'(k) = 0,則 ln(1-a^k) - \frac{ka^klna}{1-a^k} =0\\&\therefore (1-a^k)ln(1-a^k) = ka^klna = a^kln{a^k}\\&\therefore (1-a^k) = a^k \\&\therefore a^k = \frac{1}{2}\\&\therefore e^{\frac{-nk}{m}} = \frac{1}{2} \\&\therefore \frac{nk}{m} = ln2 \\&\therefore k = \frac{mln2}{n} = 0.7\frac{m}{n}\\&因此誤判率p是:\\&[1-e^{\frac{-kn}{m}}]^k = (1-e^{ln\frac{1}{2}})^{\frac{mln2}{n}} \\&=(\frac{1}{2})^{-ln2\frac{m}{n}}\\&≈0.6185^\frac{m}{n}&\end{align} \]
從上面咱們能夠知道,若是想讓誤判率一直維持穩定,那麼則m和n要維持線性增長。固然,若是是其餘變量保持不變,也能夠用上面的方法進行求出。

空間佔用狀況

在這裏咱們能夠很簡單的看出來,使用布隆過濾器以後,空間佔用率仍是蠻低的,仍是以上面的例子舉例:有一億個用戶,在咱們保證保證錯誤率位0.01%的狀況下,咱們須要大概19億位的空間來保存數據(大約是230M的空間)。其中須要的散列函數的個數\(k=13\)。相比前面仍是節省了蠻多的空間。

markdown 寫數學公式仍是蠻爽的(●'◡'●)

相關文章
相關標籤/搜索