30 哈希的应用

位图

概念

题目

给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何判断一个数是否在这40亿个整数中

1.遍历,时间复杂度O(N)
2.二分查找,需要先排序,排序(N*logN),二分查找,logN。1个g大约存储10g字节,40亿个整数就需要160g字节,需要16个g的连续空间,内存中无法开出这么大的容量。
3.位图。判断一个数在不在的最小单位可以是位,将整数的范围全部做一个映射,有的值设置为1,没有就设置为0。这样,需要的空间就是42亿个位,0.5个g就可以存下

在这里插入图片描述
上面是3个字节的值,一个字节32位,可以表示的数的范围。计算一个值在第几个字节,在这个字节的第几个位。将一个数除以32就知道在第几个字节,取模就知道在第几个位,比如40,在第1个字节里,在第8位

位图概念

用每一位存放某种状态,适用于海量数据,数据无重复的场景,判断某个数据村部还存在的

实现

成员函数

可以用内置数组,这里直接用vector,成员类型是int

构造

为vector开辟需要的空间,每一位代表一个值,看需要多大的值,用非类型模板参数传入值。传入的是位,除以32再补上去的余数的一位,就是开辟多大整形的空间
在这里插入图片描述

set

将这个数据映射的值设为1。计算出数据所在的位,设置为1。i和j分别计算在第几个字节和第几位,让一个数的一位变为1,其他位不变化,可以或一个数,这个数这一位为1,其他位为0。可以将1左移j位就有了这个数

内存有大端和小端存储,左移都是往高位移动
在这里插入图片描述

reset

将这个数据清除,变为0。计算出i和j,让某一位变为0,可以与一个数,这个数这一位为0,其他都为1。1左移j位然后取反
在这里插入图片描述

test

查询一个数是否存在。1左移j位,与操作
在这里插入图片描述

#pragma once
#include <vector>

//N是需要多少位
template <size_t N>
class bitset
{
public:

	bitset()
	{
		//多开一个防止不够
		_bit.resize(N / 32 + 1, 0);
		//_bit.resize( (N >> 5) + 1, 0)
	}

	void set(size_t x)
	{
		int i = x / 32;
		int j = x % 32;
		_bit[i] = _bit[i] | (1 << j);
	}

	void reset(size_t x)
	{
		int i = x / 32;
		int j = x % 32;
		_bit[i] = _bit[i] & ~(1 << j);
	}

	bool test(size_t x)
	{
		int i = x / 32;
		int j = x % 32;
		return _bit[i] & (1 << j);
	}
public:
	std::vector<int> _bit;
};

测试

40亿的整数需要开辟的空间必须是无符号的整形大小,int是有符号的,所以用0xffffffff或-1
在这里插入图片描述

bitset<0xffffffff> bs;
bs.set(39256);
bs.set(43450);
bs.reset(40);

cout << bs.test(24515) << endl;
cout << bs.test(32329) << endl;
cout << bs.test(39256) << endl;
cout << bs.test(2314) << endl;
cout << bs.test(43450) << endl;

在这里插入图片描述

应用

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

题目

1.给定100亿个整数,设计算法找到只出现一次的整数
位图用一个位标识两种状态,存在和不在,找到出现一次的数需要第三种状态,可以用两个位来保存一个数。也可以复用前面的位图,用一个结构,成员两个位图。set时,当两个位图表示的是00的时候,就设置为01,01就设置为10,10就不做任何改变。打印的时候打印出01状态的数字

template <size_t N>
class twobitset
{
public:

	void set(size_t x)
	{
		//00 0次
		//01 1次
		//10 2次或以上
		int i = x / 32;
		int j = x % 32;
		if (_bs1.test(x) == false && _bs2.test(x) == false)
		{
			_bs2.set(x);
		}
		else if (_bs1.test(x) == false && _bs2.test(x) == true)
		{
			_bs1.set(x);
			_bs2.reset(x);
		}
	}

	void printOne()
	{
		for (size_t i = 0; i < N; i++)
		{
			if (_bs1.test(i) == false && _bs2.test(i) == true)
			{
				printf("%d ", i);
			}
		}

		printf("\r\n");
	}

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

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

和上面的方法一样,无论多少整数,还是申请42亿,两个位图里都有的就是交集

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

还是上面的类型,稍微修改,set函数10的时候变为11,11不变

template <size_t N>
class twobitset
{
public:

	void set(size_t x)
	{
		//00 0次
		//01 1次
		//10 2次或以上
		int i = x / 32;
		int j = x % 32;
		if (_bs1.test(x) == false && _bs2.test(x) == false)
		{
			_bs2.set(x);
		}
		else if (_bs1.test(x) == false && _bs2.test(x) == true)
		{
			_bs1.set(x);
			_bs2.reset(x);
		}
		else if (_bs1.test(x) == true && _bs2.test(x) == false)
		{
			_bs1.set(x);
			_bs2.set(x);
		}
	}

	void printOne()
	{
		for (size_t i = 0; i < N; i++)
		{
			if (_bs1.test(i) == false && _bs2.test(i) == true)
			{
				printf("一次%d ", i);
			}
			else if (_bs1.test(i) == true && _bs2.test(i) == false)
			{
				printf("两次%d ", i);
			}
		}

		printf("\r\n");
	}

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

布隆过滤器

提出

每次看新闻时,会不断推荐新的内容,去掉已经看过的内容。问题来了,如何实现推送去重的,用服务器记录所有看过的记录,当推荐系统推荐新闻时从每个用户的历史记录里筛选,过滤掉已经存在的记录,怎么快速查找

目前搜索采用的各种方法
1.暴力查找,数据量太大了,效率就低
2.排序+二分查找,问题a:排序有代价 问题b:数组不方便增删
3.搜索树,avl树+红黑树
上面的数据结构对空间消耗的都很高,如果面对数据量很大的
5.[整形],在不在及其扩展问题,位图和变形,节省空间
6.[其他类型] 在不在,哈希和位图结合,布隆过滤器

概念

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

在这里插入图片描述

一个值映射一个比特位,冲突的概率很大,两个不同的字符串正好映射在一个比特位,这时判断的存在就是错误的。为了降低误判的概率,多映射几个比特位,映射的越多,消耗的空间就越多

插入

在这里插入图片描述在这里插入图片描述在这里插入图片描述上图中,当k3个时,100m数据误判率0.01已经很低了

在这里插入图片描述
按公式计算:
在这里插入图片描述
3个哈希函数,n和m的关系是4.3,约为4倍容量

查找

将一个元素用多个哈希函数映射到一个位图中,因此被映射到的位置比特位一定为1.所以可以按照以下方式进行查找:分别计算每个哈希值对应的比特位置存储的是否为零,只要有一个零,代表该元素一定不在哈希表中,否则可能在哈希表中

注意:布隆过滤器如果说某个元素不存在时,一定不存在,如果该元素存在时,可能存在,因为存在一定的误判

删除

不能直接支持删除操作,因为在删除一个元素时,可能影响到其他元素
比如:删除上图的"tecent”元素,如果直接将该元素对应的二进制比特位置置为0,“baidu”元素也被删除了,因为这两个元素在多个哈希函数计算的比特位有重叠

一种支持删除的方法:将布隆罗氯气每个比特位扩展成一个小的计数器,插入元素时给k个计数器(k个哈希函数计算出的哈希地址)加一,删除元素时,给k个计数器减一,通过多占用几倍存储空间的代价来增加删除操作。如果引用计数最大为255时,映射的单位就必须扩展为8位

缺陷:
1.无法确认元素是否真正在布隆过滤器中
2.存在计数回绕

实现

#pragma once
#include <bitset>

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

		return hash;
	}
};

struct APHash
{
	size_t operator()(const std::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 std::string& key)
	{
		size_t hash = 5381;
		for (auto ch : key)
		{
			hash += (hash << 5) + ch;
		}
		return hash;
	}
};


template <size_t N, class K = std::string,
		class HashFunc1 = BKDRHash,
		class HashFunc2 = APHash,
		class HashFunc3 = DJBHash>
class BloomFilter
{
public:
	void set(const std::string& key)
	{
		size_t hashi1 = HashFunc1()(key) % N;
		size_t hashi2 = HashFunc2()(key) % N;
		size_t hashi3 = HashFunc3()(key) % N;

		_bs.set(hashi1);
		_bs.set(hashi2);
		_bs.set(hashi3);

	}

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

	bool test(const std::string& key)
	{
		size_t hashi1 = HashFunc1()(key) % N;
		if (_bs.test(hashi1) == false)
			return false;
		size_t hashi2 = HashFunc2()(key) % N;
		if (_bs.test(hashi2) == false)
			return false;
		size_t hashi3 = HashFunc3()(key) % N;
		if (_bs.test(hashi3) == false)
			return false;

		return true;
	}

private:
	std::bitset<N> _bs;
};

测试

#include <time.h>
#include <vector>
#include <iostream>
#include <string>
#include "bloom.h"

int main()
{
	srand(time(0));
	const size_t N = 100000;
	BloomFilter<N * 4> bf;

	std::vector<std::string> v1;
	//std::string url = "https://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html";
	std::string url = "猪八戒";

	for (size_t i = 0; i < N; ++i)
	{
		v1.push_back(url + std::to_string(i));
	}

	for (auto& str : v1)
	{
		bf.set(str);
	}

	// v2跟v1是相似字符串集(前缀一样),但是不一样
	std::vector<std::string> v2;
	for (size_t i = 0; i < N; ++i)
	{
		std::string urlstr = url;
		urlstr += std::to_string(9999999 + i);
		v2.push_back(urlstr);
	}

	size_t n2 = 0;
	for (auto& str : v2)
	{
		if (bf.test(str)) // 误判
		{
			++n2;
		}
	}
	std::cout << "相似字符串误判率:" << (double)n2 / (double)N << std::endl;

	// 不相似字符串集
	std::vector<std::string> v3;
	for (size_t i = 0; i < N; ++i)
	{
		//string url = "zhihu.com";
		std::string url = "孙悟空";
		url += std::to_string(i + rand());
		v3.push_back(url);
	}

	size_t n3 = 0;
	for (auto& str : v3)
	{
		if (bf.test(str))
		{
			++n3;
		}
	}
	std::cout << "不相似字符串误判率:" << (double)n3 / (double)N << std::endl;


	return 0;
}

在这里插入图片描述

优点

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

例如网页注册时,判断用户名存不存在。如果需要更进一步正确,可以将判断为存在的和数据库对比

缺陷

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

哈希切割

1. 给定两个文件,分别有100亿个query(字符串),只有1G内存,找到文件交集,精确算法和近似算法

近似算法就是上面的布隆过滤器
精确算法:
假设一个query有50个字节,100亿数据就需要500G,内存存不下,可以用哈希切分
读取每个query,计算i=Hash(query)%500,i是几,query就进入Ai小文件
在这里插入图片描述

A和B相同的字符串会进入相同编号的块里,只需要比较两个相同编号的块,就能找到交集
如果切分的某个文件大于10G,还是无法加载到内存里?
1.这个小文件大多数都是1个query
2.这个小文件,有很多不同的query

不管文件大小,直接读到内存插入set,如果是情况1,文件有很多重复,会去重
如果是情况2,插入后就会内存不足,抛异常,换一个哈希函数,二次划分,再找交集

2. 给一个超过100G大小的logfile,存ip地址,设计找出次数最多的ip地址

还是用哈希切分,相同的ip就进入了同一个小文件,然后用map统计次数。如果找topk,也可以用堆来解决

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

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

相关文章

CriticGPT: 用 GPT-4 找出 GPT-4 的错误

CriticGPT 是 OpenAI 发布的一个基于 GPT-4 的模型&#xff0c;它可以帮助我们人类 Review 并纠正 ChatGPT 在生成代码时的错误。使用 CriticGPT 审查代码时&#xff0c;有 60% 的概率生成的代码更好更正确。

搭建大型分布式服务(四十二)SpringBoot 无代码侵入实现多Kafka数据源整合插件发布

系列文章目录 文章目录 系列文章目录前言MultiKafkaStarter [V2.2]一、功能特性二、快速开始&#xff08;生产端&#xff09;三、快速开始&#xff08;消费端&#xff09;四、其它特性五、变更记录六、参考文章 前言 在分布式服务的架构演进中&#xff0c;消息队列作为核心组件…

容易涨粉的视频素材有哪些?容易涨粉的爆款短素材库网站分享

如何挑选社交媒体视频素材&#xff1a;顶级视频库推荐 在社交媒体上脱颖而出&#xff0c;视频素材的选择至关重要。以下是一些顶级的视频素材网站推荐&#xff0c;不仅可以提升视频质量&#xff0c;还能帮助你吸引更多粉丝。 蛙学网&#xff1a;创意的源泉 作为创意和独特性的…

携程二面测开—中核

4.12 35min面试经验 自我介绍 在面试的开始&#xff0c;我简洁明了地进行了自我介绍&#xff0c;突出了我的教育背景、技能特长以及实习经历&#xff0c;为后续的面试内容打下了良好的基础。 实习的具体工作内容 在谈及实习经历时&#xff0c;我详细阐述了在实习期间所承担…

NodeJs 使用中间件实现日志生成功能

写在前面 今天我们实现一个记录 nodejs 服务请求日志的功能&#xff0c;大概的功能包括请求拦截&#xff0c;将请求的信息作为日志文件的内容写入到 txt 文件中&#xff0c;然后输出到指定的日志到当天日期目录中&#xff0c;从而实现后续查找用户请求信息的功能&#xff0c;下…

Ubuntu 20.04安装中文输入法出错:gnome-user-docs-zh-hans安装失败

问题&#xff1a;Ubuntu20.04安装中文输入法出错&#xff1a;gnome-user-docs-zh-hans安装失败 现象&#xff1a; 打开language Support页面的时候&#xff0c;提示install依赖的文件 这个过程中会弹窗提示: The following packages have unmet dependencies:gnome-user-doc…

Lombok的使用

IntelliJ 安装 Lombok Lombok 注解大全说明 NonNull&#xff1a;给方法参数增加这个注解&#xff0c;会自动在方法内对该参数进行是否为空的校验&#xff0c;如果为空&#xff0c;则抛出 NPE&#xff08;NullPointerException&#xff09; Getter/Setter&#xff1a;用在属性上…

Python_Socket

Python Socket socket 是通讯中的一种方式&#xff0c;主要用来处理客户端与伺服器端之串连&#xff0c;只需要protocol、IP、Port三项目即可进行网路串连。 Python套件 import socketsocket 常用函式 socket.socket([family], [type] , [proto] ) family: 串接的类型可分为…

Rpc服务的提供方(Rpcprovider)的调用流程

首先&#xff0c;服务的提供方&#xff0c;会通过rpcprovider向rpc服务方注册rpc服务对象和服务方法&#xff0c; 那么&#xff0c;我们通过protobuf提供的抽象层的service和method&#xff0c;将服务对象和它所对应的服务方法记录在map表中&#xff0c; 当它启动以后&#xff…

隐藏Python运行产生的缓存文件(__pycache__)

不少同学使用VScode 提交或运行python代码的时候&#xff0c;出现一些缓存文件 类似于(__pycache__) 这种&#xff0c;对于我这种有一丢丢强迫症的人来说&#xff0c;运行一次就得删除一次&#xff0c;那有没有什么办法将其隐藏的&#xff1f; 在vscode编辑器中打开设置&#…

权限维持-域环境单机版---粘滞键屏保登录

免责声明;本文仅做技术交流与学习,,, 目录 粘滞键: 粘滞键位置&#xff1a; 屏保&登录: 1、WinLogon配合无文件落地上线 结合ps命令: 2、屏幕保护生效后执行后门 粘滞键: Windows维权之粘滞键项维权-腾讯云开发者社区-腾讯云 (tencent.com) 系统自带的辅助功能进行替…

密码学基础之ASN.1编码

简介 ASN.1(Abstract Syntax Notation One)&#xff0c;抽象语法标记。ASN.1是一种国际标准的正式语言&#xff0c;由国际标准化组织&#xff08;ISO&#xff09;和国际电信联盟&#xff08;ITU-T&#xff09;共同制定&#xff0c;用于定义数据结构的抽象语法。它的设计目标是…

鸿蒙开发设备管理:【@ohos.multimodalInput.inputConsumer (组合按键)】

组合按键 InputConsumer模块提供对按键事件的监听。 说明&#xff1a; 本模块首批接口从API version 8开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。本模块接口均为系统接口&#xff0c;三方应用不支持调用。 导入模块 import inputConsumer …

EfficientNet-V2论文阅读笔记

目录 EfficientNetV2: Smaller Models and Faster Training摘要Introduction—简介Related work—相关工作EfficientNetV2 Architecture Design—高效EfficientNetV2架构设计Understanding Training Efficiency—了解训练效率Training-Aware NAS and Scaling—训练感知NAS和缩放…

Leetcode3190. 使所有元素都可以被 3 整除的最少操作数

Every day a Leetcode 题目来源&#xff1a;3190. 使所有元素都可以被 3 整除的最少操作数 解法1&#xff1a;遍历 遍历数组&#xff0c;累加最少操作数&#xff0c;即 min(num % 3, 3 - num % 3)。 代码&#xff1a; /** lc appleetcode.cn id3190 langcpp** [3190] 使所…

自媒体常用的高清素材网站有哪些?自媒体必备的素材网站库分享

在自媒体时代&#xff0c;拥有高质量的素材库对创作者来说至关重要。素材的高清晰度、多样性和易用性可以显著提升你的内容吸引力和专业感。今天&#xff0c;我们就来探讨一些对自媒体创作者非常有用的高清素材网站。 蛙学网&#xff1a;自媒体创作者的理想选择 蛙学网为自媒体…

五、Spring IoCDI ★ ✔

5. Spring IoC&DI 1. IoC & DI ⼊⻔1.1 Spring 是什么&#xff1f;★ &#xff08;Spring 是包含了众多⼯具⽅法的 IoC 容器&#xff09;1.1.1 什么是容器&#xff1f;1.1.2 什么是 IoC&#xff1f;★ &#xff08;IoC: Inversion of Control (控制反转)&#xff09;总…

将深度相机的实时三维坐标数据保存为excel文档

一、如何将数据保存为excel文档 1.excel文件库与相关使用 &#xff08;1&#xff09;导入相应的excel文件库&#xff0c;导入前先要进行pip安装&#xff0c;pip install xlwt import xlwt # 导入用于创建和写入Excel文件的库 (2) 建立一个excel文档&#xff0c;并在第0行写…

51单片机STC89C52RC——12.1 数据存储芯片AT24C02

目的/效果 利用存储芯片AT24C02存储数据&#xff0c;LCD1602显示存储的数据。 一&#xff0c;STC单片机模块 二&#xff0c;AT24C02存储芯片 2.1 介绍 AT24C02是一个2K位串行CMOS E2PROM&#xff0c;内部含有256个8位字节&#xff0c;采用先进CMOS技术实质上减少了器件的功…

什么是中断?---STM32篇

目录 一&#xff0c;中断的概念 二&#xff0c;中断的意义 三&#xff0c;中断的优先级 四&#xff0c;中断的嵌套 如果一个高优先级的中断发生&#xff0c;它会立即打断当前正在处理的中断&#xff08;如果其优先级较低&#xff09;&#xff0c;并首先处理这个高优…