您好,登錄后才能下訂單哦!
先來(lái)看幾個(gè)比較常見(jiàn)的例子
字處理軟件中,需要檢查一個(gè)英語(yǔ)單詞是否拼寫正確
在 FBI,一個(gè)嫌疑人的名字是否已經(jīng)在嫌疑名單上
在網(wǎng)絡(luò)爬蟲里,一個(gè)網(wǎng)址是否被訪問(wèn)過(guò)
yahoo, gmail等郵箱垃圾郵件過(guò)濾功能
這幾個(gè)例子有一個(gè)共同的特點(diǎn): 如何判斷一個(gè)元素是否存在一個(gè)集合中?
數(shù)組
鏈表
樹(shù)、平衡二叉樹(shù)、Trie
Map (紅黑樹(shù))
哈希表
雖然上面描述的這幾種數(shù)據(jù)結(jié)構(gòu)配合常見(jiàn)的排序、二分搜索可以快速高效的處理絕大部分判斷元素是否存在集合中的需求。但是當(dāng)集合里面的元素?cái)?shù)量足夠大,如果有500萬(wàn)條記錄甚至1億條記錄呢?這個(gè)時(shí)候常規(guī)的數(shù)據(jù)結(jié)構(gòu)的問(wèn)題就凸顯出來(lái)了。數(shù)組、鏈表、樹(shù)等數(shù)據(jù)結(jié)構(gòu)會(huì)存儲(chǔ)元素的內(nèi)容,一旦數(shù)據(jù)量過(guò)大,消耗的內(nèi)存也會(huì)呈現(xiàn)線性增長(zhǎng),最終達(dá)到瓶頸。有的同學(xué)可能會(huì)問(wèn),哈希表不是效率很高嗎?查詢效率可以達(dá)到O(1)。但是哈希表需要消耗的內(nèi)存依然很高。使用哈希表存儲(chǔ)一億 個(gè)垃圾 email 地址的消耗?哈希表的做法:首先,哈希函數(shù)將一個(gè)email地址映射成8字節(jié)信息指紋;考慮到哈希表存儲(chǔ)效率通常小于50%(哈希沖突);因此消耗的內(nèi)存:8 * 2 * 1億 字節(jié) = 1.6G 內(nèi)存,普通計(jì)算機(jī)是無(wú)法提供如此大的內(nèi)存。這個(gè)時(shí)候,布隆過(guò)濾器(Bloom Filter)就應(yīng)運(yùn)而生。在繼續(xù)介紹布隆過(guò)濾器的原理時(shí),先講解下關(guān)于哈希函數(shù)的預(yù)備知識(shí)。
哈希函數(shù)的概念是:將任意大小的數(shù)據(jù)轉(zhuǎn)換成特定大小的數(shù)據(jù)的函數(shù),轉(zhuǎn)換后的數(shù)據(jù)稱為哈希值或哈希編碼。下面是一幅示意圖:
可以明顯的看到,原始數(shù)據(jù)經(jīng)過(guò)哈希函數(shù)的映射后稱為了一個(gè)個(gè)的哈希編碼,數(shù)據(jù)得到壓縮。哈希函數(shù)是實(shí)現(xiàn)哈希表和布隆過(guò)濾器的基礎(chǔ)。
巴頓.布隆于一九七零年提出
一個(gè)很長(zhǎng)的二進(jìn)制向量 (位數(shù)組)
一系列隨機(jī)函數(shù) (哈希)
空間效率和查詢效率高
有一定的誤判率(哈希表是精確匹配)
布隆過(guò)濾器(Bloom Filter)的核心實(shí)現(xiàn)是一個(gè)超大的位數(shù)組和幾個(gè)哈希函數(shù)。假設(shè)位數(shù)組的長(zhǎng)度為m,哈希函數(shù)的個(gè)數(shù)為k
以上圖為例,具體的操作流程:假設(shè)集合里面有3個(gè)元素{x, y, z},哈希函數(shù)的個(gè)數(shù)為3。首先將位數(shù)組進(jìn)行初始化,將里面每個(gè)位都設(shè)置位0。對(duì)于集合里面的每一個(gè)元素,將元素依次通過(guò)3個(gè)哈希函數(shù)進(jìn)行映射,每次映射都會(huì)產(chǎn)生一個(gè)哈希值,這個(gè)值對(duì)應(yīng)位數(shù)組上面的一個(gè)點(diǎn),然后將位數(shù)組對(duì)應(yīng)的位置標(biāo)記為1。查詢W元素是否存在集合中的時(shí)候,同樣的方法將W通過(guò)哈希映射到位數(shù)組上的3個(gè)點(diǎn)。如果3個(gè)點(diǎn)的其中有一個(gè)點(diǎn)不為1,則可以判斷該元素一定不存在集合中。反之,如果3個(gè)點(diǎn)都為1,則該元素可能存在集合中。注意:此處不能判斷該元素是否一定存在集合中,可能存在一定的誤判率??梢詮膱D中可以看到:假設(shè)某個(gè)元素通過(guò)映射對(duì)應(yīng)下標(biāo)為4,5,6這3個(gè)點(diǎn)。雖然這3個(gè)點(diǎn)都為1,但是很明顯這3個(gè)點(diǎn)是不同元素經(jīng)過(guò)哈希得到的位置,因此這種情況說(shuō)明元素雖然不在集合中,也可能對(duì)應(yīng)的都是1,這是誤判率存在的原因。
將要添加的元素給k個(gè)哈希函數(shù)
得到對(duì)應(yīng)于位數(shù)組上的k個(gè)位置
將這k個(gè)位置設(shè)為1
將要查詢的元素給k個(gè)哈希函數(shù)
得到對(duì)應(yīng)于位數(shù)組上的k個(gè)位置
如果k個(gè)位置有一個(gè)為0,則肯定不在集合中
如果k個(gè)位置全部為1,則可能在集合中
下面給出python的實(shí)現(xiàn),使用murmurhash算法
import mmh4from bitarray import bitarray# zhihu_crawler.bloom_filter# Implement a simple bloom filter with murmurhash algorithm.# Bloom filter is used to check wether an element exists in a collection, and it has a good performance in big data situation.# It may has positive rate depend on hash functions and elements count.BIT_SIZE = 5000000class BloomFilter: def __init__(self): # Initialize bloom filter, set size and all bits to 0 bit_array = bitarray(BIT_SIZE) bit_array.setall(0) self.bit_array = bit_array def add(self, url): # Add a url, and set points in bitarray to 1 (Points count is equal to hash funcs count.) # Here use 7 hash functions. point_list = self.get_postions(url) for b in point_list: self.bit_array[b] = 1 def contains(self, url): # Check if a url is in a collection point_list = self.get_postions(url) result = True for b in point_list: result = result and self.bit_array[b] return result def get_postions(self, url): # Get points positions in bit vector. point1 = mmh4.hash(url, 41) % BIT_SIZE point2 = mmh4.hash(url, 42) % BIT_SIZE point3 = mmh4.hash(url, 43) % BIT_SIZE point4 = mmh4.hash(url, 44) % BIT_SIZE point5 = mmh4.hash(url, 45) % BIT_SIZE point6 = mmh4.hash(url, 46) % BIT_SIZE point7 = mmh4.hash(url, 47) % BIT_SIZE return [point1, point2, point3, point4, point5, point6, point7]
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。