C++ - 哈希的应用

前面的文章中我们讲解了如何进行哈希表的构建以及使用实现的哈希表来模拟实现unordered_map,在本文中我们将继续来讲解一下哈希的应用。

位图

问题引入

首先我们来引入一个问题:给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。

第一眼看到这个题目的时候,我们可能会有这样的解题思路:使用排序+二分查找的方法或者将这些数据放入红黑树或者哈希表中,但是这样的处理有这一个问题就是内存不够。1G的数据约有10亿byte的大小(1G = 1024MB = 1024 * 1024KB = 1024 * 1024 * 1024byte)那么40亿的整数就一共有16G的大小,这么大量的数据是不能够放入内存中的。因为我们的目的是判断数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在。

位图概念

所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用
来判断某个数据存不存在的。

位图实现

template<size_t N>
class bitset
{
public:
	bitset()
	{
		_bits.resize(N/8 + 1, 0); // 为了防止访问越界的问题出现
	}

	void set(size_t x)
	{
		size_t i = x / 8; // 计算x映射的位置在第i个char数组的位置
		size_t j = x % 8; // 计算x映射的位置在第i个数组的第j个位置

		_bits[i] |= (1 << j); // 注意左移右移的区别
	}

	void  reset(size_t x)
	{
		size_t i = x / 8; 
		size_t j = x % 8; 

		_bits[i] &= ~(1 << j); // 注意左移右移的区别
	}

	bool test(size_t x)
	{
		size_t i = x / 8;
		size_t j = x % 8;

		return _bits[i] & (1 << j); // 运算符的优先级的问题,位运算的优先级比较低
		// (_bits[i] >> j) & 1; // 10%8 == 2
	}
private:
	vector<char> _bits;
};

按照上述的代码所示使用vector<char>类型的数组,每一个char有8个比特位我们对需要映射的数据进行映射,例如需要映射10这个数据,首先将10/8可以得到它在第几个char字符中,然后将其再模上8,就可以得到它在char字符的哪一个比特位上。这里还需要我们注意的就是之前学习的比特位是从右往左依次增大的,那么我们要将第i个字符的第j位设置为1时就需要将 1<<j 然后 | _bits[i]这样就可以得到正确的结果。同样要将映射取消就可以将_bits[i] &= ~(1<<j)。下面图中的数据转换为16进制就是8c。

位图的应用

下面我们再来看几道题目:

1. 给定100亿个整数,设计算法找到只出现一次的整数?
2. 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
3. 位图应用变形:1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数

针对第一个问题,100亿的整数,肯定是有着大量的重复,那么就需要我们开辟整数个数大小的空间,这里空间的开辟可以使用不同的方式,例如:

bitset<-1> bs; // 开辟的空间是根据数据的范围,开辟所有的整数可以通过-1的形式
bitset<0xffffffff> bs;

我们可以使用两张位图来解决第一个问题,给定两个位图进行处理同样的进行标记 00 01 10 11,出现了1次就将这两个位图的值设置为01;或着我们使用一个位图的方式,但是将开辟的空间改为四个,每两个作为一个组合进行标记。

针对第二个问题可以将其中的一个文件读进位图之中,在读取另外的一个文件,在就是存在交集,但是这样会出现一个问题就是存在重复的值需要进行去重操作,这里同样的有两种方法,方法1:将已经找到的交集的比特位归零防止二次统计;方法2:将两个文件分别读取进入两个位图进行比较。第三个问题与第一个问题是同样的解决方案。

总结一下就是位图的优点:速度快节省空间;缺点: 只能映射整形, 其他的类型如浮点数不能存储。那么这里就有新的方案 -- 布隆过滤器。

布隆过滤器

布隆过滤器概念

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

由于整数的范围比较小字符串的范围比较大,那么从小范围映射到大范围就会产生冲突布,隆过滤器降低冲突概率的方法思想就是,一个值映射一个位置容易误判,那么映射多个位置就可以降低误判率,按上图的例子,可以看出xyz分别映射在了不同的位置,假如现在有一个新的数据M的哈希字符串已经被xyz的数据置为了1,那么当我们进行查询的时候就会发现M虽然没有映射在布隆过滤器中但是在其中可以查询找得到。

对与一个数据来说如果它在布隆过滤器中那么它可能会是存在误判的,但是如果一个数据不存在其中,那么它一定是不存在的。

布隆过滤器的使用场景

例如以前在进行用户注册的时候需要我们填写称昵,这时就可以使用布隆过滤器,只要该用户名不出现在其中就可以使用,即使是出现虚假的重复也没有什么关系。再例如查询手机号是否重复,那么就可以先进行布隆过滤器的查找,如果不在就可以直接返回,如果存在,不管是什么情况,都可以去数据库中再次进行查找,这样就可以减少工作量。

布隆过滤器的实现

// N最多会插入的数据个数
// 哈希函数的个数,代表一个值映射几个位,哈希函数越多,误判率越低;但是哈希函数越多。平均的空间越多
template<size_t N, class K = string,
class Hash1 = BKDRHash,
class Hash2 = APHash,
class Hash3 = DJBRHash>

class BloomFilter
{
public:
	void set(const K& key)
	{
		size_t len = _X * N; // 布隆过滤器的长度
		size_t hash1 = Hash1()(key) % len;
		_bs.set(hash1);

		size_t hash2 = Hash2()(key) % len;
		_bs.set(hash2);

		size_t hash3 = Hash3()(key) % len;
		_bs.set(hash3);

		//cout << hash1 << " " << hash2 << " " << hash3 << " " << endl << endl;
	}

	bool test(const K& key)
	{
		size_t len = _X * N;
		size_t hash1 = Hash1()(key) % len;
		if (!_bs.test(hash1))
		{
			return false;
		}

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

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

private:
	static const size_t _X = 4; // 布隆过滤器的长度与存放数据个数的比值
	bitset<N*_X> _bs;
};

关于布隆过滤器其中还有着一些具体的问题,读者可以通过这个链接进行更加详细的阅读

详解布隆过滤器的原理,使用场景和注意事项 - 知乎 (zhihu.com)

下面我们来看一道题目

给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?(query就是相当与字符串,假设单个query,平均50byte,100亿个query就是5000亿byte)

按照之前的想法,我们可以将文件进行划分,例如划分成1000个小文件,两个文件分别切分成A0~A999和B0~B999,但是这样有一个问题就是我们要将每两个小文件都进行比较然后再取出交集的部分,这样的话工作量就会变得非常巨大。因此在这里需要我们使用一种名叫哈希切割的方法。

哈希切割 -- 小文件的编号 i = HashFunc(query) % 1000 ,每个query算出对应的i是多少就进入哪一个小文件。

然后,将AB两个文件都进行哈希切割,这样操作之后我们得到的小文件,是对应着的,在A0和B0中存放着进行切割之后相同结果的文件,那么在进行比较的时候只需要比较两者的同号小文件即可。相同的信息一定会存放在相同编号的小文件之中。

此时,有可能会出现新的问题就是:
        某些小文件因为不是平均切分会导致文件过大,这里又会有两种不同的情况
        1. 单个文件中有某个大量重复的query
        2. 单个文件中有大量不同的query

解决方案:使用unordered_map / set 依次读取文件query,插入set中
        1. 如果读取中整个小文件query都可以成功插入set那么就说明情况1
        2. 如果读取中整个小文件query 插入过程中异常(无内存),则是情况2,换用其他的哈希函数,再次分割,再求交集。
说明:set插入key如果已经有了返回false;如果没有内存就会抛出bad_alloc的异常

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

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

相关文章

介绍 9 个研发质量度量指标

研发质量管理中的 MTTR、MTBF、MTTF、MTTD 都是什么&#xff1f;今天我们从生产事件的全生命周期出发&#xff0c;认识研发质量管理的 9 个度量指标——「MT 家族」。 01 Mean Time To ALL 「MT」是 Mean Time 的缩写&#xff0c;意为平均时间&#xff0c;「MT 家族」则是 Li…

【AcWing算法基础课】第一章 基础算法(部分待更)

文章目录 前言课前温习一、快速排序核心模板1.1题目描述1.2思路分析1.3代码实现 二、归并排序核心模板2.1题目描述2.2思路分析2.3代码实现 三、二分查找整数二分题目一3.1题目描述3.2思路分析3.3代码实现 浮点数二分题目二3.1题目描述3.2思路分析3.3代码实现 四、高精度加法核心…

记录--巧用 overflow-scroll 实现丝滑轮播图

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 前言: 近期我在项目中就接到了一个完成轮播图组件的需求。最开始我也像大家一样&#xff0c;直接选择使用了知名的开源项目 "Swiper"&#xff0c;但是后来发现它在移动端项目中某些测试环境…

函数调用的机器级表示

文章目录 1.Call和ret指令2. 如何访问栈帧里面的数据为什么栈底放在上面&#xff0c;栈顶放在下面X86中的寄存器EBP、ESP寄存器push 、pop 指令mov 指令总结如何访问栈帧 3. 如何切换栈帧函数调用时函数返回时 4. 完整的函数调用过程1. 一个函数的栈帧内包含哪些内容2. 汇编代码…

Jenkins 发送文件到远程服务器:Publish Over SSH 插件

Jenkins 发送文件到远程服务器&#xff1a;Publish Over SSH 插件 文章目录 Jenkins 发送文件到远程服务器&#xff1a;Publish Over SSH 插件一、Publish Over SSH 插件1、概述2、主要功能和特点3、插件主页4、安装 Publish Over SSH 插件5、配置远程主机 二、发送文件到远程主…

windows安装python开发工具pycharm

下载地址 PyCharm: the Python IDE for Professional Developers by JetBrains 点击下载 安装 双击exe安装等待安装完成即可 设置python环境 添加本地python环境 选择python.exe 所在路径即可&#xff0c;2.x版本和3.x版本都可&#xff0c;根据需要进行调整

【Spring】——Spring生命周期

前言 ❤️❤️❤️Spring专栏更新中&#xff0c;各位大佬觉得写得不错&#xff0c;支持一下&#xff0c;感谢了&#xff01;❤️❤️❤️ Spring_冷兮雪的博客-CSDN博客 前面我们讲完了Spring中有关Bean的读和取&#xff0c;我们还没有好好去了解了解Bean对象&#xff0c;这篇 …

基于appnium+python+夜神模拟器的自动化

目录 1、安装夜神模拟器 2、定位元素 3、开始编码 首先搭好appnium环境&#xff01;参考https://www.cnblogs.com/testlearn/p/11419797.html 1、安装夜神模拟器 下载安装夜神模拟器后&#xff0c;在cmd命令输入adb connect 127.0.0.1:62001&#xff0c;显示出设备则表示…

redis协议与异步方式学习笔记

目录 1 交互方式 pipline2 广播机制2.1 概念演示2.2 使用场景 3 redis事物3.1 概念3.2 使用场景3.3 解决的问题3.3.1 背景&#xff1a;多线程竞争出现问题3.3.2 事务3.3.3 安全性事务 3.4两种类型的“事务”3.4.1 watch ... multi exec3.4.2 lua 脚本实现“原子”执行&#xff…

再以汇编代码分析c++的右值引用

汇编分析c语言的执行结果最为准确。 可见&#xff0c;右值引用其实还是引用&#xff0c; bb 和 cc 都是对 aa 的引用&#xff0c;其内存里存储了 aa 的地址。 而且还有一个很奇特的现象&#xff0c;bb无法给cc赋值&#xff0c;右值引用无法给右值赋值。 同样是调用std:: move…

d2l_第七章学习_卷积神经网络

参考: d2l今日学习——卷积神经网络&#xff08;CNN&#xff09;https://blog.csdn.net/m0_61165991/article/details/124176077图像工程&#xff08;上册&#xff09;-图像处理傅里叶变换https://blog.csdn.net/qq_43369406/article/details/131350139CNN卷积神经网络基础知识…

STC15 Proteus仿真DHT11环境湿度采集报警系统STC15W4K32S4-0043

STC15 Proteus仿真DHT11环境湿度采集报警系统STC15W4K32S4-0043 Proteus仿真小实验&#xff1a; STM32 Proteus仿真DHT11环境湿度采集报警系统STC15W4K32S4-0043 功能&#xff1a; Protues版本&#xff1a;8.9 硬件组成&#xff1a;STC15W4K32S4单片机 LCD1602显示器DHT11…

基于深度学习的高精度推土机检测识别系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于深度学习的高精度推土机检测识别系统可用于日常生活中检测与定位推土机目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的推土机目标检测识别&#xff0c;另外支持结果可视化与图片或视频检测结果的导出。本系统采用YOLOv5目标检测模型训…

2023 node 接入腾讯云短信服务,实现发送短信功能

1、在 腾讯云开通短信服务&#xff0c;并申请签名和正文模板 腾讯云短信 https://console.cloud.tencent.com/smsv2 a、签名即是短信的开头。例如 【腾讯云短信】xxxxxxx&#xff1b; b、正文模板即短信内容&#xff0c; 变量部分使用{1}&#xff0c; 数字从1开始累推。例如&a…

深度学习-第T10周——数据增强

深度学习-第T10周——数据增强 深度学习-第T10周——数据增强一、前言二、我的环境三、前期工作1、导入数据集2、查看图片数目 四、数据预处理1、 加载数据1.1、设置图片格式1.2、划分训练集1.3、划分验证集1.4、查看标签1.5、再次检查数据1.6、配置数据集 2、数据可视化 五、数…

软件工程实践总结

前言 这次我们学校花了很多心血在这次的课设上&#xff0c;真的是特别感动和感谢&#xff0c;当你遇到真心为你好对你好的老师的时候&#xff0c;真的是会觉得人间值得&#xff01; 之前在学软件工程的时候我就会觉得这些理论的东西有什么用啊&#xff0c;什么UML&#xff0c;…

Scrapy框架之下载中间件(详解)

目录 Scrapy中下载中间件 概念 方法 process_request(self, request, spider) 参数: process_response(self, request, response, spider) 参数 基本步骤 示例代码 注意 Scrapy 中 Downloader 设置UA 开发UserAgent下载中间件 代码 三方模块 配置模块到Settin…

【js30天挑战】第四天:数组操作

总结 filter(筛选条件为true的项) map(你想要输出的东西)&#xff0c;进来多少个 出去多少个 sort()&#xff0c;默认可排字母顺序。sort(compareFn(a, b))其中compareFn(a, b)返回的值若大于0则a在b的后面。 reduce()&#xff0c;最复杂。reduce(func(){上一轮计算出的结果…

Flink-SQL 写入PostgreSQL 问题汇总

​ 1.主键字段为空问题 错误信息 org.apache.flink.table.api.TableException: Column bus_no is NOT NULL, however, a null value is being written into it. You can set job configuration table.exec.sink.not-null-enforcerDROP to suppress this exception and drop …

罗技k380键盘教程

在智能手机和平板电脑上享受台式电脑般舒适便捷的输入体验。罗技蓝牙™ 多设备键盘 K380 是一款小巧独特的键盘&#xff0c;让您在家中任何地方都能使用个人设备进行沟通和创作。 借助便捷的易于切换™ 按钮&#xff0c;可以通过蓝牙™ 无线技术同时连接最多三台设备&#xff…
最新文章