特殊的資料結構(布隆過濾器)的原理和實現及探究
一、布隆過濾器的使用價值
有時候我們需要判斷一個元素是否在一個集合中。比如,在字處理軟體中,需要檢查一個單詞是否拼寫正確(也就是要判斷它是否在已知的字典裡);在警察系統中,一個嫌疑人的名字是否出現在嫌疑名單上;在網路爬蟲裡,一個網址是否已經被訪問過,等等。
最直接的方法就是講集合中的元素存在計算機中,遇到一個新元素時,將它和集合中的元素直接比較即可。一般來講,計算機中的集合是用雜湊表(Hash Table)來儲存的。它的好處是快速準確,缺點是耗費儲存空間。
為什麼說耗費儲存空間呢?其根本原因是雜湊表方法需要把實實在在的具有特定長度(每個Email地址對應成一個8位元組的資訊指紋)的元素的資訊指紋儲存在記憶體或硬碟中的雜湊表中,這個儲存量在實際應用中一般是相當大的。比如每儲存一億個Email地址,需要0.8G大小的數字指紋儲存空間,考慮到雜湊表的儲存空間利用率一般只有一半,所以需要1.6G的儲存空間。如果儲存幾十億上百億的Email地址,那就需要百億位元組的記憶體儲存空間。
而布隆過濾器只需要雜湊表1/8到1/4的大小就能解決同樣的問題,它實際上是一個很長的二進位制向量和一系列的隨機對映函式。
下面以WEB頁面地址的儲存為例來說明布隆過濾器的工作原理:
假定儲存一億個WEB頁面地址,先建立一個16億二進位制(位元),即2億位元組的向量,然後將這16億個二進位制位清零。對於每一個WEB頁面地址X,用8個隨機數產生器(f1,f2,…,f8)。再用一個隨機數產生器G把這8個資訊指紋對映到1-16億中的8個自然數g1,g2,…g8。現在把這8個位置的二進位制位都置為1。對著一億個WEB頁面地址都進行這樣的處理後,一個針對WEB頁面的布隆過濾器就建成了,見下圖。
蒲隆地過濾器的對映方法:
現在,讓我們看看如何用布隆過濾器來檢測一個WEB網頁地址Y是否已經被我們收錄。用相同的8個隨機數生成器(f1,f2,…,f8)對這個WEB網頁地址產生8個資訊指紋s1,s2,…s8,然後將這8個指紋對應到布隆過濾器的8個二進位制位,分別是t1,t2,…,t8。如果Y已被收錄,顯然t1,t2,…,t8對應的8個二進位制位一定是1。通過這樣的方式我們能夠很快地確定一個WEB頁面是否已被我們收錄。
二、布隆過濾器的實現
布隆過濾器實現程式碼:
#encoding=UTF-8
'''
Created on 2014年6月21日
@author: jin
'''
import BitVector
class MyHash():#雜湊類,根據不同引數初始化後作為不同的雜湊函式
def __init__(self, cap, seed):
self.cap = cap
self.seed = seed
def hash(self, value): #計算雜湊值得過程
ret = 0
for i in range(len(value)):
ret += self.seed*ret + ord(value[i]) #ord()函式計算傳入的url字串中每一個字元在ASCII碼錶中對應的順序值
return (self.cap-1) & ret #返回雜湊值,即在位元序列中的位置
class BloomFilter():
def __init__(self, BIT_SIZE=1<<31):
self.BIT_SIZE = 1 << 31 #不攏過濾器的位元數,
self.seeds = [5, 7, 11, 13,19, 31, 37, 61] #8個種子,用於產生hash函式
self.bitset = BitVector.BitVector(size=self.BIT_SIZE)
self.hashFuncList = []
for i in range(len(self.seeds)):
self.hashFuncList.append(MyHash(self.BIT_SIZE, self.seeds[i])) #對每個種子,建立一個MyHash物件,一共8個
def insert(self, value): #插入值,這裡並非真正地插入並儲存,而是把該值對應的8個位置的位元位置為1
for function in self.hashFuncList:
locationBit = function.hash(value) #計算應該置為1的位元位
self.bitset[locationBit] = 1
def isContaions(self, value):
if value == None:
return False
ret = True
for f in self.hashFuncList:
locationBit = f.hash(value)
ret = ret & self.bitset[locationBit] #可以看出,對8個雜湊函式,只要有一個為0,那麼將返回0,即該值尚未存在
return ret
def Main(): #主函式
fd = open("urls.txt") #有重複的網址 http://www.kalsey.com/tools/buttonmaker/
bloomfilter = BloomFilter()
while True:
url = fd.readline()
if cmp(url, 'exit') == 0:
print 'complete and exit now'
break
elif bloomfilter.isContaions(url) == False:
bloomfilter.insert(url)
else:
print 'url :%s has exist' % url
Main()
url.txt內部儲存有一系列網址,最後一行是‘exit’,內容如下:
http://sourceforge.net/robots.txt
http://sourceforge.net/
http://sourceforge.net
http://sourceforge.net and https://sourceforge.net
http://sourceforge.net/sitemap.xml
http://sourceforge.net/allura_sitemap/sitemap.xml
http://sourceforge.net/directory_sitemap.xml
http://a.fsdn.com
http://a.fsdn.com/con/img/sftheme/favicon.ico
http://a.fsdn.com/con/js/min/sf.head.js
http://a.fsdn.com/con/js/sftheme/dd_belatedpng.js
http://fonts.googleapis.com
http://fonts.googleapis.com/css
http://a.fsdn.com/con/css/sf.css
http://sourceforge.net/blog/feed/
http://email.playtime.uni.cc/
http://services.nexodyne.com/email/
http://gizmo967.mgs3.org/Gmail/
http://www.hkwebs.net/catalog/tools/gmail/
http://sagittarius.dip.jp/~toshi/cgi-bin/designmail/designmail.html
http://www.eoool.com/
http://sourceforge.netand
https://sourceforge.net
http://a.fsdn.com/con/js/adframe.js
http://sourceforge.net/directory/
http://kalsey.com/tools/buttonmaker/
http://www.lucazappa.com/brilliantMaker/buttonImage.php
http://www.feedforall.com/public/rss-graphic-tool.htm
http://www.yugatech.com/make.php
http://www.hkwebs.net/catalog/tools/buttonmaker/index.php
http://phorum.com.tw/Generator.aspx
http://www.logoyes.com/lc_leftframe.htm
http://cooltext.com/Default.aspx
exit
執行效果如下,可以看到未發生儲存地址衝突:
complete and exit now
往url.txt裡面再增加一個原來未有的網址:
http://www.kalsey.com/tools/buttonmaker/
再次執行,竟然發生了衝突,如下:
url :http://www.kalsey.com/tools/buttonmaker/
has exist
complete and exit now
這說明此網址和另外一個網址對應的8個資訊指紋相同,雖然它們本身的值是不同的,這就產生了衝突。
可以看到布隆過濾器有一定的誤識別率。下面我們對其進行分析。
三、誤識別率的問題
假定布隆過濾器有m位元,裡面有n個元素,每個元素對應k個資訊指紋的雜湊函式,當然m個位元里有0也有1。我們假定某個位元為0,在這個布隆過濾器裡插入一個元素,他的第一個雜湊函式會把過濾器中的某個位元置為1,理想情況下,任一位元位被置1的概率是1/m,它依然為0的概率則是1-1/m。
對於過濾器中的一個特定位置,如果這個元素的k個雜湊函式都沒有把它設定成1,其概率是
。如果過濾器插入第二個元素,這個特定位置仍不被置1的概率是
,類似的,插入n個元素其仍為0的概率是
。反過來,一個位元在插入n個元素後,被置1的概率則是
現在假定這n個元素都放到布隆過濾器中了,新來的一個不在集合中的元素,由於它的資訊指紋的雜湊函式都是隨機的,因此,它的第一個雜湊函式正好命中某個值為1的位元的概率就是上述概率。一個不再集合中的元素被誤識別為已經在集合中,需要所有的雜湊函式對應的位元值均為1,其概率為
我們下面對簡化的誤識別率的公式進行研究。
圖2 誤識別率與m/n的關係
圖2的程式碼:
k=8
r = np.linspace(1,50,1000)
p = np.power(1-np.exp(-k/r),k)
plt.title('misjudgment & m/n')
plt.plot(r,p)
plt.xlabel('m/n')
plt.ylabel('misjudgment')
plt.show()
圖3 誤識別率與k的關係
圖3的程式碼:
handlebars
r = 30;
k = np.linspace(1,10,100)
p = np.power(1-np.exp(-k/r),k)
plt.title('misjudgment & k')
plt.xlabel('k')
plt.ylabel('misjudgment')
plt.plot(k,p)
plt.show()
本群免費分享學習資料(C/C++,Linux,golang,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,ffmpeg,TCP/IP,協程,DPDK,嵌入式)等。
交流討論領取資料請加群Q:1106675687。
相關文章
- 詳解布隆過濾器的原理和實現過濾器
- 布隆過濾器的原理及應用過濾器
- 布隆過濾器(BloomFilter)原理 實現和效能測試過濾器OOMFilter
- 詳解布隆過濾器原理與實現過濾器
- PHP實現布隆過濾器PHP過濾器
- 實現布隆過濾器的三種方式過濾器
- Guava的布隆過濾器Guava過濾器
- 布隆過濾器過濾器
- 冷飯新炒:理解布隆過濾器演算法的實現原理過濾器演算法
- Redis 中的布隆過濾器Redis過濾器
- 利用 Redis 的 bitmap 實現簡單的布隆過濾器Redis過濾器
- 演算法(3)---布隆過濾器原理演算法過濾器
- Redis布隆過濾器分析與總結Redis過濾器
- C++進階(點陣圖+布隆過濾器的概念和實現+海量資料處理)C++過濾器
- 淺談布隆過濾器過濾器
- Redis-布隆過濾器Redis過濾器
- 布隆過濾器(Bloom Filter)過濾器OOMFilter
- 大白話布隆過濾器過濾器
- 布隆過濾器 Bloom Filter過濾器OOMFilter
- Bloom Filter 布隆過濾器OOMFilter過濾器
- 布隆過濾器-使用場景的思考過濾器
- 【Redis的那些事 · 續集】Redis的點陣圖、HyperLogLog資料結構演示以及布隆過濾器Redis資料結構過濾器
- Redis 應用-布隆過濾器Redis過濾器
- victoriaMetrics庫之布隆過濾器過濾器
- 布隆過濾器 與 Redis BitMap過濾器Redis
- Xor過濾器:比布隆Bloom過濾器更快,更小過濾器OOM
- Redis布隆過濾器的原理和應用場景,解決快取穿透Redis過濾器快取穿透
- 從點陣圖到布隆過濾器,C#實現過濾器C#
- 品味布隆過濾器的設計之美過濾器
- 快取問題(二) 布隆過濾器(Bloom Filter) 介紹和原理快取過濾器OOMFilter
- 布隆過濾器(Bloom Filter)詳解過濾器OOMFilter
- 還有人不懂布隆過濾器嗎?過濾器
- 5分鐘掌握布隆過濾器過濾器
- 布隆過濾器實戰【防止快取擊穿】過濾器快取
- 那些有趣的演算法之布隆過濾器演算法過濾器
- 從快取穿透聊到布隆過濾器快取穿透過濾器
- Redis詳解(十三)------ Redis布隆過濾器Redis過濾器
- AI考拉技術分享--布隆過濾器實戰AI過濾器