【布隆過濾器】過濾器中的戰鬥機
阿新 • • 發佈:2020-12-14
>[轉載請表明出處](https://blog.csdn.net/ERKELIU/article/details/111126716)
# 背景
> 對 布隆過濾器 畫個重點,它應該在 “過濾” 二字上,這個演算法的重點在於把曾經來過跟從未來過的事物區分開,具體過濾那種事物(曾經來過or從未來過),由具體場景決定。它一般用於資料庫儲存中過濾不存在的行(減少訪問磁碟),推薦系統去重等等場景。
# SET
說到去重,很容易想到STL中的SET容器,它本身自帶去重功能,而且還能查詢。
那麼最簡單的方式,直接用SET。每次操作的時候,先查詢該事物是否存在?
* 如果存在直接返回“存在”結果。
* 如果不存在就插入其中,然後再返回“不存在”結果。
具體如下圖所示:
這種方式準確率是絕對準確的,但是這種方式耗費的記憶體也是巨大的。
假設每個事物需要 K 位元組,那麼如果有 M 個事物,一共需要 K * M 位元組。那麼我們能不能縮小這裡的記憶體呢?
稍微損失一點準確率換取記憶體?具體見下面**HashMap**的方式
# 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$$
假設現在要新插入一個事物,這次插入的衝突率就是:
$$1-(1-\frac{1}{M})^N = 1- ((1+\frac{1}{-M})^{-M})^{-\frac{N}{M}} \approx 1 - e^{-\frac{N}{M}}$$
所以這裡在N接近M的時候,衝突率是很高的,這種演算法就完全失效了。
有沒有辦法解決這種情況呢?布隆過濾器能降低這裡衝突率。
# 布隆過濾器
布隆過濾器是用了多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。
具體如下圖所示:
這種演算法是已經支援刪除+統計了,相應的它的記憶體佔用可不是翻倍這麼簡單的。
不同的場景用不同的演算法吧,最合適的才是效果最好滴。