11.21 高級算法篇:布隆過濾器?非也,布穀鳥過濾器是也

過濾器在數據科學中的應用十分廣泛,包括數據庫查詢、數據快速檢索,數據去重等等。過濾器的出現是為了解決在大量數據的環境下,能夠更好更快的(節省計算資源或者存儲資源)篩查數據的需求。實際的應用場景有:

爬蟲程序的URL識別:即爬蟲在訪問 URL 時對 URL 進行判斷,如果訪問過(在集合中)就不訪問,如果沒有訪問過那麼就訪問然後放入已訪問集合,提高爬蟲效率。

垃圾郵件地址的儲存,如何判斷一封郵件是否是垃圾郵件,這樣要對郵件地址進行判斷,看看是否是在垃圾郵件地址集合中。但實際上郵件地址太多,如果全部儲存的話佔用大量存儲資源並且在比較的時候也會佔用大量的計算資源,所以用過濾器來存儲判斷可以解決問題。

在 LevelDB 數據庫引擎中使用了 LSM tree,由於設計時為了優化寫性能抑制了讀性能,在磁盤中(sstable)查找 key 時(雖然已經使用文件索引並且定期合併文件來減少文件的數量,但是面對海量數據增量時還是捉襟見肘)使用 Bloom filter 這種在內存中的高效方法來判斷文件中是否包含key。

接下來介紹最基本的兩個過濾器,幫助大家理解過濾器技術的實現。

Bloom filter

Bloom filter 使用 hash 函數的散列技術存儲信息的存在狀態而不是存儲信息本身,常常用於判斷一個信息是否在一個集合中,這樣只需要幾個bit的空間就能解決問題。

基本原理

bloom filter作為一種海量數據處理算法,其要點在於用於存儲的位數組和用於處理的hash函數(一般有多個,並且為了精確度和數據量增加)。

初始化存儲空間:bloom filter首先在內存中開闢一塊儲存空間,並將裡面的bit位全部置為0,表示尚未有數據進行處理或者儲存。

高級算法篇:布隆過濾器?非也,布穀鳥過濾器是也

映射集合中的數據:bloom filter通過設置k個hash函數,將一個集合中的所有數據或者說信息映射到儲存空間中,被映射到的區域bit位設置為1。

高級算法篇:布隆過濾器?非也,布穀鳥過濾器是也

集合中的數據映射

判斷數據是否屬於集合:假設任何一個信息或者數據key,要判斷其是否在集合中,bloom filter將key帶入k個hash函數進行映射(fi=fi(key)),然後判斷其映射到的區域是否全部為1,如果全部為1,那麼信息或者數據key表示在集合中,只要有一個映射位置為0,那麼表示信息或者數據key不在集合中。

優缺點

優點:存儲數據量小,節省存儲及計算空間

缺點:只能對集合添加元素,無法刪除(也並非完全不能,可以使用 bloom filter 的變種 CounterBloom Filte,該過濾器給每一位存儲空間分配一個計數空間,每次刪除時候計數減1。這個計數空間通常需要4位計數16則溢出。另外根據數據量,在滿足一定錯誤率的情況下 hash 函數個數 k 需要變動。

高級算法篇:布隆過濾器?非也,布穀鳥過濾器是也

Cuckoo filter理解

原理

Cuckoo filter 同樣使用哈希表來實現數據到實際存儲區域的映射,不同於 Bloom filer 的是Cuckoo filter中只採用兩個哈希映射函數 H1 和 H2,H3用於計算數據的 fingerprint,減小存儲量。他們的關係如下:

H1(key) = hash1(key)
H2(key) = H1(key) xor H1(key’s fingerprint)
H3(key) = key’s fingerprint = hash(key)

當一個數據需要存儲的時候,Cuckoo filter 使用兩個哈希函數進行映射,只要有一個映射到的區域為空,那麼就將數據的指紋信息存儲到相應的區域。如果兩個區域都被佔用,那麼將原來佔有那個存儲區域的數據指紋踢出存儲區用來存儲新到的數據,原來的數據重新使用另外一個哈希函數映射存儲,依次反覆。

當然這個過程可能無限反覆下去,那麼一般會對踢出操作設一個閾值,超過閾值則認為過濾器容量不足,需要對其進行擴容。

這個踢出的過程類似於布穀鳥下蛋的過程,所以稱其為布穀鳥過濾器。

附:散列技術

散列技術(也就是 hash 映射)因為在 bloom 過濾器 與 cuckoo 過濾器中就使用到了 hash 技術去映射,主要是散列表查找(哈希表):

  • 引入

在順序表查找(逐個比較)乃至有序表查找(折半查找)的時候難免需要使用比較,但這太消耗資源,考慮一種方法通過關鍵字Key直接得到想要查找的記錄內存存儲的位置: 存儲位置 = f(關鍵字Key),這樣不需要比較就能獲得需要記錄的儲存位置,通過一個f(key)映射關係就能查找到儲存位置,這種用於存儲的技術稱之為散列技術,其中f為hash函數。

  • 散列技術既是存儲方法,又是查找方法

最適合精確查找,也就是查找與給定值相等的記錄。

不適合一個關鍵字對應多個記錄(set is a class,key = 男)以及範圍查找(set is a class,Q:18

設計一個簡單、均勻、存儲利用率高的散列函數是關鍵。

  • 散列函數的構造方法

設計原則:計算簡單(提高效率)、散列地址分佈均勻(存儲空間的利用率)

  1. 直接定址法:f(key) = keyf(key) = a * key + b( a、b為常數 )
  2. 數字分析法:數字反轉(1234 -> 4321)、環形位移(1234 -> 4123)
  3. 摺疊法:分解數字相加(或者別的運算)(9876543210 -> 987+654+321+0)
  4. 除留餘數法:f(key) = key mod p (p<=m)
  5. 隨機數法:f(key) = random(key)

如果是字符串或者中文可以轉化為ASCII或者Unicode碼來使用上面介紹的方法。

  • 處理散列衝突的方法

如果兩個以上的關鍵字通過hash函數映射後都指向一個儲存地址的話,那就會產生衝突,所以解決衝突也是一個關鍵問題,主要有如下一些方法:

  1. 開放定址法;
  2. 再散列函數法;
  3. 鏈地址法:在原地址製造鏈表存儲,衝突時就是為鏈表添加節點;
  4. 公共溢出法,就是為衝突的區域(信息)製造一個統一存儲的區域;
"


分享到:


相關文章: