C++:位图,布隆过滤器

本文探讨了如何使用位图数据结构高效判断40亿整数中的成员,并介绍了布隆过滤器的概念、误判机制及在场景中的应用,包括昵称验证、黑名单过滤和数据查找加速。同时涉及了布隆过滤器的删除问题和哈希切割在大文件IP统计中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一.位图

位图应用题1:

set 增加值:

位图题1代码:

位图应用题2:给定100亿个整数,设计算法找到只出现一次的整数?

二.布隆过滤器

1.概念

2.布隆过滤器删除

3.应用场景

4.哈希切割


一.位图

位图应用题1:

给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在
这40亿个数中。【腾讯】
40亿个整数,占用多少空间:16G:40 0000 0000 * 4=160 0000 0000 byte字节=16G
1G 等于 2^30 约等于 10亿字节

1、set/unordered set? 内存不够
2、外排序+二分查找? 不能很好支持快速随机访问 ,这两种都不行

 
3. 位图解决

位图:直接定址法哈希---用一个比特位标识映射值在不在

方法:

数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一
个二进制比特位来代表数据是否存在的信息,如果二进制比特位为 1 ,代表存在,为 0
代表不存在。比如:

原本4个字节存一个数,现在1个bit存一个数的状态,1字节=8bit,4字节=32bit,16G/32=500MB,现在消耗500MB即可

set 增加值:

        //x映射的位标记成1
		void set(size_t x)    //增加x到这些数中,把x对应的比特位设置为1
		{
			// x映射的比特位在第几个char对象
			size_t i = x / 8;

			// x在char第几个比特位
			size_t j = x % 8;

			_bits[i] |= (1 << j);
		}

位图题1代码:

#pragma once
#include <vector>

namespace bit
{
	// N个比特位的位图  10  16
	template<size_t N>    //总的数的数量,这里设成100先
	class bitset
	{
	public:
		bitset()
		{
			// +1保证足够比特位,最多浪费8个
			_bits.resize(N/8 + 1, 0);
		}

		//x映射的位标记成1
		void set(size_t x)    //增加x到这些数中,把x对应的比特位设置为1
		{
			// x映射的比特位在第几个char对象
			size_t i = x / 8;

			// x在char第几个比特位
			size_t j = x % 8;

			_bits[i] |= (1 << j);
		}

		void reset(size_t x)    //删除x,把x对应的比特位设置为0
		{
			// x映射的比特位在第几个char对象
			size_t i = x / 8;

			// x在char第几个比特位
			size_t j = x % 8;

			//! && ||
			//~ &  | 
			_bits[i] &= (~(1 << j));
		}

		bool test(size_t x)
		{
			// x映射的比特位在第几个char对象
			size_t i = x / 8;

			// x在char第几个比特位
			size_t j = x % 8;

			return _bits[i] & (1 << j);
		}

	private:
		std::vector<char> _bits;
		//vector<int> _bits;
	};

	void test_bit_set1()
	{
		bitset<100> bs;
		bs.set(18);
		bs.set(19);

		cout << bs.test(18) << endl;
		cout << bs.test(19) << endl;
		cout << bs.test(20) << endl;


		bs.reset(18);
		bs.reset(18);

		cout << bs.test(18) << endl;
		cout << bs.test(19) << endl;
		cout << bs.test(20) << endl;

		//bitset<INT_MAX> bigBS;
		bitset<0xFFFFFFFF> bigBS;
		//bitset<-1> bigBS;



		//bit_set<2^32-1> bigBS; // pow
	}
}

位图应用题2:给定100亿个整数,设计算法找到只出现一次的整数?

方法:用两个 bitset 数组  _bs1,_bs2记录,x对应两个数组的比特位来记录这个数出现的次数,_bs1对应比特位是0,_bs2对应比特位是0,就是出现0次;_bs1对应比特位是0,_bs2对应比特位是1,就是出现1次;_bs1对应比特位是1,_bs2对应比特位是0,就是出现2次;

#pragma once
#include <vector>

namespace bit
{
	template<size_t N>
	class two_bitset
	{
	public:
		void set(size_t x)
		{
			int in1 = _bs1.test(x);
			int in2 = _bs2.test(x);
			if (in1 == 0 && in2 == 0)
			{
				_bs2.set(x);
			}
			else if (in1 == 0 && in2 == 1)
			{
				_bs1.set(x);
				_bs2.reset(x);
			}
		}

		bool is_once(size_t x)
		{
			return _bs1.test(x) == 0 && _bs2.test(x) == 1;
		}

	private:
		bitset<N> _bs1;
		bitset<N> _bs2;
	};

	void test_two_bitset()
	{
		int a[] = { 4, 3, 2, 4, 5, 2, 2, 4, 7, 8, 9, 2, 1,3,7 };
		two_bitset<10> tbs;
		for (auto e : a)
		{
			tbs.set(e);
		}

		for (size_t i = 0; i < 10; ++i)
		{
			if (tbs.is_once(i))
			{
				cout << i << endl;
			}
		}
	}
}

二.布隆过滤器

1.概念

布隆过滤器: 将哈希与位图结合,即布隆过滤器。它是用多个哈希函数,将一个数据映射到位图结构中

无法解决哈希冲突
存在冲突既是误判——在:存在误判。不在:是准确的,这个数据只要有一个映射值不在就一定不在。
给出几个网址ip形式的字符串,set添加数据:通过不同的函数将字符串转换成整数记录在哈希表上,这里假设只用了3个不同函数进行映射,比如 123.0.3.22 这个字符串映射3个数:100 33 22,set(123.0.3.22 ) 以后就加入这3个数。

test查看:假如下面前3个字符串都set了,若test查看第2个字符串 192.0.3.21 是否存在,则看对应的映射值 23 33 99 是否都存在,都在就说明已经set过了,存在,但是还是有误判的可能,只不过函数越多,误判的概率越小。比如:test查看第4个字符串:

若test查看第4个字符串 31.22.1.1 是否存在,则看对应的映射值 55 88 70是否都存在,比如查55存在,和第3个重了;查88存在,和第3个重了;查70不存在,说明没有这个字符串,所以  31.22.1.1不存在。

2.布隆过滤器删除

布隆过滤器不能直接支持删除工作,因为在删除一个元素时,可能会影响其他元素。
一种支持删除的方法:将布隆过滤器中的每个比特位扩展成一个小的计数器,插入元素时给 k 个计
数器 (k 个哈希函数计算出的哈希地址 ) 加一,删除元素时,给 k 个计数器减一,通过多占用几倍存储
空间的代价来增加删除操作。
缺陷:
1. 无法确认元素是否真正在布隆过滤器中
2. 存在计数回绕

删除一个值,只需要——映射位置的计数
删除这个值以后,可能存在判断他还在! -- 误判
布隆过滤器的特点节省空间,支持删除就不那么节省了

3.应用场景

(1)注册的时候,快速判断一 个昵称是否使用过?
a、不在:没有用过 绿色对钩
b、在:再去数据库查确认一遍

(2)黑名单
a、不在        就通行
b、在 就再次去系统确认

(3)过滤层,提高查找数据效率

a、不在        就返回
b、在 就再次去数据系统找一遍

这样不在的情景就效率极高

4.哈希切割

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

(1)统计次数,如何统计次数?

平分100个文件,统计次数是不对的。因为相同ip可能分到不同的文件中去了

正确做法:

哈希切割(分):利用哈希函数得出字符串对应的映射值 i,把这个字符串放到对应的 Ai文件中,则相同的ip,一定进入同一个小文件

<think>好的,用户想了解C++位图布隆过滤器的相关知识及总结,我需要结合提供的引用资料来组织回答。首先,我应该先明确位图布隆过滤器的基本概念,然后讨论它们的实现和应用,最后总结它们的优缺点和应用场景。 首先看位图部分。根据引用[3],位图用于处理海量数据的存在性判断,比如40亿个整数去重。位图通过每个bit位表示一个数据是否存在,节省空间。需要提到位图的数据结构设计,比如用vector<bool>或直接操作比特位。实现部分可能需要给出C++的代码示例,比如设置某位为1,清除为0,检查存在性。应用方面包括快速查找、去重、排序等,引用[2]和[3]都有提到这些应用。 然后是布隆过滤器。根据引用[4],布隆过滤器是概率型数据结构,用于判断元素可能存在或一定不存在。它使用多个哈希函数映射到不同的位,引用[3]和[4]都提到了这一点。实现部分需要说明插入和查找的过程,以及为什么不能删除,或者如何支持删除(可能需要引用计数)。应用方面,引用[5]提到了缓存穿透的解决方案,防止数据库被无效查询压垮。此外,分布式系统和爬虫中的URL去重也是常见应用。 需要注意布隆过滤器的优缺点,比如空间效率高但存在误判率,无法删除元素等。引用[4]和[5]都有相关描述。可能需要比较位图布隆过滤器的适用场景,比如位图适合精确判断且数据范围小的情况,而布隆过滤器适合大数据量且允许一定误判的情况。 代码实现部分,位图可以用bitset或手动操作位,布隆过滤器则需要多个哈希函数和位图的组合。需要确保示例代码正确,比如展示如何设置位,检查存在性,以及布隆过滤器的插入和查询步骤。 最后总结部分,需要将位图布隆过滤器的核心点对比,强调它们在海量数据处理中的重要性,并结合引用中的内容,如引用[1]提到的结合位图布隆过滤器的方案,可能可以提到这种混合结构在优化空间和查询速度上的优势。 现在需要检查是否有遗漏的引用内容,比如引用[1]提到结合位图布隆过滤器的优点,这可能是在实际应用中的优化策略,需要在总结部分提到。同时,用户要求生成相关问题,需要根据内容设计相关问题,比如应用场景、实现细节、优缺点比较等。 需要注意回答的结构,先讲位图,再讲布隆过滤器,每个部分包括概念、实现、应用,最后总结对比。确保引用标识正确添加,例如在提到布隆过滤器概念时加上[^4],在缓存穿透时引用[^5]等。</think>### C++位图布隆过滤器总结 #### 一、位图(Bitmap) **1. 概念与实现** 位图通过每个比特位(bit)表示一个数据的存在性(0/1),适用于海量数据的快速存在性判断[^3]。例如,40亿个整数(约16GB)用位图仅需约500MB内存。 **C++实现示例**: ```cpp class Bitmap { private: std::vector<uint32_t> bits; // 使用uint32_t数组存储位 public: Bitmap(size_t range) : bits((range >> 5) + 1, 0) {} // 计算需要多少32位块 void set(size_t x) { size_t index = x >> 5; // 确定数组下标 size_t offset = x % 32; // 确定比特位偏移 bits[index] |= (1 << offset); } bool test(size_t x) { size_t index = x >> 5; size_t offset = x % 32; return (bits[index] & (1 << offset)) != 0; } }; ``` **2. 应用场景** - **数据去重**:如统计40亿整数中不重复的数字。 - **快速查询**:判断IP是否在黑名单中。 - **排序**:对有限范围的整数进行非比较排序[^2]。 --- #### 二、布隆过滤器(Bloom Filter) **1. 概念与实现** 布隆过滤器通过$k$个哈希函数将元素映射到多个位,若所有对应位为1则“可能存在”,否则“一定不存在”[^4]。牺牲一定准确性换取极高的空间效率。 **C++实现核心逻辑**: ```cpp class BloomFilter { private: Bitmap bitmap; size_t k; // 哈希函数数量 public: BloomFilter(size_t size, size_t k) : bitmap(size), k(k) {} void add(const std::string& key) { for (size_t i = 0; i < k; ++i) { size_t hash = std::hash<std::string>{}(key + std::to_string(i)); bitmap.set(hash % bitmap.size()); } } bool contains(const std::string& key) { for (size_t i = 0; i < k; ++i) { size_t hash = std::hash<std::string>{}(key + std::to_string(i)); if (!bitmap.test(hash % bitmap.size())) return false; } return true; // 可能存在(有一定误判率) } }; ``` **2. 应用场景** - **缓存穿透防护**:拦截不存在的数据请求,保护数据库。 - **分布式系统**:减少节点间冗余数据传输。 - **爬虫URL去重**:避免重复抓取已处理的页面[^3]。 **3. 优缺点** - **优点**:空间效率高,查询时间$O(k)$,适合海量数据[^4]。 - **缺点**:误判率随元素增加而上升,且不支持删除(除非引入计数布隆过滤器)。 --- #### 三、对比与总结 | **特性** | **位图** | **布隆过滤器** | |------------------|------------------------------|------------------------------| | **数据范围** | 适用于整数且范围较小 | 支持任意数据类型 | | **误判率** | 无 | 有(可调整哈希函数数量优化) | | **删除支持** | 直接修改位即可 | 需额外结构(如计数位图) | | **典型场景** | 精确存在性判断 | 允许误判的存在性预筛 | **混合优化方案**:结合位图布隆过滤器,例如用位图处理高频数据,布隆过滤器处理低频数据,提升整体性能[^1]。 ---
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值