海量数据判重——布隆过滤器(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;

};



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