[大数据处理]——布隆过滤器

布隆过滤器

说到布隆这两个字大家可千万不要认为我们这里讲的是英雄联盟中的布隆大叔,今天我们所讲的布隆过滤器是一种大数据处理利器,他是由布隆算法Bloom Filter音译而来,是以Bitmap集合为基础的去重算法。

为什么需要布隆过滤器

直观的说,bloom算法类似一个位图,用来判断某个元素(key)是否在某个集合中。和一般的位图不同的是,这个算法无需存储key的值,对于每个key,只需要k个比特位,每个存储一个标志,用来判断key是否在集合中。

应用场景:比如网络爬虫抓取时url去重,邮件提供商反垃圾黑名单Email地址去重,之所以需要k个比特位是因为我们大多数情况下处理的是字符串,那么不同的字符串就有可能映射到同一个位置,产生冲突。如下图:

本来这两个垃圾邮件都应该被标记,但是下图第一种方式只映射一位,映射bcda邮件时以为其已经被标记,导致其被认为是非垃圾邮件,而第二种方式是一个字符串映射多个位,也就正确的标记了每一个垃圾邮件。这也正是布隆过滤器的算法思想
在这里插入图片描述

布隆算法

  1. 首先需要k个hash函数,每个函数可以把key散列成为1个整数(将每个字符转换成不同数字
  2. 初始化时,需要一个长度为n比特的数组,每个比特位初始化为0(需要一个位图
  3. 某个key加入集合时,用k个hash函数计算出k个散列值,并把数组中对应的比特位置为1(设置映射的一位为1
  4. 判断某个key是否在集合时,用k个hash函数计算出k个散列值,并查询数组中对应的比特位,如果所有的比特位都是1,认为在集合中。

布隆算法优缺点

优点:不需要存储key,节省空间
缺点:算法判断key在集合中时,有一定的概率key其实不在集合中,已经映射的数据无法删除

我们这里来详细讲解一下布隆过滤器的缺点:首先,即使使用了多位映射的思想我们依旧发现会出现误判的情况,如下图,当acba网址时,它所映射的位为1,所以它误以为自己已经被标记
在这里插入图片描述
再来看看第二个缺点:正如你所看到的,删除数据abcd时将其所对应的位设置为0,却不知bcda也在映射共同的一位,所以导致下一次映射bcda时会误判为不在位图中
在这里插入图片描述

实现简易布隆过滤器

说到这里我们就来实现一个简易的布隆过滤器,但是这里的问题是,我们映射n个数据时需要给bitmap开多大的空间呢?复杂的数学推导就不拿来了,直接给大家贴一个简单的公式k = (m/n)In,k是哈希函数的个数,m是需要开多少位,n是数据的多少,ln如果你的高中知识还在的话他大概是2.7左右,我们经过大体的估算映射n个数据我们需要开4.28n 的空间,索性开5倍可以降低冲突。

以下是简单的布隆过滤器实现,使用到的bitmap在笔者的上一篇博客有讲到

struct HashFunc1
{
	size_t operator()(const string& s)
	{
		size_t hash = 0;
		for (int i = 0; i < s.size(); i++)
		{
			hash = hash * 131 + s[i];
		}
		return hash;
	}
};

struct HashFunc2
{
	size_t operator()(const string& s)
	{
		size_t hash = 0;
		for (int i = 0; i < s.size(); i++)
		{
			hash = hash * 65599 + s[i];
		}
		return hash;
	}
};

struct HashFunc3
{
	size_t operator()(const string& s)
	{
		size_t hash = 0;
		for (int i = 0; i < s.size(); i++)
		{
			hash = hash * 1313 + s[i];
		}
		return hash;
	}
};

template<class K = string, 
class HashFunc1 = __HashFunc1<K>, 
class HashFunc2 = __HashFunc2<K>, 
class HashFunc3 = __HashFunc3<K>>

class BloomFilter
{
public:
	BloomFilter(size_t num)
		:_bm(num * 5)
		, _size(num * 5)
	{}

	void Set(const K& key)
	{
		size_t index1 = HashFunc1()(key) % _size;
		_bm.SetBit(index1);

		size_t index2 = HashFunc2()(key) % _size;
		_bm.SetBit(index2);

		size_t index3 = HashFunc3()(key) % _size;
		_bm.SetBit(index3);
	}

	bool Test(const K& key)
	{
		size_t index1 = HashFunc1()(key) % _size;
		if (_bm.TestBit(index1) == false)
		{
			return false;
		}

		size_t index2 = HashFunc2()(key) % _size;
		if (_bm.TestBit(index2) == false)
		{
			return false;
		}

		size_t index3 = HashFunc3()(key) % _size;
		if (_bm.TestBit(index3) == false)
		{
			return false;
		}
		//所有位置都为真. 但是它是不准确的.
		return true;
	}
private:
	BitMap _bm;
	size_t _size;
};

再谈布隆过滤器使用场景

记住一点原则,布隆过滤器判断是不准确的,判断不在是一定准确的

场景一:使用布隆过滤器标记垃圾邮箱

  • 理想状态如果我们布隆过滤器已经帮我们标记了所有垃圾邮箱,那么我们收件箱中的邮件一定是非垃圾的,但是由于会出现误判,所以我们垃圾箱中却可能出现非垃圾邮件

场景二:使用布隆过滤器帮助我们标记垃圾网站

  • 理想状态如果我们布隆过滤器已经帮我们标记了所有垃圾网站,那么我们访问的网站未弹出威胁信息,那么这个网站一定是安全的,如果弹出威胁信息,因为存在误判,所以这个网站可能是安全的

场景三:使用布隆过滤器标记空查询

  • 理想状态如果我们布隆过滤器已经帮我们标记了所有查询,那么如果我们进行查询时如果这条数据不在数据库中就直接返回,如果误判为在的话,我们再进行数据库中的查找,若不在则返回。

总结

通过我们对于布隆过滤器的介绍,你需要掌握他的作用优缺点,一定要明白对于误判时,什么是准确的什么是不准确的。并且要知道他最大的缺陷就是不能删除,其实之前我们在智能指针中聊到了引用计数的技术,但是我们要进行计数又要牵扯到每一位的计数放在哪里,本来我们使用布隆过滤器就是为了节省空间,这样又会导致空间出现大量的浪费,所以有时候过滤器出现了误判也是不可以避免的。

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值