【C++】位图/布隆过滤器+海量数据处理

目录

一、位图

1.1 位图的概念

1.2 位图的实现

1.3 位图的应用(面试题)

二、布隆过滤器

2.1 布隆过滤器的引入

2.2 布隆过滤器概念

2.3 布隆过滤器的插入和查找

2.4 布隆过滤器的实现

2.5 布隆过滤器的优点和缺陷

2.6 布隆过滤器的应用(面试题)


一、位图

1.1 位图的概念

在C++中,位图(Bitmap)是一种数组,其中数组的每个元素可以表示多个布尔值。用于高效地存储和查询海量数据,其中元素的每个布尔值只占用一个位(bit)的空间。通常是用来判断某个数据存不存在的。

例如在32位系统中,一个unsigned int类型的变量可以表示32个布尔值。通过位操作,我们可以检查、设置或清除特定的位,从而实现对大量布尔值的快速访问和修改。

面试题
给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在
这40亿个数中。【腾讯】

方法:

1. 遍历,时间复杂度O(N)
2. 排序(O(NlogN)),利用二分查找: logN
3. 位图解决

方法1和2中都需要将数据保存在数组中,40亿个整数需要16G内存,内存开辟不了这么大的连续空间。
数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一
个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0
代表不存在。
比如:

40亿个不重复的无符号整数的范围在0 ~ 2^32,有42亿多种可能性。一个字符型8个比特位,每个比特位来存储一个数字是否存在的状态,需要(2^32) / 8 = 2^29字节,即0.5G的内存。

1.2 位图的实现

C++标准库中的位图在线文档说明     #include <bitset>

在C++中,位图的实现通常使用unsigned int数组或者std::vector<unsigned int>。下面是一个简单的位图实现:

// N是需要多少比特位
template<size_t N>
class myBitSet
{
public:
	myBitSet()
	{
		_bs.resize((N >> 5) + 1, 0);//一个size_t 4个字节,32个比特位,右移五位相当于除以32,+1是防止有余数
		//注意:右移运算符>>优先级高于+,需要在右移时加上括号
	}

	void set(size_t x)//对第x个比特位置为1
	{
		size_t i = x / 32;//确定在哪一个size_t
		size_t j = x % 32;//确定在哪一个比特位上
		_bs[i] |= (1 << j);
	}

	void reset(size_t x)//第x个比特位置清0
	{
		size_t i = x / 32;//确定在哪一个size_t
		size_t j = x % 32;//确定在哪一个比特位上
		_bs[i] &= ~(1 << j);
	}

	bool test(size_t x)//返回第x位的状态
	{
		size_t i = x / 32;//确定在哪一个size_t
		size_t j = x % 32;//确定在哪一个比特位上
		return _bs[i] & (1 << j);
	}
	
private:
	vector<size_t> _bs;
};

1.3 位图的应用(面试题)

1. 快速查找某个数据是否在一个集合中
2. 排序 + 去重
3. 求两个集合的交集、并集等
4. 操作系统中磁盘块标记

1. 给定100亿个整数,设计算法找到只出现一次的整数?

100一个整数,它的范围也是只在0 ~ 2^32 之间,可以设计两个位图,每一次映射都调整位图里面的数字。例如出现0次设为00,出现1次设为01,二次及以上都为10。

template<size_t N>
class twobitset
{
public:
	void set(size_t x)
	{
		//00->01
		//01->10
		//10->不变
		if (_bs1.test(x) == false && _bs2.test(x) == false)//出现0次
		{
			_bs2.set(x);//新增设为01
		}
		else if (_bs1.test(x) == false && _bs2.test(x) == true)//出现1次
		{
			_bs1.set(x);
			_bs2.reset(x);//新增设为10
		}
		//出现2次及以上
	}

	void PrintOnce()
	{
		for (size_t i = 0; i < N; i++)
		{
			if (_bs1.test(i) == false && _bs2.test(i) == true)
			{
				cout << i << endl;
			}
		}
		cout << endl;
	}

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

2. 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?

各自映射到一个位图,若一个值在两个位图都存在,则是交集

3. 位图应用变形:1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数。

方法同问题1,创建两个位图,出现0次00,出现1次01,出现2次10,出现3次及以上 11。

二、布隆过滤器

2.1 布隆过滤器的引入

之前的搜索方法中,传统的数据结构和方法存在一定的局限性:

  • 暴力查找:数据量大了,效率就低。O(n)
  • 排序+二分查找:排序有代价、数组不方便增删。虽然查找的时间复杂度为 O(log n),但排序的时间复杂度为 O(n log n)
  • 搜索树 ->AVL树+红黑树:随着数据量的增加,树的高度也会增加,导致性能下降。
  • 哈希:当发生哈希碰撞时,性能会下降。此外,哈希表的空间消耗相对较高。
  • 以上数据结构,空间消耗很高。如何应对数量很大的数据?
  • 位图及变形:位图只能处理整数,对于其他类型的数据则不适用。

如果要应对其他类型数据的问题,如何快速查找?

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

2.2 布隆过滤器概念

布隆过滤器(Bloom Filter)是一种由布隆在1970年提出的概率型数据结构,它通过使用多个哈希函数将元素映射到一个 bit 数组中来实现高效的数据查询和插入。布隆过滤器的核心优势在于它的空间效率:它使用相对较少的内存来表示一个可能非常大的集合。可以用来告诉你 “某样东西 一定不存在 或者 可能存在”。

布隆过滤器的特点包括:

  • 高效插入和查询:插入和查询操作都非常快速,因为它们只涉及到哈希计算和位操作。
  • 概率性:布隆过滤器可能会返回误报(即一个元素被错误地判断为存在于集合中),但不会返回漏报(即如果一个元素被判断为不存在,那么它一定不存在)。
  • 固定大小:布隆过滤器的位数组大小在创建时确定,不会随着元素的添加而改变。
  • 不可删除:一旦元素被插入布隆过滤器,就无法删除,因为删除可能会导致其他元素的位被错误地设置为0。(传统布隆过滤器不可删除)

举个例子:原本在位图或哈希表/桶中,可能出现多个关键字映射到同一个位置,现在使用多个哈希函数,让原本映射到一个位置的结果映射到多个位置。检查关键字在不在,要把关键字映射的所有位置都检查一遍,只有所有位置的状态都为1,这个关键字才 “可能存在”,如果映射的位置有一个检测到了0,那么这个关键字肯定不存在。

详解布隆过滤器的原理,使用场景和注意事项

上面链接的讲解非常精彩,有兴趣的话可以看一下。

2.3 布隆过滤器的插入和查找

布隆过滤器是一个 bit 向量或者说 bit 数组

如果我们要映射一个值到布隆过滤器中,我们需要使用多个不同的哈希函数生成多个哈希值,并对每个生成的哈希值指向的 bit 位置为 1,例如针对值 “baidu” 和三个不同的哈希函数分别生成了哈希值 1、4、7,则上图转变为:

在再存一个值 “tencent”,如果哈希函数返回 3、4、8 的话,图继续变为:

值得注意的是,4 这个 bit 位由于两个值的哈希函数都返回了这个 bit 位,因此它被覆盖了。现在我们如果想查询 “dianping” 这个值是否存在,哈希函数返回了 1、5、8三个值,结果我们发现 5 这个 bit 位上的值为 0,说明没有任何一个值映射到这个 bit 位上,因此我们可以很确定地说 “dianping” 这个值不存在。而当我们需要查询 “baidu” 这个值是否存在的话,那么哈希函数必然会返回 1、4、7,然后我们检查发现这三个 bit 位上的值均为 1,那么我们可以说 “baidu” 存在了么?答案是不可以,只能是 “baidu” 这个值可能存在。

这是为什么呢?答案跟简单,因为随着增加的值越来越多,被置为 1 的 bit 位也会越来越多,这样某个值 “taobao” 即使没有被存储过,但是万一哈希函数返回的三个 bit 位都被其他值置位了 1 ,那么程序还是会判断 “taobao” 这个值存在。

2.4 布隆过滤器的实现

各种字符串Hash函数

选取3种平均分较高的哈希算法:BKDRHash,APHash,DJBHash。

#include "myBitSet.h"

struct BKDRHash
{
	size_t operator()(const string& key)
	{
		// BKDR
		size_t hash = 0;
		for (auto e : key)
		{
			hash *= 31;
			hash += e;
		}

		return hash;
	}
};

struct APHash
{
	size_t operator()(const string& key)
	{
		size_t hash = 0;
		for (size_t i = 0; i < key.size(); i++)
		{
			char ch = key[i];
			if ((i & 1) == 0)
			{
				hash ^= ((hash << 7) ^ ch ^ (hash >> 3));
			}
			else
			{
				hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));
			}
		}
		return hash;
	}
};

struct DJBHash
{
	size_t operator()(const string& key)
	{
		size_t hash = 5381;
		for (auto ch : key)
		{
			hash += (hash << 5) + ch;
		}
		return hash;
	}
};

template <size_t N, class K = string, 
	class HashFunc1 = BKDRHash, 
	class HashFunc2 = APHash, 
	class HashFunc3 = DJBHash>
class myBloomFilter
{
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);
	}

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

	bool test(const K& key)
	{
		//判断不存在是准确的
		size_t hash1 = HashFunc1()(key) % N;
		if (_bs.test(hash1) == false)
			return false;

		size_t hash2 = HashFunc2()(key) % N;
		if (_bs.test(hash2) == false)
			return false;

		size_t hash3 = HashFunc3()(key) % N;
		if (_bs.test(hash3) == false)
			return false;

		return true;//有可能误判
	}

private:
	myBitSet<N> _bs;
};

2.5 布隆过滤器的优点和缺陷

布隆过滤器优点
1. 增加和查询元素的时间复杂度为:O(K), (K为哈希函数的个数,一般比较小),与数据量大小无关
2. 哈希函数相互之间没有关系,方便硬件并行运算
3. 布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势
4. 在能够承受一定的误判时,布隆过滤器比其他数据结构有这很大的空间优势
5. 数据量很大时,布隆过滤器可以表示全集,其他数据结构不能
6. 使用同一组散列函数的布隆过滤器可以进行交、并、差运算

布隆过滤器缺陷
1. 有误判率,即存在假阳性(False Position),即不能准确判断元素是否在集合中(补救方法:再建立一个白名单,存储可能会误判的数据)
2. 不能获取元素本身
3. 一般情况下不能从布隆过滤器中删除元素
4. 如果采用计数方式删除,可能会存在计数回绕问题

2.6 布隆过滤器的应用(面试题)

布隆过滤器使用场景:可以接受误判的场景

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

query 是字符串,假设平均1个query是50byte,1G 约等于10亿byte,100亿个query就是5000亿byte ,约等于 500G,内存根本存不下。

500G的文件存放不下,可以将它们拆分成多个小文件,例如500个小文件a0~a499,每个文件大小就是1G。读取第一个文件的每个query,使用hash函数将query映射到500个文件中(计算 i = Hash(query) % 500),i 是几,query就进入第几个小文件。

两个文件中相同的query分别进入了编号相同的小文件 ai 和 bi。

两个大文件对应的编号相同的小文件ai 和 bi求交集,可以分别插入到一个setA和一个setB中,快速找到交集。

但是这样也可能有其他问题,例如某个文件可能太大,原因:

  1. 小文件中大多数都是某一个query,重复过多。
  2. 小文件有很多不同的query。

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

近似算法(布隆过滤器)
1. 创建布隆过滤器:由于1G内存大约可以表示10亿字节,即约80亿bit,我们可以创建一个布隆过滤器,使用足够多的哈希函数(例如4到6个),以保持较低的误报率。

2. 插入第一个文件的query:遍历第一个文件中的每个query,对每个query应用哈希函数,并将对应位设置为1。

3. 查询第二个文件的query:遍历第二个文件中的每个query,同样应用哈希函数。如果布隆过滤器中的所有对应位都是1,则认为该query可能出现在第一个文件中。

4. 输出可能交集:将所有可能出现在交集中的query输出到新文件中。

5. 误报处理:由于布隆过滤器的性质,输出文件中可能包含一些误报,即实际上并不在第一个文件中的query。可以通过调整布隆过滤器的参数来减少误报率。

精确算法(哈希分割)
1文件分割:对两个文件的query进行哈希处理,然后使用除留余数法(%5000)将query分布到5000个不同的子文件中。每个子文件的大小大约为100M,这样每个文件的总大小就是5000 * 100M = 500G,符合原始数据的大小。

2.读取和比较:对于第二个文件中的每个query,同样进行哈希处理,然后将query放入对应的子文件中。接着,对于每个子文件,将其内容读取到内存中的unordered_map中,然后查找第一个文件中的对应子文件,看是否有相同的query。
3.输出交集:如果在一个子文件中找到了匹配的query,那么这个query就属于两个文件的交集。将这些query输出到新文件中。
4.内存使用:由于每个子文件是独立处理的,所以每次只需要将一个子文件的内容读取到内存中,这样内存的使用就被限制在了100M左右,符合1G内存的限制。
精确算法的关键在于将大文件分割成多个小文件,然后逐个处理,这样可以避免一次性将所有数据加载到内存中。这种方法虽然准确,但是处理时间可能会比近似算法长,因为需要多次读写磁盘。

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

与上一题同理,i= Hash(ip) % 100,这个ip就进入Ai小文件。相同的ip一定进入了同一个小文件,我们对小文件用map统计出ip次数就是准确的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/634620.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C++】详解多态

目录 初识多态 多态的条件 接口继承和实现继承 override 和 final 多态原理 继承与虚函数表 析构函数与多态 抽象类 本篇内容关联知识的链接 【C】详解C的继承-CSDN博客 【C】详解C的模板-CSDN博客 【C】C的内存管理-CSDN博客 初识多态 父类被不同子类继承后&#…

STM32控制HC-SR04超声模块获取距离

欢迎入群共同学习交流 时间记录&#xff1a;2024/5/23 一、模块介绍 &#xff08;1&#xff09;引脚介绍 VCC&#xff1a;电源引脚&#xff0c;接单片机3.3/5V GND&#xff1a;电源地 Trig&#xff1a;超声信号触发引脚 Echo&#xff1a;超声信号接收引脚 &#xff08;2&…

多商户消费券系统源码(ThinkPHP+FastAdmin+微信公众号)

打造智能促销新体验 一、引言&#xff1a;消费券系统的时代意义 在当今这个数字化高速发展的时代&#xff0c;电子商务和移动支付已经成为人们日常生活的重要组成部分。随着市场竞争的加剧&#xff0c;多商户消费券系统作为一种创新的促销手段&#xff0c;正逐渐受到商家和消…

安全工程师考试摸拟试题

安全工程师考试摸拟试题安全工程师是指在工程项目中负责安全管理和安全技术服务的专业人员。他们需要具备扎实的理论知识和丰富的实践经验&#xff0c;能够有效预防和控制各类安全风险… 1 安全工程师考试摸拟试题 安全工程师是指在工程项目中负责安全管理和安全技术服务的专业…

基于windows通过kind部署轻量级便携式k8s集群

感谢老师的视频教程&#xff1a; 基于windows通过kind部署轻量级便携式k8s集群 wsl windows下的linux wsl --set-default-version 2 wsl --help wsl --list --online wsl --install -d Ubuntu wsl -l -v &#xff08;看看版本是不是2&#xff0c;否则docker那边识别不到&…

vite+ts+mock+vue-router+pinia实现vue的路由权限

0.权限管理 前端的权限管理主要分为如下&#xff1a; 接口权限路由权限菜单权限按钮权限 权限是对特定资源的访问许可&#xff0c;所谓权限控制&#xff0c;也就是确保用户只能访问到被分配的资源 1.项目搭建 创建vite项目 yarn create vite配置别名 npm install path -…

查看cpu

cpu是几核的怎么查看_windows查看cpu核数-CSDN博客文章浏览阅读1.4w次&#xff0c;点赞11次&#xff0c;收藏24次。cpu是几核的怎么查看_windows查看cpu核数https://blog.csdn.net/llg___/article/details/125317223?ops_request_misc&request_id&biz_id102&utm_t…

多模态大模型新进展——GPT-4o、Project Astra关键技术丨青源Workshop第27期

青源Workshop丨No.27 多模态大模型新进展—GPT-4o、Project Astra关键技术主题闭门研讨会 刚刚过去的两天&#xff0c;OpenAI、Google纷纷发布了多模态大模型的最新成果&#xff0c;GPT-4o、Project Astra先后亮相。 本周五&#xff08;北京时间5月17日&#xff09;18点&#x…

力扣1809 没有广告的剧集(postgresql)

需求 Table: Playback ----------------- | Column Name | Type | ----------------- | session_id | int | | customer_id | int | | start_time | int | | end_time | int | ----------------- 该表主键为&#xff1a;session_id &#xff08;剧集id&#xff09; customer_…

v-md-editor和SSE实现ChatGPT的打字机式输出

概述 不论是GPT还是文心一言&#xff0c;在回答的时候类似于打字机式的将答案呈现给我们&#xff0c;这样的交互一方面比较友好&#xff0c;另一方面&#xff0c;当答案比较多、生成比较慢的时候也能争取一些答案的生成时间。本文后端使用express和stream&#xff0c;使用SSE将…

WXML模板语法-数据绑定

1.数据绑定的基本原则 (1)在data中定义数据 (2)在WXML中使用数据 2.在data页面中定义数据&#xff1a;在页面对应的.js文件中&#xff0c;把数据定义在data对象中即可 &#xff08;这里打错了 应该是数组类型的数据... 报意思啊&#xff09; 3.Mustache语法的格式 把data中的…

容器组件:栅格布局,侧边栏容器(HarmonyOS学习第四课【4.5】)

栅格布局 栅格布局可以为布局提供规律性的结构&#xff0c;解决多尺寸多设备的动态布局问题&#xff0c;保证不同设备上各个模块的布局一致性。 栅格容器组件&#xff0c;仅可以和栅格子组件(GridCol)在栅格布局场景中使用。 说明 该组件从API Version 9开始支持。后续版本…

WordPress主题 7B2 PRO 5.4.2 免授权开心版源码

本资源提供给大家学习及参考研究借鉴美工之用&#xff0c;请勿用于商业和非法用途&#xff0c;无任何技术支持&#xff01; WordPress主题 7B2 PRO 5.4.2 免授权开心版源码 B2 PRO 5.4.2 最新免授权版不再需要改hosts&#xff0c;和正版一样上传安装就可以激活。 直接在Word…

计算机精选期刊特辑

文章目录 一、征稿简介二、合作期刊三、投稿咨询四、咨询 一、征稿简介 艾思科蓝依托互联网信息与数据库技术、整合渠道与合作资源&#xff0c;提供EI/SCI/SCIE/SSCI期刊论文的内容审查、发表支持等服务。艾思科蓝与多所知名出版社达成战略合作关系&#xff0c;持续开展合作征…

【Unity】Rider无法调试团结引擎

近在学习unity&#xff0c;代码编辑器选择了熟悉的idea系列&#xff0c;C# 对应的编辑器 rider 之前在使用unity的时候&#xff0c;可以直接使用 Rider进行调试&#xff0c;很方便 但是后来又安装了团结引擎&#xff0c;在启动调试的时候断点总是无法激活 在点击调试按钮的时…

Vue文本溢出如何自动换行

css新增 word-break: break-all; word-wrap: break-word;

如何在没有密码或Face ID的情况下解锁iPhone

iPhone 是一款以其一流的安全功能而闻名的设备&#xff0c;包括面容 ID 和密码。但是&#xff0c;你有没有想过&#xff0c;如果没有这些安全措施&#xff0c;你是否可以解锁iPhone&#xff1f;无论您是忘记了密码&#xff0c;Face ID不起作用&#xff0c;还是只是对其他方法感…

浅析declval关键字

浅析 declval 关键字 文章目录 浅析 declval 关键字前言declval 的基本概念declval 的工作原理declval 的实际应用案例总结 前言 ​ 在现代C编程中&#xff0c;std::declval是一个非常有用的工具&#xff0c;它允许我们在不实例化对象的情况下使用其类型。这在模板元编程中尤其…

Git系列:git rm 的高级使用技巧

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

实用css整理

网页一键变灰 body{filter: grayscale(1); } 一般用于特殊时期&#xff0c;网页变灰&#xff0c;只需要给body标签添加这行样式代码。 一键换主题色 body {filter: hue-rotate(45deg);} 给body标签设置这个属性样式&#xff0c;改变角度看看效果吧。 设置字母大小写 p {t…