布隆过滤器

4. 布隆过滤器

4.1 问题引入

假设现在有大量的字符串数据,如何判断哪些字符串已经存在,那些不存在?

  1. 用哈希表存储用户记录,缺点:浪费空间
  2. 用位图存储用户记录,缺点:位图一般只能处理整形,如果内容编号是字符串,就无法处理了。
  3. 将哈希与位图结合,即布隆过滤器

核心就是将 字符串 --字符串哈希–> 整形 --> 映射到一个位图中

image-20240912140052439

判断该字符串在不在?

  1. 在:是不准确的,可能存在误判。
  2. 不在:是准确的。

由于1个值映射1个位比较容易误判,所以布隆打算1个值映射多个位,这样误判的概率就会变小

4.2 概念

布隆过滤器是由布隆(Burton Howard Bloom)在1970年提出的 一种紧凑型的、比较巧妙的概率型数据结构,特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”,它是用多个哈希函数,将一个数据映射到位图结构中。此种方式不仅可以提升查询效率,也可以节省大量的内存空间

image-20240912141218244

相关文章,我们这里将哈希函数设置为3,当有n个值时,布隆过滤器的大小设置为4.3n比较好。

4.3 布隆过滤器的使用场景

image-20240912154429625

在上图中,如果想要精确的结果,可以在判断"该昵称被别人注册的时候",这时去服务器中进行查询(由于布隆过滤器判断 在 是不准确的),来得到准确的结果。这样就可以不查询(过滤掉) “该昵称被别人注册的时候” 的情况,从而加快速度

4.4 布隆过滤器的实现

struct BKDRHash
{
	size_t operator()(const string& s)
	{
		register size_t hash = 0;
		for (const auto& e : s) {
			hash = hash * 131 + e;
		}
		return hash;
	}
};

struct SDBMHash
{
	size_t operator()(const string& s)
	{
		register size_t hash = 0;
		for (const auto& e : s) {
			hash = 65599 * hash + e;
		}
		return hash;
	}
};

struct RSHash
{
	size_t operator()(const string& s)
	{
		register size_t hash = 0;
		size_t magic = 63689;
		for (const auto& e : s) {
			hash = hash * magic + e;
			magic *= 378551;
		}
		return hash;
	}
};

template<size_t N
	, class K = string
	, class HashFunc1 = BKDRHash
	, class HashFunc2 = SDBMHash
	, class HashFunc3 = RSHash>
class BloomFilter
{
public:
	void set(const K& key)
	{
		size_t hash1 = HashFunc1()(key) % N;
		size_t hash2 = HashFunc2()(key) % N;
		size_t hash3 = HashFunc3()(key) % N;

		_bs.set(hash1);
		_bs.set(hash2);
		_bs.set(hash3);
	}

	bool test(const K& key)
	{
		size_t hash1 = HashFunc1()(key) % N;
		if (!_bs.test(hash1))	return false;
		size_t hash2 = HashFunc2()(key) % N;
		if (!_bs.test(hash2))	return false;
		size_t hash3 = HashFunc3()(key) % N;
		if (!_bs.test(hash3))	return false;

		return true;
	}
	/*
	* 一般不支持删除,删除一个值可能会影响其他值
	* 非要支持删除,也是可以的,用多个位标记一个值,存引用计数
	* 但是这样话,空间消耗的就变大了
	*/ 
	void reset(const K& key);
private:
	bitset<N> _bs;
};

4.5 应用

给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法

image-20240912194717483

近似算法可以使用布隆过滤器,精确算法可以先使用哈希切割,再将Ai和Bi插入到setA和setB中,这样就可以快速且准确的找交集

此时就会有一个问题,如果某一个小文件还是太大(30GB),该如何处理?有两种情况

  1. 这个小文件中大多数都是某1个query
  2. 这个小文件,有很多不同的query

解决方案:不管文件大小,直接读到内存插入到set。如果是情况1,文件很大有很多重复,后面重复插入都失败,可以插入到set中
如果是情况2,不断插入set以后,内存不足,会抛异常,需要换一个哈希函数进行二次切分,再找交集。


给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址?

image-20240912200851958

哈希切割,如果需要统计ip的topK问题,可以使用优先级队列

5.搜索问题

  1. 暴力查找 数据量大了,效率就低

  2. 排序+二分查找 问题a:排序有代价 问题b:数组不方便增删

  3. 搜索树 ->AVL树+红黑树

  4. 哈希


以上数据结构,空间消耗很高,当需要存储数量很大的数据时

  1. 整形的在不在及其扩展问题:位图及变形 节省空间
  2. 其他类型的在不在问题:布隆过滤器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值