【布隆過濾器】過濾器中的戰鬥機

轉載請代表出處算法

背景

對 布隆過濾器 畫個重點,它應該在 「過濾」 二字上,這個算法的重點在於把曾經來過跟從將來過的事物區分開,具體過濾那種事物(曾經來過or從將來過),由具體場景決定。它通常用於數據庫存儲中過濾不存在的行(減小訪問磁盤),推薦系統去重等等場景。數據庫

SET

說到去重,很容易想到STL中的SET容器,它自己自帶去重功能,並且還能查詢。
那麼最簡單的方式,直接用SET。每次操做的時候,先查詢該事物是否存在?app

  • 若是存在直接返回「存在」結果。
  • 若是不存在就插入其中,而後再返回「不存在」結果。

具體以下圖所示:.net

這種方式準確率是絕對準確的,可是這種方式耗費的內存也是巨大的。
假設每一個事物須要 K 字節,那麼若是有 M 個事物,一共須要 K * M 字節。那麼咱們能不能縮小這裏的內存呢?
稍微損失一點準確率換取內存?具體見下面HashMap的方式blog

HashMap

在上一種方式中,它存儲了具體的事物信息,其實在咱們這個場景中是不須要的。咱們須要的只是這個事物是否存在就好了,因此HashMap的方式誕生了。
這種算法在每次操做的時候,先查詢該事物是否存在,內存

  • 若是存在返回「存在」結果。
  • 若是不存在就在特定位置置1,而後再返回「不存在」結果。

衆所周知,hash都是有必定機率衝突的,並且當哈希桶快滿的時候,衝突率更高。
接下來咱們來看看這裏的衝突率有多少?假設哈希桶長度爲M,那麼每次插入到特定一個桶的機率是:$$(1-\frac{1}{M})$$
而沒有插入特定桶的機率是:$$1-(1-\frac{1}{M})$$
而後假設目前已經插入N個事物,那麼特定桶中爲0的機率是:$$(1-\frac{1}{M})^N$$
特定桶中爲1的機率是:$$1-(1-\frac{1}{M})^N$$get

假設如今要新插入一個事物,此次插入的衝突率就是:hash

\[1-(1-\frac{1}{M})^N = 1- ((1+\frac{1}{-M})^{-M})^{-\frac{N}{M}} \approx 1 - e^{-\frac{N}{M}} \]

因此這裏在N接近M的時候,衝突率是很高的,這種算法就徹底失效了。
有沒有辦法解決這種狀況呢?布隆過濾器能下降這裏衝突率。class

布隆過濾器

布隆過濾器是用了多hash的方式下降了衝突率的。
這種算法在每次操做的時候,先查詢該事物在K個hash桶中是否都存在,容器

  • 若是存在返回「存在」結果。
  • 若是有一個桶不存在,就在K個hash桶中所有置1,而後再返回「不存在」結果。

接下來咱們來看看這種有K個Hash加持的算法,衝突率有多少?假設哈希桶長度爲M,那麼每次插入到特定一個桶的機率是:$$K (1-\frac{1}{M})$$
而沒有插入特定桶的機率是:$$(1-(1-\frac{1}{M}))^K$$
而後假設目前已經插入N個事物,那麼特定桶中爲0的機率是:$$(1-\frac{1}{M})^{NK}$$
特定桶中爲1的機率是:$$1-(1-\frac{1}{M})^{NK}$$
假設如今要新插入一個事物,此次插入的衝突率就是:

\[(1-(1-\frac{1}{M})^{NK})^K= (1- ((1+\frac{1}{-M})^{-M})^{-\frac{NK}{M}} )\approx (1 - e^{-\frac{NK}{M}})^K \]

因爲這裏有K次方的加持,它的衝突率小不少的。
通常來講,在使用布隆過濾器的時候,N是由場景已經決定了的,怎麼選擇M跟K呢?
P爲衝突率

\[M=-\frac{NlnP}{(ln2)^2} \]

\[K=\frac{M}{N}ln2 \]

因爲布隆過濾器都是直接置1,因此它根本沒法刪除一個事物的。有沒有辦法支持它刪除+統計個數呢?

布隆過濾器升級版

想要刪除特定事物,那其實也很簡單。
直接把布隆過濾器的位存儲改爲數字存儲就好了。
那麼在每次操做的時候,跟布隆過濾器差異的點在於:

  • 不管是否存在都在K個hash桶中加1,
  • 而後在須要刪除的時候,就在K個hash桶中減1。
    具體以下圖所示:

這種算法是已經支持刪除+統計了,相應的它的內存佔用可不是翻倍這麼簡單的。 不一樣的場景用不一樣的算法吧,最合適的纔是效果最好滴。

相關文章
相關標籤/搜索