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

目录

前言

位图

模拟实现 

应用举例

布隆过滤器

模拟实现

应用举例

后记


前言

        在介绍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左右,可能已经多的加载不进内存了,此时需要一个循环重新选择哈希函数再去分割。

后记

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

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

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

相关文章

软件测试岗,看完这31个问题再去面试(附解析)

今天&#xff0c;带给大家一套软件测试的高频面试题&#xff0c;这套题包含了各种类型的面试问题&#xff0c;涵盖了过往经历、业务考查、个人素质、HR问答等多种类型。完整的软件测试面试题大家可以移步公众号 「伤心的辣条」。在面试前可以结合自身实际情况&#xff0c;根据问…

Java编程--synchronized/死锁/可重入锁/内存可见性问题/wait()、notify()

前言 逆水行舟&#xff0c;不进则退&#xff01;&#xff01;&#xff01; 目录 线程安全 synchronized原子锁 可重入锁&#xff08;递归锁&#xff09; 死锁 内存可见性问题 wait()、notify() 线程安全 线程安全是指在多线程环境下&#xff0c;程序的行为表现仍然符合我…

WebSphere Liberty 8.5.5.9 (二)

WebSphere Liberty 8.5.5.9 &#xff08;二&#xff09; encode and decode Pre WebSphere Liberty 8.5.5.9 xor and AES 提取 D:\wlp-webProfile7-java8-8.5.5.9\wlp\lib 下必要加解密包 com.ibm.ws.crypto.certificateutil_1.0.12.jar com.ibm.ws.crypto.passwordutil_1…

C语言 每日一题 PTA 11.7 day13

1.求e的近似值 自然常数 e 可以用级数 1 1 / 1! 1 / 2! ⋯ 1 / n! ⋯ 来近似计算。 本题要求对给定的非负整数 n&#xff0c;求该级数的前 n 1 项和。 代码实现 #include<stdio.h> void main() {int a, i, j; double b 1; double c 1;printf("请输入一个数\n…

华为ensp:静态默认路由

静态路由 到r2 上的系统视图模式 下一跳为1.1.1.2 ip route-static 192.168.2.0 255.255.255.0 1.1.1.2 如果找2网段下一跳为1.1.1.2接口 默认路由 到r3上做的是默认路由 ip route-static 0.0.0.0 0 1.1.1.1 所有的流量去找1.1.1.1 查看效果 只要做完完整的路由就可…

思维模型 超限效应

本系列文章 主要是 分享 思维模型&#xff0c;涉及各个领域&#xff0c;重在提升认知。物极必反。 1 超限效应的应用 1.1 教育中的超限效应 一位老师在课堂上批评了一位学生&#xff0c;这位学生可能会因为老师的批评而感到沮丧和失落。如果老师在接下来的课程中继续批评这位…

【Armstrong公理】【求闭包和候选码】【判断范式】

1. Armstrong公理 2.求闭包和候选码 3.判断范式

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

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

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

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

C语言 每日一题 PTA 11.8 day14

1.矩阵A乘以B 给定两个矩阵A和B&#xff0c;要求你计算它们的乘积矩阵AB。需要注意的是&#xff0c;只有规模匹配的矩阵才可以相乘。 即若A有Ra​行、Ca列&#xff0c;B有Rb行、Cb列&#xff0c;则只有Ca与Rb​相等时&#xff0c;两个矩阵才能相乘。 输入格式&#xff1a; 输入…

【网络编程】网络层——IP协议

文章目录 基本概念路径选择主机和路由器 IP协议格式分片与组装网段划分IP地址的数量限制私网IP地址和公网IP地址深入认识局域网路由 基本概念 TCP作为传输层控制协议&#xff0c;其保证的是数据传输的可靠性和传输效率&#xff0c;但TCP提供的仅仅是数据传输的策略&#xff0c…

selenium+python做web端自动化测试框架实战

最近受到万点暴击&#xff0c;由于公司业务出现问题&#xff0c;工作任务没那么繁重&#xff0c;有时间摸索seleniumpython自动化测试&#xff0c;结合网上查到的资料自己编写出适合web自动化测试的框架&#xff0c;由于本人也是刚刚开始学习python&#xff0c;这套自动化框架目…

开发ios电脑app的费用受到哪方面的影响?

开发iOS电脑应用程序的费用受到多方面的影响&#xff0c;包括市场需求、功能复杂度、设计要求、开发人员经验、市场竞争以及后期维护等因素&#xff0c;下面我们将详细介绍这些影响因素&#xff0c;帮助您更好地了解开发iOS应用程序的费用构成。 一、市场需求 市场需求是影响…

puzzle(1612)拼单词、wordlegame

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

计算机网络技术

深入浅出计算机网络 微课视频_哔哩哔哩_bilibili 第一章概述 1.1 信息时代的计算机网络 1. 计算机网络各类应用 2. 计算机网络带来的负面问题 3. 我国互联网发展情况 1.2 因特网概述 1. 网络、互连网&#xff08;互联网&#xff09;与因特网的区别与关系 如图所示&#xff0…

C语言——个位数为 6 且能被 3 整除但不能被 5 整除的三位自然数共有多少个,分别是哪些?

#define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h> int main() {int i,j0;for(i100;i<1000;i) {if(i%106&&i%30&&i%5!0){printf("%6d",i); j;}}printf("\n一共%d个\n",j);return 0; } %6d起到美化输出格式的作用&#xff…

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

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

centos7安装linux版本的mysql

1.下载linux版本的mysql 进入mysql官网&#xff0c;点击社区版本下载&#xff1a; https://dev.mysql.com/downloads/mysql/ 选择版本&#xff0c;可以跟着我下面这个图进行选择&#xff0c;选择红帽版本的既可&#xff0c;都是linux版本的。 2.上传解压linux版本的mysql安装包…

悟空crm二次开发 增加客户保护功能 (很久没有消息,但是有觉得有机会的客户)就进入了保护转态

需求&#xff1a;客户信息录入不限数量&#xff0c;但是录入的信息1个月内只有自己和部门领导能看到&#xff0c;如果1个月内未成交或者未转移至自己的客保 则掉入公海所有人可见&#xff0c;这里所说的客保就是现在系统自带的客保 1、需求思维导图 2、新增保护按钮 3、点击该…

transformers安装避坑

1.4 下载rust编辑器 看到这里你肯定会疑惑了&#xff0c;我们不是要用python的吗&#xff1f; 这个我也不知道&#xff0c;你下了就对了&#xff0c;不然后面的transformers无法安装 因为是windows到官网选择推荐的下载方式https://www.rust-lang.org/tools/install。 执行文…