【C++/STL】:哈希的应用 -- 位图布隆过滤器

news2024/12/27 12:14:10

目录

  • 🚀🚀前言
  • 一,位图
    • 1. 位图的概念
    • 2. STL库中的位图
    • 3. 位图的设计
    • 4. 位图的模拟实现
    • 5. 位图的优缺点
    • 6. 位图相关考察题⽬
  • 二,布隆过滤器
    • 1. 布隆过滤器的概念
    • 2. 布隆过滤器的实现
    • 3. 布隆过滤器删除问题
    • 4. 布隆过滤器的优缺点

点击跳转至文章: 【C++/STL】:哈希 – 线性探测&哈希桶

🚀🚀前言

在前面的文章里我们已经学习了什么是哈希和以哈希表为底层的unordered系列容器的使用。本篇文章的位图&布隆过滤器也是哈希思想下的产物,经常使用它们来解决有关海量数据的问题。

一,位图

1. 位图的概念

在引出位图的概念之前,先来看一个经典面试题:

在这里插入图片描述

深入分析:

解题思路2是否可行,我们先算算40亿个数据⼤概需要多少内存。
1G = 1024MB =1024 * 1024KB = 1024*1024 * 1024byte约等于10亿多byte,那么40亿个整数约等于16G,说明40亿个数是无法直接放到内存中的,只能放到硬盘文件中。而二分查找只能对内存数组中的有序数据进行查找

解题思路3:数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使⽤⼀个⼆进制⽐特位来代表数据是否存在的信息,如果⼆进制⽐特位为1,代表存在,为0代表不存在

那么我们设计⼀个⽤二进制比特位表⽰数据是否存在的数据结构,这个数据结构就叫位图

2. STL库中的位图

在这里插入图片描述

根据文档可知,位图bitset 是非类型模板,N是指需要多少比特位。使用时需要包含头文件:

#include <bitset>

位图常用的核心接口主要有三个:

(1) x 映射位标记成1(插入 x,有这个数据了)

在这里插入图片描述

(2) x 映射位标记成0(删除 x,没有这个数据了)

在这里插入图片描述
(3) 检查是否存在这个数据
x映射的位是1,返回真
x映射的位是0,返回假

在这里插入图片描述

3. 位图的设计

位图本质是⼀个直接定址法的哈希表,每个整型值映射⼀个bit位,位图提供控制这个bit的相关接口

实现中需要注意的是,C/C++没有对应位的类型,只能看int/char这样整形类型,我们再通过位运算去控制对应的⽐特位。⽐如我们数据存到vector< int >中,相当于每个int值映射对应的32个值,⽐如第⼀个整形映射0-31对应的位,第⼆个整形映射32-63对应的位,后⾯的以此类推,那么来了⼀个整形值x,i = x / 32;j = x % 32;计算出x映射的值在vector的第 i 个整形数据的第 j 位

在这里插入图片描述

4. 位图的模拟实现

#include<vector>

//非类型模板参数。需要多少比特位
template <size_t N>
class bitset
{
public:
	bitset()
	{
		_bs.resize(N / 32 + 1);
	}

	//x映射位标记成1
	void set(size_t x)
	{
		//映射的位置:第i个整型数据的第j位
		size_t i = x / 32;
		size_t j = x % 32;

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

	//x映射位标记成0
	void reset(size_t x)
	{
		size_t i = x / 32;
		size_t j = x % 32;

		_bs[i] &= (~(1 << j));
	}

	//x映射的位是1,返回真
	//x映射的位是0,返回假
	bool test(size_t x)
	{
		size_t i = x / 32;
		size_t j = x % 32;

		return _bs[i] & (1 << j);
	}
private:
	vector<size_t> _bs;
};

5. 位图的优缺点

优点:增删查改快,节省空间
缺点:只适⽤于整形

6. 位图相关考察题⽬

在这里插入图片描述
深入分析

第一题和第三题是同一类型的题,我们先来分析这两题。首先我们知道把数据放入位图中只能标记在或不在两种状态,而题目要求的是找出出现几次的数据,可能是出现0次,1次……,显然单用一个位图的一个比特位是无法满足的。

用两个位图的标记就可以解决。我们规定00表示数据出现0次,01表示数据出现1次,10表示数据出现2次,11表示数据出现3次及以上

代码实现如下:
注意:这里用的bitset是上文我们自己实现的bitset

我们针对第一题和第三题设计一个结构:

template<size_t N>
class twobitset
{
public:
	void set(size_t x)
	{
		bool bit1 = _bs1.test(x);
		bool bit2 = _bs2.test(x);

		if (!bit1 && !bit2) // 00->01
		{
			_bs2.set(x);
		}
		else if (!bit1 && bit2) // 01->10
		{
			_bs1.set(x);
			_bs2.reset(x);
		}
		else if (bit1 && !bit2) // 10->11
		{
			_bs1.set(x);
			_bs2.set(x);
		}
	}

	// 返回0 出现0次数
	// 返回1 出现1次数
	// 返回2 出现2次数
	// 返回3 出现2次及以上
	int get_count(size_t x)
	{
		bool bit1 = _bs1.test(x);
		bool bit2 = _bs2.test(x);

		if (!bit1 && !bit2)
		{
			return 0;
		}
		else if (!bit1 && bit2)
		{
			return 1;
		}
		else if (bit1 && !bit2)
		{
			return 2;
		}
		else
		{
			return 3;
		}
	}

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

//测试代码,可以统计出100以内哪个数据出现了几次
void test_twobitset()
{
	twobitset<100> tbs; //开100个bit位
	int a[] = { 5,7,9,2,5,99,5,5,7,5,3,9,2,55,1,5,6,6,6,6,7,9 };
	for (auto e : a)
	{
		tbs.set(e);
	}

	for (size_t i = 0; i < 100; ++i)
	{
		cout << i << "->" << tbs.get_count(i) << endl;
		//if (tbs.get_count(i) == 1 || tbs.get_count(i) == 2)
			//cout << i << endl;
	}
}

第二题的代码实现:
把数据分别放入两个位图中,如果两个位图的标记均为1,说明都存在,就是二者的交集

void test_bitset1()
{
	int a1[] = { 5,7,9,2,5,99,5,5,7,5,3,9,2,55,1,5,6 };
	int a2[] = { 5,3,5,99,6,99,33,66 };

	bit::bitset<100> bs1;
	bit::bitset<100> bs2;

	for (auto e : a1)
		bs1.set(e);
	
	for (auto e : a2)
		bs2.set(e);

	for (size_t i = 0; i < 100; i++)
	{
		if (bs1.test(i) && bs2.test(i))
			cout << i << endl;
	}
}

在这里插入图片描述

二,布隆过滤器

有⼀些场景下⾯,有⼤量数据需要判断是否存在,⽽这些数据不是整形,那么位图就不能使⽤了,使⽤红⿊树/哈希表等内存空间可能不够。这些场景就需要布隆过滤器来解决。

1. 布隆过滤器的概念

布隆过滤器是⼀种可以⽤来告诉你"某样东西⼀定不存在或者可能存在"的数据结构,它是⽤多个哈希函数,将⼀个数据映射到位图结构中

布隆过滤器的思路就是把key先映射转成哈希整型值,再映射⼀个位,如果只映射⼀个位的话,冲突率会⽐较多,所以可以通过多个哈希函数映射多个位,降低冲突率
在这里插入图片描述

布隆过滤器这⾥跟哈希表不⼀样,它⽆法解决哈希冲突的,因为他压根就不存储这个值,只标记映射的位。它的思路是尽可能降低哈希冲突判断⼀个值key在是不准确的,判断⼀个值key不在是准确的

2. 布隆过滤器的实现

说明:我们这里用的bitset是我们上文自己实现的bitset,不是库里的

#include "BitSet.h"
#include <string>

struct HashFuncBKDR
{
	size_t operator()(const std::string& s)
	{
		size_t hash = 0;
		for (auto ch : s)
		{
			hash *= 31;
			hash += ch;
		}
		return hash;
	}
};

struct HashFuncAP
{ 
	size_t operator()(const std::string& s)
	{
		size_t hash = 0;
		for (size_t i = 0; i < s.size(); i++)
		{
			if ((i & 1) == 0) // 偶数位字符
				hash ^= ((hash << 7) ^ (s[i]) ^ (hash >> 3));
			else              // 奇数位字符
				hash ^= (~((hash << 11) ^ (s[i]) ^ (hash >> 5)));
		}
		return hash;
	}
};

struct HashFuncDJB
{
	size_t operator()(const std::string& s)
	{
		size_t hash = 5381;
		for (auto ch : s)
			hash = hash * 33 ^ ch;
		
		return hash;
	}
};

//要用多个哈希函数多映射几个位,以便降低哈希冲突
template <size_t N,
		  size_t X = 5,
		  class K = string,
		  class Hash1 = HashFuncBKDR,
		  class Hash2 = HashFuncAP,
		  class Hash3 = HashFuncDJB>
class BloomFilter
{
public:
	void Set(const K& key)
	{
		size_t hash1 = Hash1()(key) % M;
		size_t hash2 = Hash2()(key) % M;
		size_t hash3 = Hash3()(key) % M;

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

	//如果其中一个位为0,就确定不在,如果都为1,那就在(存在误判)
	bool Test(const K& key)
	{
		size_t hash1 = Hash1()(key) % M;
		if (!_bs.test(hash1))
			return false;

		size_t hash2 = Hash2()(key) % M;
		if (!_bs.test(hash2))
			return false;

		size_t hash3 = Hash3()(key) % M;
		if (!_bs.test(hash3))
			return false;

		return true;
	}
private:
	static const size_t M = N * X; //布隆过滤器的bit长度
	bit::bitset<M> _bs;
};

//测试代码
void TestBloomFilter1()
{
	BloomFilter<10> bf;
	bf.Set("猪八戒");
	bf.Set("孙悟空");
	bf.Set("唐僧");

	cout << bf.Test("猪八戒") << endl;
	cout << bf.Test("孙悟空") << endl;
	cout << bf.Test("唐僧") << endl;
	cout << bf.Test("沙僧") << endl;
	cout << bf.Test("猪八戒1") << endl;
	cout << bf.Test("猪戒八") << endl;
}

在这里插入图片描述

3. 布隆过滤器删除问题

布隆过滤器默认是不⽀持删除的,因为⽐如"猪⼋戒“和”孙悟空“都映射在布隆过滤器中,他们映射的位有⼀个位是共同映射的(冲突的),如果我们把孙悟空删掉,那么再去查找“猪⼋戒”会查找不到,因为那么“猪⼋戒”间接被删掉了。

4. 布隆过滤器的优缺点

优点:增删查改快,节省空间
缺点:只适⽤于整形

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

江森自控引入电子签,“碳”索智能建筑数字未来

近年来&#xff0c;伴随着科技的不断进步和市场领域的持续延伸&#xff0c;智能建筑已然成为建筑业科技升级的必经之路。 作为智能建筑领域的全球领导者&#xff0c;江森自控始终走在数字化变革前列&#xff0c;并在业内率先引入了电子合同&#xff0c;通过全流程线上进行的签…

【办公类-53-03】2024年第一学期校历制作(“月/日(星期)”版、排班表、跳过节日和周三)

背景需求&#xff1a; 前期代码制作出2024年第一学期校历&#xff0c;按照5天一周的方法&#xff0c;提取实际工作日。制作成“周计划教案”使用的长日期、短日期 -【办公类-53--01】2024年第一学期校历制作&#xff08;星火讯飞提取实际工作日&#xff0c;5天一行&#xff0…

Spring-Retry 框架实战经典重试场景

Spring-Retry框架是Spring自带的功能&#xff0c;具备间隔重试、包含异常、排除异常、控制重试频率等特点&#xff0c;是项目开发中很实用的一种框架。 1、引入依赖 坑点&#xff1a;需要引入AOP&#xff0c;否则会抛异常。 xml <!-- Spring-Retry --> <dependency&…

【AI大模型】通义千问:开启语言模型新篇章与Function Call技术的应用探索

文章目录 前言一、大语言模型1.大模型介绍2.大模型的发展历程3.大模型的分类a.按内容分类b.按应用分类 二、通义千问1.通义千问模型介绍a.通义千问模型介绍b.应用场景c.模型概览 2.对话a.对话的两种方式通义千问API的使用 b.单轮对话Vue页面代码&#xff1a;Django接口代码 c.多…

【C++】—— 类与对象(一)

【C】—— 类与对象&#xff08;一&#xff09; 1、类的定义1.1、类定义1.1.1、类定义格式1.1.2、成员变量的标识1.1.3、C 中的 s t r u c t struct struct1.1.4、C 中的内联函数1.1.5、总结 1.2、访问限定符1.3、类域 2、实例化2.1、实例化的概念2.2、对象大小2.2.1、对象的大…

【Hot100】LeetCode—1143. 最长公共子序列

目录 题目1- 思路2- 实现⭐最长公共子序列——题解思路 3- ACM 实现 题目 原题连接&#xff1a;1143. 最长公共子序列 1- 思路 模式识别1——> 求子序列问题 ——> dp数组定义为 i-1 和 j-1 动规五部曲 1- 创建dp数组 dp[i][j]&#xff0c;代表以 i-1 结尾的 text1 和…

“外挂”——逆向软件的分析与破解

本文来源无问社区&#xff0c;更多实战内容&#xff0c;渗透思路可前往查看http://www.wwlib.cn/index.php/artread/artid/11458.html#maodian1 前言&#xff1a; “外挂”的制作离不开软件的分析破解&#xff0c;这平时做ctf中的逆向题是有⼀定的差别的。最直观的区别体现在…

Leetcode 438. 找到字符串中所有字母异位词(java实现)

解题思路&#xff1a;滑动窗口。 异位词的典型解题方法就是用数组来统计出现的个数。本题也不例外。 直接上代码来进行讲解&#xff1a; public List<Integer> findAnagrams(String s, String p) {//初始化返回数组List<Integer> res new ArrayList<>();if …

百问网全志T113-PRO LVGL环境配置

运行LVGL示例 启动开发板 ​ 按要求接入电源或Type-c数据线&#xff0c;拨动拨码开关&#xff0c;将开发板上电 运行LVGL示例 ​ 打开串口终端软件&#xff0c;这里我使用MobaXterm软件演示&#xff0c;选择开发板的串口终端号&#xff0c;可以在设备管理中查看 这里我的串…

喜加一!望繁信科技再摘「2023年度新锐技术品牌奖」

1月31日&#xff0c;望繁信科技再次获颁由全球知名科技社区InfoQ评选的「2023年度新锐技术品牌奖」。这是InfoQ连续第二年将该奖项授予望繁信科技&#xff0c;用以肯定过去一年中&#xff0c;望繁信科技在流程挖掘与智能领域的技术创新与业务价值创造。 InfoQ在颁奖词中写到&…

【STM32】GPIO口以及EXTI外部中断

个人主页~ 有关结构体的知识在这~ 有关枚举的知识在这~ GPIO口以及EXTI外部中断 GPIO一、简介二、基本结构三、输入输出模式1、输入模式&#xff08;1&#xff09;上拉输入&#xff08;2&#xff09;下拉输入&#xff08;3&#xff09;浮空输入&#xff08;4&#xff09;模拟输…

【Python】面向对象的程序设计

一、面向对象的介绍 1.对象 对象是一种抽象概念&#xff0c;表示客观世界存在的实物&#xff0c;现实世界中能够看到的、触碰到的都可以成为对象&#xff0c;如&#xff1a;人、大象、小猫等。 对象通常分为两个部分&#xff0c;即静态部分和动态部分。静态部分为“属性”&a…

UE4 SLUA IOS打包报错解决办法

IOS打包报错&#xff1a;Fatal error "lua.h" file not found &#xff1a; SLua Unreal 在IOS远程打包编译的时候 Exteral没有copy过去&#xff0c;导致lua.h文件报错找不到&#xff0c;看了网上也没有写解决办法 错误信息截图&#xff1a; github上也有很多遇到改…

【每日一题】【动态规划(DP) 】【分治 迭代】“葡萄城杯”牛客周赛 Round 53 D题 小红组比赛 C++

“葡萄城杯”牛客周赛 Round 53 D题 小红组比赛 题目背景 “葡萄城杯”牛客周赛 Round 53 题目描述 小红希望出一场题目&#xff0c;但是他的实力又不够&#xff0c;所以他想到可以从以前的比赛中各抽一题&#xff0c;来组成一场比赛。不过一场比赛的难度应该是有限制的&a…

【nginx】nginx日志出现on文件

问题描述 生产发现容器里多出了一个on文件&#xff0c;非常大。 去看nginx配置已经这样保持16个月了&#xff08;已经去掉其他无关内容&#xff09; 原因解释 这个问题是因为nginx配置了access_log on; 有两个知识点&#xff1a; 1.access_log只有off的特殊值&#xff0c…

三菱电火花设备采集

三菱电火花机器一般情况下应该很少能见到,但在模具行业应该说是一种常见的加工设备。 一、通过官方手册采集数据 1.资料使用 连接方式: 这里重点关注下端口是1112 这里主要讲NC的状态转换 我们重点关注的是状态相关命令,手册中还支持NC程序上传和下载,感兴趣的话可以研究…

排序算法:堆排序,golang实现

目录 前言 堆排序 代码示例 1. 算法包 2. 堆排序代码 3. 模拟程序 4. 运行程序 5. 从大到小排序 堆排序的思想 堆排序的实现逻辑 1. 构建最大堆 2. 排序 循环次数测试 假如 10 条数据进行排序 假如 20 条数据进行排序 假如 30 条数据进行排序 假设 5000 条数据…

数据库——战德臣

1. 数据存储体系 1.1 计算机系统的存储体系 1.将不同性价比的存储组织在一起&#xff0c;满足高速的、大容量、低价格需求 2.CPU与内存直接交换信息&#xff0c;按存储单元&#xff08;存储字&#xff09;进行访问 3.外存按存储块进行访问&#xff0c;其信息先装入内存&…

NSSCTF-GDOUCTF 2023新生赛

[GDOUCTF 2023]hate eat snake 考察&#xff1a;js代码审计 打开题目&#xff0c;发现需要坚持60秒&#xff0c;那么简单的一个思路就是修改得分的变量>60即可 办法1&#xff1a;修改变量 右键查看源代码&#xff0c;之后发现有一个snake.js的文件&#xff0c;ctrlf搜索i…

健康小贴士丨中考考生怎么吃得好?

文章目录 引言三餐最佳的进食时间引言 中考正值初夏,气温急剧升高,食物易滋生细菌,发生食物中毒的风险也随之增加,医生建议 选择新鲜的食材,煮熟煮透,现吃现做,尽量不食用剩饭剩菜;不在路边露天摊点、不到无证和食品安全状况差的餐馆用餐;尽量减少在外就餐和点外卖的…