1. 程式人生 > >【資料結構】布隆過濾器——點陣圖擴充套件

【資料結構】布隆過濾器——點陣圖擴充套件

本篇博文,旨在介紹一種可以快速檢索元素是否存在的資料結構 --- 布隆過濾器;本文從點陣圖和布隆過濾器的對比,討論了使用這兩種資料結構的不同情況;並介紹了布隆過濾器的幾種主要使用場景

布隆過濾器的引入

之前學習了點陣圖,可以快速的判斷一個整數是否存在於一個集合中

然而,現實生活中我們用的很多是字串,單用點陣圖處理不了字串,由此引來了點陣圖

布隆過濾器的思想

學過了雜湊表後我們知道字串雜湊演算法,可以將字串轉換為一個key值,然後存入點陣圖

但是,通過字串雜湊演算法後,也很有可能會造成雜湊衝突(多個字串對映成同一個key值),這樣誤判率就會很高

布隆過濾器的實現

於是,布隆過濾器就是採用同時進行多個字串雜湊演算法,進行對映;

假設我們用5個字串雜湊演算法進行對映,那麼在判斷一個字串是否存在時,只需判斷對應的五個位是否同時存在即可

布隆過濾器的程式碼實現

#pragma once

#include<iostream>
using namespace std;

//包我們自己定義的點陣圖標頭檔案
#include"BitMap.h"

//定義五個字串雜湊演算法
template<class T>
struct BKDRHash
{
	size_t operator()(const T str)
	{
		register size_t hash = 0;
		for (size_t i = 0; i < str.size(); ++i)
		{
			hash = hash * 131 + str[i];
		}
		return hash;
	}
};

template<class T>
struct SDBMHash
{
	size_t operator()(const T str)
	{
		register size_t hash = 0;
		for (size_t i = 0; i < str.size(); ++i)
		{
			hash = 65599 * hash + str[i];
		}
		return hash;
	}
};

template<class T>
struct RSHash
{
	size_t operator()(const T str)
	{
		register size_t hash = 0;
		size_t magic = 63689;
		for (size_t i = 0; i < str.size(); ++i)
		{
			hash = hash * magic + str[i];
			magic *= 378551;
		}
		return hash;
	}
};

template<class T>
struct APHash
{
	size_t operator()(const T str)
	{
		register size_t hash = 0;
		for (long i = 0; i < str.size(); ++i)
		{
			if ((i & 1) == 0)
			{
				hash ^= ((hash << 7) ^ str[i] ^ (hash >> 3));
			}
			else
			{
				hash ^= (~((hash << 11) ^ str[i] ^ (hash >> 5)));
			}
		}
		return hash;
	}
};

template<class T>
struct JSHash
{
	size_t operator()(const T str)
	{
		if (str.empty())
			return 0;
		register size_t hash = 1315423911;
		for (size_t i = 0; i < str.size(); ++i)
		{
			hash ^= ((hash << 5) + str[i] + (hash >> 2));
		}
		return hash;
	}
};

//定義布隆過濾器,5個HashFunc是五種不同的字串雜湊演算法,用來輔助實現布隆過濾器
template<typename K = string,
typename HashFunc1 = BKDRHash<K>,
typename HashFunc2 = SDBMHash<K>,
typename HashFunc3 = RSHash<K>,
typename HashFunc4 = APHash<K>,
typename HashFunc5 = JSHash<K>>
class BoolmFilter
{
public:
	BoolmFilter(size_t num)
		:_bp(num*2*5)
	{}

	size_t HashFunC1(const K& num)
	{
		HashFunc1 hf;
		size_t index = hf(num);
		return index;
	}
	size_t HashFunC2(const K& num)
	{
		HashFunc2 hf;
		return hf(num);
	}
	size_t HashFunC3(const K& num)
	{
		HashFunc3 hf;
		return hf(num);
	}
	size_t HashFunC4(const K& num)
	{
		HashFunc4 hf;
		return hf(num);
	}
	size_t HashFunC5(const K& num)
	{
		HashFunc5 hf;
		return hf(num);
	}
	void Set(const K &num)
	{
		//用五中不同的字串雜湊演算法求出五個不同的值
		size_t hf1 = HashFunC1(num);
		size_t hf2 = HashFunC2(num);
		size_t hf3 = HashFunC3(num);
		size_t hf4 = HashFunC4(num);
		size_t hf5 = HashFunC5(num);

		//測試五個雜湊值
		//cout << hf1 << " " << hf2 << " " << hf3 << " " << hf4 << " " << hf5 << endl;

		//將五個值都放入點陣圖中
		_bp.Set(hf1);
		_bp.Set(hf2);
		_bp.Set(hf3);
		_bp.Set(hf4);
		_bp.Set(hf5);
	}

	//Reset可以用引用計數來實現
	//void Reset();

	bool Find(K &num)
	{
		//分別判斷num對應的五個值是否存在於點陣圖中
		//若有一個不存在,則返回FALSE,num必定不存在
		//若都存在,則返回TRUE,但是不能確定一定存在
		int hf1 = HashFunC1(num);
		if(_bp.Find(hf1) == false)
			return false;

		int hf2 = HashFunC2(num);
		if (_bp.Find(hf2) == false)
			return false;

		int hf3 = HashFunC3(num);
		if (_bp.Find(hf3) == false)
			return false;

		int hf4 = HashFunC4(num);
		if (_bp.Find(hf4) == false)
			return false;

		int hf5 = HashFunC5(num);
		if (_bp.Find(hf5) == false)
			return false;

		return true;
	}
protected:
	//定義點陣圖 _bp;
	BitMap _bp;
};


布隆過濾器和點陣圖的對比

點陣圖的優點:

可以快速的判斷一個整數是否存在於集合中

點陣圖的缺點:

就如同其優點中說的,只能判斷整數,無法處理其他型別的資料

布隆過濾器的優點:

1、在點陣圖的基礎上利用雜湊演算法進行擴充,使之可以處理字串型別的資料

2、時間複雜度O(k),【K表示字串雜湊演算法的個數】,其效率遠高於其他的演算法

布隆過濾器的缺點:

由於雜湊衝突,布隆過濾器有一定的誤判性,如何使用適量的字串雜湊演算法是需要考慮的問題

布隆過濾器的誤區:

使用字串雜湊演算法的數量越多越好?(錯)

這裡要說明,字串雜湊演算法過多,就會導致一個字串會佔好多個位,衝突的概率還會增大

所以,要使用適量的字串雜湊演算法

布隆過濾器的幾種主要應用場景:

(1)垃圾網站、郵件的過濾

(2)在爬蟲中,如何快速的判斷一個網頁是否爬過也是布隆過濾器的使用場合之一