yo!这里是哈希应用相关介绍

news2025/1/16 7:47:37

目录

前言

位图

模拟实现 

应用举例

布隆过滤器

模拟实现

应用举例

后记


前言

        在介绍unordered系列容器时,我们知道其底层使用的是哈希表,其实哈希是一种方法,是一种思想,哈希思想(Hashing)是一种在常数时间内完成数据插入和查找的算法思想。其基本思想是通过对数据进行一个映射函数的变换,把数据存储在一个数组中,这个数组称为哈希表。受这种思想启发,许多哈希应用应运而生,包括位图、布隆过滤器、海量数据处理等,下面我们逐一进行介绍,深度理解一下哈希这种思想,无论是在解决笔试题还是面试题都能有所帮助。

位图

        在32位机器下,一个整数最大可以表示到2^32,也就是42亿多。如果给定40亿个数,如何查找一个数是否在这40亿个数中?理想情况下,创建一个size为2^32的vector,用1标识存在,用0标识不存在,再将这40亿个数映射到vector中,查找一个数只要查看对应下标所在元素是1or0即可,但是想一下,这个vector有多大,2^32(个数)*4(int大小)Byte=16G,这是何等浪费。

        想一下,非要用一个int来记录0、1以标记存在情况吗?是不是可以考虑使用一个比特位,如果用一个比特位标记那需要多大空间呢?2^32bit=512MB,这极大地减少了内存的消耗同时又达到了目的。因此,可以在vector中存储字符,一个字符是8个比特位,使用的时候是一个比特位一个比特位的用,设计出这么一个类在处理这种海量数据上面可以说是相当的合适了,stl的位图(bitset)就是这样实现的,如下图。所谓位图,就是用每一位比特位来存放某种状态,适用于海量数据且数据无重复的场景,来判断某个数据存不存在的。具体使用不过多介绍,参考

https://cplusplus.com/reference/icon-default.png?t=N7T8https://cplusplus.com/reference/类中主要函数的使用在模拟实现时会介绍到,继续往下看!

  • 模拟实现 

        首先,通过模板参数将需要存储的个数传进来,因为我这里将成员属性vector的元素设置成了char(也可设置成int),所以在构造函数中将N个bit除以8,就是char的个数,再+1的原因是预留(比如N为6,则N/8就是0,+1才可以进行存储,多出来两个bit也没事)。

        其次,对于set,将N除以8得到第几个char,将N取模8得到此char的第几个比特位,将对应比特位【或】上1,其余【或】上0不变;对于reset,如set一样,不同在于将对应比特位【且】上0,其余位【且】上1不变;对于test,将对应比特位【且】上1得到0则当前比特位是0,得到1则当前比特位是1。

代码:

template<size_t N>
class Bitset
{
public:
	Bitset()
	{
		_bits.resize(N / 8 + 1, 0);
	}
	//将x位置的比特位设置成1
	void set(size_t x)
	{
		size_t i = x / 8;
		size_t j = x % 8;
		_bits[i] |= (1 << j);
	}
	//将x位置的比特位设置成0
	void reset(size_t x)
	{
		size_t i = x / 8;
		size_t j = x % 8;
		_bits[i] &= ~(1 << j);
	}
	//x位置的比特位是否为1
	bool test(size_t x)
	{
		size_t i = x / 8;
		size_t j = x % 8;
		if ((_bits[i] & (1 << j)) == 0)
			return false;
		else
			return true;
	}
private:
	vector<char> _bits;
};
  • 应用举例

1.给100亿个整数,找到只出现一次的整数

        因为整数范围是0~2^32-1,即最大是42亿多,所以这100亿个整数肯定存在重复。

        这里我们借助两个bitset,对应两个比特位结合起来标识不同的情况,即00表不存在;01表仅存在一个;10表存在两个及以上,将100亿个整数插入之后,查看对应比特位是01的就是只出现一次的整数。

        实现代码参考如下,twoBitset1是对此实现的一个类,成员对象包括两个位图bitset;对于set函数,若遇到00的情况说明不存在此整数,将其变成01,表示插入了一个,若遇到01说明此整数仅存在一个,将其变成10,表示再插入一个,若遇到10,说明此整数有两个或以上,无需再插入了;对于print_once_num函数,功能是同时遍历两个位图,对应比特位是01的记录下来,即为仅出现一次的整数。

代码:

template <size_t N>
class twoBitset1
{
public:
	void set(size_t x)
	{
		bool bs1bool = _bs1.test(x);
		bool bs2bool = _bs2.test(x);
		if (bs1bool == false && bs2bool == false)
		{
			_bs2.set(x);
		}
		else if (bs1bool == false && bs2bool == true)
		{
			_bs1.set(x);
			_bs2.reset(x);
		}
	}

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

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

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

        先想一下强调只有1G内存的意义在哪。前面提到过整数范围是0~2^32-1,也就是有2^32个整数,虽然文件里的整数有100亿个,但是说明其中肯定有重复的,映射到0~2^32-1的范围内最多也就是有2^32个,也就是2^32个bit,即2^29个byte=2^19个kb=2^9个mb,即512mb,使用两个这样的位图正好1G内存。

        这里我们就是使用两个位图,将两个文件的整数分别映射到这两个位图中,再让两个位图的对应比特位【且】一下(可以【且】到某一个位图上),然后找到此位图上比特位为1的整数就是两个文件的交集,思路很清晰,实现也不难,这里无参考代码,可以自己实现一下。

3.一个文件中有100亿个整数,有1G内存,找到出现次数不超过两次的所有整数。

         如题,不超过两次,也就是仅出现一次或出现两次的整数。其实,这道题是第一题的变形,思路跟第一个大差不差,就是需要多标识一种情况,即00标识没出现,01标识仅出现一次,10标识仅出现两次,11标识出现三次及以上,实现过程与第一题也一样,这里也不多赘述,可以自己实现一下。

布隆过滤器

        思考一下上面位图的缺点,是不是只能映射整数,如果关键字是非整数,比如浮点数、string,面对海量数据又如何处理呢,是不是可以通过哈希函数将这类关键字进行一个转换再映射到位图当中来标记数据是否存在啊,布隆过滤器(bloomfilter)就是这么操作的。布隆过滤器是一种概率性数据结构,使用多个哈希函数将同一个数据映射到位图中,可以高效地插入和查询,用来告诉某样东西一定不存在或者可能存在,如下图。

        比如说我们设计三个哈希函数来映射key,对于插入函数set,根据三个哈希函数将对应三个比特位改为1即可;对于查询函数,给一个key,也通过三个哈希函数计算出对应下标查看三个位置是否都是1,首先有一个不是1那肯定就是不存在,那三个都是1就一定存在吗?我们说不一定,因为这三个位置可能是另外一个key映射过来的,并不一定是你当前查询的key映射的,这也就是布隆过滤器为什么告诉你某样东西一定不存在或可能存在的原因,要是判断为存在,那就是可能存在也可能不存在,具体存不存在需要进一步判断,但这不是布隆过滤器的任务了。

  • 模拟实现

        先看属性,属性是一个位图,大小是ratio*N,其中N是插入元素的个数,ratio是一定的比率,详细可看详解布隆过滤器的原理,使用场景和注意事项 - 知乎 (zhihu.com)https://zhuanlan.zhihu.com/p/43263751icon-default.png?t=N7T8https://zhuanlan.zhihu.com/p/43263751再看模板参数,除了N以外,布隆过滤器需要传入一个类型,这里默认是string,因为布隆过滤器常用于处理字符串,还需要传入三个Hash函数用以处理同一个key映射到三个比特位,这里Hash函数也是针对于string,若是不同的类型,可以针对性的传入,其中针对于string的Hash函数的选取可参考各种字符串Hash函数(转) - 鸭子船长 - 博客园 (cnblogs.com)https://www.cnblogs.com/zl1991/p/11820922.htmlicon-default.png?t=N7T8https://www.cnblogs.com/zl1991/p/11820922.html这里我选择了其中的三个。

        对于插入函数set,使用Hash函数映射出三个哈希值,分别将其变成1;对于判断存在函数test,实现逻辑一样,使用位图的test函数判断是否存在,实现代码可参考下方。

代码:

#pragma once
#include <iostream>
#include <bitset>
#include <string>
#include <vector>
using namespace std;

//将关键字类型默认为string,是因为布隆过滤器常用于处理字符串
template<size_t N, class K = string, 
	class Hash1 = HashBKDR, class Hash2 = HashAP, class Hash3 = HashDJB>
class BloomFilter
{
public:
	void set(const K& key)
	{
		Hash1 hash1;
		Hash2 hash2;
		Hash3 hash3;

		size_t i1 = hash1(key) % (ratio * N);   //注意ratio*N加上括号,否则会有优先级问题
		size_t i2 = hash2(key) % (ratio * N);
		size_t i3 = hash3(key) % (ratio * N);

		_bs.set(i1);
		_bs.set(i2);
		_bs.set(i3);
	}

	bool test(const K& key)
	{
		Hash1 hash1;
		Hash2 hash2;
		Hash3 hash3;

		size_t i1 = hash1(key) % (ratio * N);   //注意ratio*N加上括号,否则会有优先级问题
		size_t i2 = hash2(key) % (ratio * N);
		size_t i3 = hash3(key) % (ratio * N);
		
		if (!_bs.test(i1))
			return false;   //明确不存在
		if (!_bs.test(i2))
			return false;   //明确不存在
		if (!_bs.test(i3))
			return false;   //明确不存在
		return true;   //可能存在(即有误判)
	}

private:
	const static size_t ratio = 5;
	bitset<ratio* N> _bs;
};

 注意:为什么布隆过滤器没有支持reset删除函数?

        因为删除某一个key时可能会影响到其他key,eg:如下图,删除美团,百度也会受到影响

那如何扩展布隆过滤器使得支持删除?可以使用计数技术为每个比特位增加一个计数器(类似硬链接),有key映射到比特位,计数器就++,删除就--,当减到0才真正的删除,比如:

但是布隆过滤器并没有这样做,因为空间消耗更加的大了,本身的优势就被削弱了,能应用到布隆过滤器的地方也不是很需要删除操作。

  • 应用举例

          那布隆过滤器能给出某样东西一定不存在,或者可能存在,这样的数据结构能应用在什么情形呢?其实还是比较多的,有很多地方就是不需要特别的准确,只需要一个概率即可,比如说游戏的昵称存在机制、预备黑名单等。

        先说游戏的昵称存在机制,在刚开始注册游戏时需要输入一个昵称,当你输入一个已经存在的,游戏会让你重新输入,直到输入一个游戏不存在的昵称。其中可以用一个布隆过滤器实现,将所有昵称放进一个布隆过滤器,当玩家输入一个昵称时,就会到这个布隆过滤器查询,若是不存在则真是不存在,此昵称可以使用,若时是存在则是可能存在,直接让玩家重新输入一个。

        一般情况下,黑名单会放进一个数据库,当判断一个ip是不是在黑名单中时,一个个去遍历查询就很麻烦,可以在这个正式黑名单之前放一个预备黑名单,用布隆过滤器实现,查询一个ip时,若不存在则肯定不在正式黑名单中,若存在则可能存在,则需要再进入正式黑名单中遍历检查。

        再来看一个有关的面试题:给两个文件,分别有100亿个query(请求(字符串)),只有1G内存,如何找到两个文件的交集,分别给出近似算法和精确算法。

近似算法:

        将一个文件的query映射到一个布隆过滤器,遍历另外一个文件的query去查看是否存在。这样做会存在两个问题:①会有误判,因为这是布隆过滤器自身的缺陷;②得到的交集存在重复,但是这也算是达到了近似算法的要求了。

精确算法:

         精确算法要求真真切切的交集,不能有重复不能有误判,那这就不能使用布隆过滤器了,这里我们使用哈希切分的思想。假设两个文件分别是A、B,

①一次读取A中的query,根据i=Hash(query)%份数M,将此query放进名为Ai的小文件,B中的query也是如此,分别放进Bi小文件中;

②将对应i相等的Ai、Bi两个小文件加载到内存,用set去判断两个小文件的交集,然后将所有对应小文件的交集放在一起即可,如下图。

原理:A、B中相同的query会进入相同编号的小文件,避免了A中的一个query要与B中所有的query都比较一番。

份数M的选取:按照平分情况分割下的小文件大小能加载进内存的份数。比如说,若一个query10byte,100亿个query就是3000亿type=300G,平均分成1000份下来,一份就是300MB左右,可加载进内存,因此M可选1000。但是值得注意,实际上切割并不是平分,而是哈希切割,也就是可能某一份小文件大小并不是300MB左右,可能已经多的加载不进内存了,此时需要一个循环重新选择哈希函数再去分割。

后记

        从上面两种应用以及举例可以看出,哈希思想特别适合处理海量数据的情形,可以将海量的数据通过哈希中一一映射的原理分类,从而解决”是否存在“、“出现几次”问题,这种题型在面试时很大概率会被提到,希望大家能够理解这种思想,在面对各种此类型的题目时都可以不变应万变,有不理解的地方可以问在评论区大家讨论,拜拜!

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

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

相关文章

AI爆文变现脚本:易用且免费的自动写作脚本更新了

之前给大家分享的AI爆文变现写作脚本 由于时间仓促&#xff0c;加上我对很多东西不熟悉 免费版本对新手小白来说&#xff0c;安装部署起来是非常的困难 于是这几天我加班加点把整个软件的部署简化 现在无需复杂的环境配置安装&#xff0c;下载配置下就可以使用了。 免费版…

Leetcode Hot100之六:42.接雨水

题目 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 提示&#xff1a; n height.length 1 < n < 2 * 10^4 0 < height[i] < 10^5 思路 暴力循环&#xff1a; 原本的思路是左边界i从左到…

【性能测试】非GUI模式Jemter压测+TPS性能拐点详细,一篇带你打通...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 非GUI模式执行Jem…

从windows iso文件中提取install.wim

1、首先从微软官方下载需要的windows镜像 https://www.microsoft.com/zh-cn/software-download/windows10/ 2、在下载的iso文件右键&#xff0c;打开压缩包&#xff0c;在sources文件夹下&#xff0c;应该就可以看到install.wim了。但似乎在最新的win10版本&#xff0c;微软采…

Netty入门指南之NIO Selector监管

作者简介&#xff1a;☕️大家好&#xff0c;我是Aomsir&#xff0c;一个爱折腾的开发者&#xff01; 个人主页&#xff1a;Aomsir_Spring5应用专栏,Netty应用专栏,RPC应用专栏-CSDN博客 当前专栏&#xff1a;Netty应用专栏_Aomsir的博客-CSDN博客 文章目录 参考文献前言问题解…

MySQL的索引和复合索引

由于MySQL自动将主键加入到二级索引&#xff08;自行建立的index&#xff09;里&#xff0c;所以当select的是主键或二级索引就会很快&#xff0c;select *就会慢。因为有些列是没在索引里的 假设CA有1kw人咋整&#xff0c;那我这个索引只起了前一半作用。 所以用复合索引&am…

1994-2021年分行业二氧化碳排放量数据

1994-2021年分行业二氧化碳排放量数据 1、时间&#xff1a;1994-2021年 2、来源&#xff1a;原始数据整理自能源年鉴 3、指标&#xff1a;统计年度、行业代码、行业名称、煤炭二氧化碳排放量、焦炭二氧化碳排放量、原油二氧化碳排放量、汽油二氧化碳排放量、煤油二氧化碳排放…

笔记:AI量化策略开发流程-基于BigQuant平台(一)

从本文开始&#xff0c;按照AI策略开发的完整流程&#xff08;共七步&#xff09;&#xff0c;上手在BigQuant平台上快速构建AI策略。本文首先介绍如何使用证券代码模块指定股票范围和数据起止日期。重要的事情说三遍&#xff1a;模块的输入端口有提示需要连线的上游数据类型&a…

第七章 块为结构建模 P4|系统建模语言SysML实用指南学习

仅供个人学习记录 这部分感觉很模糊&#xff0c;理解的不好&#xff0c;后面的图也没画了&#xff0c;用到的时候再来翻书 应用端口实现接口建模 端口port表示了块边界上的一个访问点&#xff0c;也可以是由该块分类的任何组成或引用边界上的可访问点。一个块可以有多个端口规…

odoo16 库存初始化 excel导入问题

最近在为一家公司实施odoo时&#xff0c;发现库存模块实施过程中按用户实际&#xff0c;产品初始化就是个问题。下面一一记录下 一个新公司&#xff0c;产品都有上百种&#xff0c;甚致几千种&#xff0c;如何把现有产品数据录入系统就是个不小的活。odoo16是有导入导出功能不…

链表经典面试题之二

今天我们做一道环形链表的题目力扣141题https://leetcode.cn/problems/linked-list-cycle/ 这道题让我们分析链表中是否存环&#xff0c;存在的话返回true&#xff0c;不存在返回false。首先看到这道题我们要捋顺思路&#xff0c;怎么才能达到它要的效果&#xff1f;要找出是否…

[工业自动化-11]:西门子S7-15xxx编程 - PLC从站 - 分布式IO从站/从机

目录 一、什么是以分布式IO从站/从机 二、分布式IO从站的意义 三、ET200分布式从站系列 一、什么是以分布式IO从站/从机 在工业自动化领域中&#xff0c;分布式 IO 系统是目前应用最为广泛的一种 I/O 系统&#xff0c;其中分布式 IO 从站是一个重要的组成部分。 分布式 IO …

Scrum敏捷开发全流程,3款必备的项目管理工具!

​Scrum是一种敏捷方法&#xff0c;致力于帮助团队高效地协作和完成复杂的项目。它强调迭代和快速迭代、自组织、快速响应变化等原则&#xff0c;使得项目开发变得更加灵活和高效。 在Scrum敏捷开发过程中&#xff0c;项目管理工具是必不可少的。下面介绍3款常用的敏捷开发工具…

面向对象基础(以python语言为例)

1、定义一个类&#xff1b;实例化类的对象&#xff1b;调用类中的方法 #定义一个类 class Student:#类方法&#xff08;即函数&#xff09;def study(self,course_name):print(f学生正在学习{course_name})def play(self):print("xx学生正在玩游戏")#实例化&#xf…

OpenCV-Python小应用(九):通过灰度直方图检测图像异常点

OpenCV-Python小应用&#xff08;九&#xff09;&#xff1a;通过灰度直方图检测图像异常点 前言前提条件相关介绍实验环境通过灰度直方图检测图像异常点代码实现输出结果 参考 前言 由于本人水平有限&#xff0c;难免出现错漏&#xff0c;敬请批评改正。更多精彩内容&#xff…

EDA实验----四选一多路选择器设计(QuartusII)

目录 一&#xff0e;实验目的 二&#xff0e;实验仪器设备 三&#xff0e;实验原理&#xff1a; 四&#xff0e;实验要求 五&#xff0e;实验内容及步骤 1.实验内容 2.实验步骤 六&#xff0e;实验报告 七.实验过程 1.创建Verilog文件&#xff0c;写代码 2.波形仿真 …

TMUX命令的基本操作和使用

tmux&#xff1a;是两个单词的缩写&#xff0c;即“Terminal MultipleXer”&#xff0c;意思是“终端复用器”。 TMUX使用场景&#xff1a;假如你需要跑大模型或者数据集特别大的AI任务时&#xff0c;它往往需要花较长时间才能跑完&#xff0c;在跑的过程中&#xff0c;不能断…

苹果手机安装未上架APP应用测试教程

STEP 2&#xff1a;找到下载的描述文件&#xff08;如果没有找到&#xff0c;请到 设置 - 通用 - 描述文件 中查看&#xff09; STEP 3&#xff1a;安装描述文件 STEP 4&#xff1a;输入解锁密码安装描述文件 STEP 5&#xff1a;同意免责声明&#xff0c;安装描述文件 STEP 6…

“目标值排列匹配“和“背包组合问题“的区别和leetcode例题详解

1 目标值排列匹配 1.1 从目标字符串的角度来看&#xff0c;LC139是一个排列问题&#xff0c;因为最终目标子串的各个字符的顺序是固定的&#xff1f; 当我们从目标字符串 s 的角度来看 LC139 “单词拆分” 问题&#xff0c;确实可以认为它涉及到排列的概念&#xff0c;但这种…

puzzle(1612)拼单词、wordlegame

目录 拼单词 wordlegame 拼单词 在线play 找出尽可能多的单词。 如果相邻的话&#xff08;在任何方向上&#xff09;&#xff0c;你可以拖拽鼠标从一个字母&#xff08;方格&#xff09;到另一个字母&#xff08;方格&#xff09;。在一个单词中&#xff0c;你不能多次使用…