C++布隆过滤器

布隆过滤器

介绍

在许多场景下,如设置昵称时,往往要求唯一性。这时就需要高效判断该昵称是否被使用过。

  • 使用红黑树的kv模型或者哈希表来组织昵称集合,可以,但缺点是可能使用浪费大量内存空间

  • 使用位图,将字符串先转换为整型数字,然后映射到具体比特位上,通过0/1状态判断。可以,但缺点是发生哈希冲突,导致不同昵称最终映射到相同的比特位,发生误判。


布隆过滤器

    可以用来告诉你 “某样东西一定不存在或者可能存在”。它是用多个哈希函数,将一个数据映射到位图结构中。提升查询效率,节省大量的内存空间

  • 将一个数据通过多个哈希函数映射到多个比特位。
  • 查询某个数据是否存在时,通过这些哈希函数映射到对应比特位。如果都为1,则存在;如果有一位为0,则不存在。
  • 使用多个不同的哈希函数,可以降低误判率
    (如:某个未被使用的昵称,经过多个哈希函数映射后,还是有可能和昵称集合中某些映射到相同比特位,发现全为1,误判:该昵称已经被使用)

在这里插入图片描述

随着1的比特位逐渐增多,误判率也会增加,存在(可以忍受的)错误率,通过调整哈希函数和位图大小来平衡。


如何选择合适的k和m值呢?
k为哈希函数个数,m为布隆过滤器长度,n为插入的元素个数,p为误判率

这里有一个公式:

m = − n l n p ( l n 2 ) 2 k = m n l n 2 m=-\frac{nlnp}{(ln2)^2}\\ k=\frac{m}{n}ln2 m=(ln2)2nlnpk=nmln2

当哈希函数个数 k = 3 k=3 k=3 l n 2 ≈ 0.7 ln2\approx0.7 ln20.7,则 m = 4.3 n m=4.3n m=4.3n,即位图长度应该是插入元素个数的4~5倍

实现

哈希函数

下面三个都是比较经典的字符串转整型的哈希函数

struct HashBKDR
{
	// BKDR
	size_t operator()(const string& key)
	{
		size_t val = 0;
		for (auto ch : key)
		{
			val *= 131;
			val += ch;
		}

		return val;
	}
};

struct HashAP
{
	// BKDR
	size_t operator()(const string& key)
	{
		size_t hash = 0;
		for (size_t i = 0; i < key.size(); i++)
		{
			if ((i & 1) == 0)
			{
				hash ^= ((hash << 7) ^ key[i] ^ (hash >> 3));
			}
			else
			{
				hash ^= (~((hash << 11) ^ key[i] ^ (hash >> 5)));
			}
		}
		return hash;
	}
};
struct HashDJB
{
	// BKDR
	size_t operator()(const string& key)
	{
		size_t hash = 5381;
		for (auto ch : key)
		{
			hash += (hash << 5) + ch;
		}

		return hash;
	}
};

布隆过滤器

// N表示准备要映射N个值
template<size_t N,
	class K = string, class Hash1 = HashBKDR, class Hash2 = HashAP, class Hash3 = HashDJB>
	class BloomFilter
{
public:
	void Set(const K& key)
	{
		size_t hash1 = Hash1()(key) % (_ratio * N);
		_bits->set(hash1);

		size_t hash2 = Hash2()(key) % (_ratio * N);
		_bits->set(hash2);

		size_t hash3 = Hash3()(key) % (_ratio * N);
		_bits->set(hash3);
	}

	bool Test(const K& key)
	{
		size_t hash1 = Hash1()(key) % (_ratio * N);
		if (!_bits->test(hash1))
			return false; // 准确的

		size_t hash2 = Hash2()(key) % (_ratio * N);
		if (!_bits->test(hash2))
			return false; // 准确的

		size_t hash3 = Hash3()(key) % (_ratio * N);
		if (!_bits->test(hash3))
			return false;  // 准确的

		return true; // 可能存在误判
	}

	// 不支持删除
	//void Reset(const K& key);

private:
    //开辟_ration * 5个比特位空间,减少误判率
	const static size_t _ratio = 5;
	std::bitset<_ratio * N>* _bits = new std::bitset<_ratio * N>;
};

删除

布隆过滤器不能直接支持删除工作,因为在删除一个元素时,可能会影响其他元素。(如:删除某个昵称后,将其映射的几个比特位为0,可能会导致其他已使用的昵称被判定为未使用)
在这里插入图片描述

小结

优点:不存储元素本身,节约空间,保密,增加和查询效率高

缺点:存在误判

对于已保存在布隆过滤器中的元素,test()不会出现误判;对于未保存的,可能会误判,认为已存在。

使用——题目

除了可以应用在设置昵称。也可以在设置黑名单,阻止非法访问。

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

近似算法:允许一些误判。将一个文件的所有query(字符串)映射进一个布隆过滤器中,然后使用另一个文件对布隆过滤器进行test(),如果在,就是交集部分。

精确算法:不允许误判。哈希切分,假设每个query占30byte,100亿query需要300亿byte,约300G。内存不能加载那么大的空间,则需要先切分成小块。
在这里插入图片描述

2.如何扩展BloomFilter使得它支持删除元素的操作

用多个位表示一个位置,做计数。但是为了支持删除,空间消耗更多,优势削弱了


    🦀🦀观看~~

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值