海量數據判重——布隆過濾器(Bloom filter)

布隆過濾器

布隆過濾器(Bloom filter)是一個高空間利用率數據結構,由Burton Bloom於1970年提出。被用於測試一個元素是否在集合中(由於集合無重複元素的性質,可用來判重)。

布隆過濾器的優勢

布隆過濾器的思想類似位圖,主要是由一個很長的二進制向量和若干個(k個)散列映射函數組成。因爲每個元數據的存儲信息值固定,而且總的二進制向量固定。所以在內存佔用和查詢時間上都遠遠超過一般的算法。當然存在一定的不準確率(可以控制)和不容易刪除樣本數據。

應用場景

  • 黑名單:比如郵件黑名單過濾器,判斷郵件地址是否在黑名單中。

  • 排序(僅限於 BitSet) 。

  • 網絡爬蟲:判斷某個URL是否已經被爬取過。

布隆過濾器和位圖的區別

需要注意和位圖不同,布隆過濾器通常有多個散列映射函數,因爲傳入的通常是字符串,對於單獨的字符串例如‘abcd’和‘acbd’,單個散列映射函數計算的位相同,例如
在這裏插入圖片描述
當垃圾郵箱www.abcd.com已經標記的情況下,非垃圾郵箱www.acbd.com通過散列映射函數計算的地址和www.abcd.com相同,導致在誤判認爲www.acbd.com也是垃圾郵箱。
假如我們有多個地址來映射一個字符串就能大大減少這種情況。
在這裏插入圖片描述

布隆過濾器算法思想

  • 需要n個散列映射函數,每個散列映射函數將傳入的字符串映射爲不同的key值
  • 初始化,需要設置k個比特大小的數組用來映射(位圖
  • 某個key插入布隆過濾器,用散列函數計算出對應的比特位,並將其置爲1
  • 判斷某個key是否在過濾器中,需要先計算對應的比特位,判斷是否都爲1,如果成立,說明在裏面,否則不在(存在誤判情況

布隆算法優缺點

優點:不需要儲存key,節省空間

  • 在能夠承受一定的誤判時,布隆過濾器比其他數據結構有這很大的空間優勢
    缺點:
  1. 有誤判率,即存在假陽性(False Position),即不能準確判斷元素是否在集合中(補救方法:再建立一個白 名單,存儲可能會誤判的數據)
  2. 不能獲取元素本身
  3. 一般情況下不能從布隆過濾器中刪除元素
  4. 如果採用計數方式刪除,可能會存在計數迴繞問題 。

誤判產生的原因

如下圖假設有一個非垃圾郵箱www.dcba.com,經過計算的比特位都被標記,就會被誤判爲垃圾郵箱。
在這裏插入圖片描述
根據布隆過濾器的原理,一般情況下不能從布隆過濾器中刪除元素。

實現簡易布隆過濾器

到這裏,我們來實現一個簡易的布隆過濾器,不過有一個問題要解決,映射n個數字要開多大的空間?有一個公式k = (m/n)In用來計算,ln大約2.7,n是數據,m是開多少位,經過計算大約爲4到5倍左右,爲了簡單,我們取5倍。
位圖的代碼在筆者上一篇博客 數據結構-------位圖中說明。
以下爲簡易布隆過濾器的實現。

template<class K=string>
struct _Hanc1
{
	size_t operator()(const K& str)
	{
		size_t hash = 0;
		for (int i=0;  i < str.size(); i++)
		{
			hash = str[i] * 769+ hash;
		}

		return hash;
	}

};

template<class K = string>
struct _Hanc2
{
	size_t operator()(const K& str)
	{
		size_t hash = 0;
		for (int i=0;  i < str.size(); i++)
		{
			hash = str[i] * 97 + hash;
		}
		
		return hash;

	}

};

template<class K = string>
struct _Hanc3
{
	size_t operator()(const K& str)
	{
		size_t hash = 0;
		for (int i=0;  i < str.size(); i++)
		{
			hash = str[i] * 389 + hash;
		}

		return hash;
	}

};

template<class K = string
,class Hanc1 = _Hanc1<K>
,class Hanc2 = _Hanc2<K>
,class Hanc3 = _Hanc3<K>
>
class Bloomfilter
{
public:
	Bloomfilter(size_t size )
		:_bf(size*5)
		,_size(size*5)
	{}

	void insert(const K& k)
	{
		size_t index1 = Hanc1()(k) % _size;
		size_t index2 = Hanc2()(k) % _size;
		size_t index3 = Hanc3()(k) % _size;

		_bf.insert(index1);
		_bf.insert(index2);
		_bf.insert(index3);

	}

	bool test(const K& k)
	{
		size_t index1 = Hanc1()(k) % _size;
		size_t index2 = Hanc2()(k) % _size;
		size_t index3 = Hanc3()(k) % _size;
		
		return _bf.findbit(index1) 
			&& _bf.findbit(index2) 
			&& _bf.findbit(index3);//如果返回真,可能是不準確的,返回假是準確的。
	}

private:
	BitMap _bf;
	size_t _size;

};



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章