【C++ ——— 哈希】位图 | 布隆过滤器

news2024/11/20 10:46:38

文章目录

  • 1、位图
      • 1.1位图概念
  • 2.位图实现
  • 位图的应用
      • 1.一百亿个整数,设计算法找到只出现一次的整数?
      • 2.给两个文件,分别有一百亿个整数,我们只有1G内存该如何找到两个文件的交集?
      • 3.位图应用变形:一个文件有100亿个int,1G内存,设计算法找到出现不超过两次的所有整数。
  • 3.布隆过滤器
      • 3.1 布隆过滤器的提出
        • 3.2布隆过滤器概念
  • 海量数据的面试题
      • 1.给两个文件,分别由100亿个query(数据请求),我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法(近似算法就是布隆过滤器算法):
      • 2.给一个超过100G大小的log file,log 中存放着IP地址,设计算法找到出现次数最多的IP地址?如何找到top K的IP?


1、位图

1.1位图概念

1.面试题
给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。【腾讯】
解决方案:

  1. 二分查找。[不能用这种方式:1.要排序,2.40亿个整数需要约16g的内存,内存开不出这么大的空间]
  2. 位图解决:我们只需要标记这个值在不在,没必要存下来,让每个值映射一个比特位,需要2^32个比特位,也就是只需要0.5G。数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在。

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

2.位图实现

//N是需要多少比特位
template<size_t N>
class bitset
{
public:
	//构造
	bitset()
	{
		//构造函数提前开空间
		//_bits.resize(N/32+1, 0);
		_bits.resize((N >> 5) + 1, 0);
		              //2^5是32 且移位运算优先级要注意。
	}
	//映射一个值到位图中
	void set(size_t x)
	{
		//要把第j位处理为1,其他位不变。
		//找一个第j位为1其他位为0的补码
		//让他们或运算,或是两个位都为0时才为0
		size_t i = x / 32;
		size_t j = x % 32;
		_bits[i] |= (1 << j);//左移操作符向高位移动
							 //右移操作符向低位移动
							 //注意左移右移操作符不是方向的问题,是高位和低位的问题。
		
	}
	//将x的对应比特位置为0
	void reset(size_t x)
	{
		//把第j位处理位0,其他位不变
		//同上找第j位为0,其他位为1的补码
		//按位与运算。 与运算两个位都为1才为1
		size_t i = x / 32;
		size_t j = x % 32;
		_bits[i] &= ~(1 << j);
	}
	//查看这个x在位图中是否为1,为1就是存在。
	bool test(size_t x)
	{
		size_t i = x / 32;
		size_t j = x % 32;

		return _bits[i] & (1 << j);
	}

private:
	vector<int> _bits;
};

我们看上题说要开40亿个整形我们要怎么开呢?

//直接写为16进制
bitset<oxffffffff> bigbs;
//-1传过去转化为size_t类型直接能转化为最大值。
bitset<-1> bigbs2;

位图的应用

1.一百亿个整数,设计算法找到只出现一次的整数?

这有一百亿个数会不会出现空间不够的情况?
不会。就算是一百亿个整数最多也就42亿九千万个整数会出现很多重复值。
不论是100亿还是1000亿都只开42亿九千万个空间
这里需要三种状态:

  1. 出现0次
  2. 出现1次
  3. 出现两次即以上
    可以用两个位表示这三种状态。
    我们用两个位图表示一个位。
    在这里插入图片描述

位图代码实现:

//用两个位图来表示一个值
class twobitset
{
public:
	void set(size_t x)
	{
		//00->01
		//01->10
		//10->状态不变出现了两次即以上
		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 PrintfOnce()
	{
		for (size_t i = 0; i < N; i++)
		{
			if (_bs1.test(i) == false && _bs2.test(i) == true)
			{
				cout << i << endl;
			}
		}
		cout << endl;
	}



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

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

跟上题思路很像,各自映射到一个位图中。一个值两个位图都存在,就是交集。(先去重复值)

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

我们可以用上面的一个值映射两个位图解决这个方法,但是我们只有1G内存所以还是要做一些特殊处理的

template<size_t N>
//用两个位图来表示一个值
class twobitset
{
public:
	void set(size_t x)
	{
		//00->01
		//01->10
		//10->11
		//11->状态不变
		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 Printf()
	{
		for (size_t i = 0; i < N; i++)
		{
			if (_bs1.test(i) == false && _bs2.test(i) == true)
			{
				cout <<"1->"<< i << endl;
			}
			else if(_bs1.test(i) == true && _bs2.test(i) == false)
			{
				cout << "2->" << i << endl;
			}
		}
		cout << endl;
	}
private:
	bitset<N> _bs1;
	bitset<N> _bs2;
};

3.布隆过滤器

搜索

  1. 暴力排查 数据量大了,效率就低
  2. 排序+二分查找 问题a:排序有代价 问题b:不方便
  3. 搜索树-> AVL树+红黑树
  4. 哈希 ->扩容代价很高
    ——————————————
    以上数据结构,都是以空间换时间、空间消耗很高

数量很大的数据
5、[整形]的在不在及其扩展问题 – 位图及其变形 以时间换空间的算法
6、[其他类型]的在不在呢? --需要用到布隆过滤器

3.1 布隆过滤器的提出

我们在使用新闻客户端看新闻时,它会给我们不停地推荐新的内容,它每次推荐时要去重,去掉那些已经看过的内容。问题来了,新闻客户端推荐系统如何实现推送去重的? 用服务器记录了用户看过的所有历史记录,当推荐系统推荐新闻时会从每个用户的历史记录里进行筛选,过滤掉那些已经存在的记录。 如何快速查找呢?

  1. 用哈希表存储用户记录,缺点:浪费空间
  2. 用位图存储用户记录,缺点:位图一般只能处理整形,如果内容编号是字符串,就无法处理了。
  3. 将哈希与位图结合,即布隆过滤器
3.2布隆过滤器概念

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

用什么方法能让字符串映射到位图中呢?
我们可以考虑把先把字符串转换为一个对应的整形,不过整形的位图一个整形对应一个位,有42亿多的整型值够我们匹配,但是字符串呢?字符串可是无限的,所以肯定会有不同的字符串映射到同一个整形
不过我们只需要判断这个字符串在不在
以上思路:
判断:在(不准确,可能存在误判不同字符串映射的整形是同一个。本质也就是哈希冲突导致的。)
判断:不在(准确,没有字符串映射肯定就是不在的)

看下图中映射方式:
一个字符串映射三个位置的整形值。会降低误判的概率
布隆过滤器无法完全解决误判,所以布隆过滤器出现的场景就需要接受误判

在这里插入图片描述

布隆过滤器场景:
我们玩游戏创建角色时,需要给角色创建昵称,如果直接拿你输入的昵称和数据库里存的已有昵称进行比较那样效率会很低。
在这里插入图片描述
我们可以在数据库和玩家客户端的中间建立一个布隆过滤器
布隆过滤器只能准确判断不在数据库中的数据。
判断在数据库中的情况时,需要额外去数据库中匹配数据。
在这里插入图片描述
布隆过滤器、代码实现:

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


template<size_t N ,class K = string
		,class HashFunc1 = BKDRHash
		,class HashFunc2 = BKDRHash
		,class HashFunc3 = BKDRHash>
class BloomFilter
{
public:
	void Set(const K& key)
	{
		size_t Hash1 = HashFunc1()(key) % N;
		size_t Hash2 = HashFunc2()(key) % N;
		size_t Hash3 = HashFunc3()(key) % N;

		_bs.set(Hash1);
		_bs.set(Hash2);
		_bs.set(Hash3);
	}
	//一般不支持删除,删除一个值可能会影响其他值
	//非要支持删除的话,可以使用引用计数法,不过那样会
	void Reset(const K& key);
	bool Test(const K& key)
	{
		size_t Hash1 = HashFunc1()(key) % N;
		if (_bs.test(Hash1) == false)
			return false;
		size_t Hash2 = HashFunc2()(key) % N;
		if (_bs.test(Hash2) == false)
			return false;
		size_t Hash3 = HashFunc3()(key) % N;
		if (_bs.test(Hash3) == false)
			return false;


		//可能存在误判的风险
		return true;
	}
private:
	bitset<N> _bs;
};

海量数据的面试题

布隆过滤器(哈希切分)

1.给两个文件,分别由100亿个query(数据请求),我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法(近似算法就是布隆过滤器算法):

假设:平均一个query是50byte,100亿个query就是5000亿个byte。1G约等于10亿byte
那么5000亿byte 约等于 500G
内存存不下,红黑树/哈希表都使用不了。
分析:
1.我们可以先把两个文件的500G文件分成500份,每份1G左右

在这里插入图片描述
2.但是只是单纯的划分了500份空间对我们的查询比对操作效率,没有任何的提高。所以我们在划分空间时需要用到哈希算法,通过哈希函数把映射位置相同或相似的值,集中在一个小块。
我们做了这么多的目的就是为了把这块空间加载到内存中
下图这种方式就是哈希切分法
在这里插入图片描述
3.之后我们把Ai和Bi的数据分别加载到内存中,对他们两个分别进行位图映射。然后判断他们中是否有交集
在这里插入图片描述
4.但是这种思路还有一个问题–如果单个文件过大呢?
比如这个小文件有10G
这种单个文件过大差不多有两种情况:
一、这个小文件,内大多数都是相同的query
二、这个小文件,有很多种不同的query

解决方法:
Ai和Bi分别插入setA和setB中
不管文件大小直接插入:
情况一:这个小文件大多都是相同query,插入第一个后,后面重复插入就会set失败。
情况二:不断插入set后,内存不足,会抛异常,需要换个哈希函数把这个小文件进行二次切分,再找交集。

2.给一个超过100G大小的log file,log 中存放着IP地址,设计算法找到出现次数最多的IP地址?如何找到top K的IP?

哈希切割
1.我们先要统计次数,但是文件太大加载不到内存,我们无法统计次数,可以用哈希切分把相似IP地址分到一个区域里(跟上题思路类似)(相同IP一定会进入同一个文件)
2.切分完,直接在对应Ai中的数据使用map或者unordered_map统计次数,记得统计完次数和最大值比对进行更新。
3.top K问题:额外建一个小堆如果次数比堆顶大就进堆。

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

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

相关文章

Java八股文:程序员的“面试经”还是技术壁垒?

Java八股文&#xff1a;程序员的“面试经”还是技术壁垒&#xff1f; “八股文”&#xff0c;在中国古代科举考试中&#xff0c;指的是一种程式化的文章写作格式&#xff0c;内容空洞&#xff0c;缺乏创新。而如今&#xff0c;这个词语被赋予了新的含义&#xff0c;用来形容技术…

systemctl 添加自定义系统服务

以 “启动、停止、重启” boa web server为例&#xff1a; 1. 编写系统服务脚本 编写一个符合系统服务规范的脚本。这个脚本通常描述了服务的启动、停止、重启等行为。你可以使用shell、C、C、Java等语言来编写这个脚本。 # boa_server_run.sh&#xff1a;#!/bin/bashset -e …

软考随记(二)

I/O系统的5种不同的工作方式&#xff1a; 程序控制方式&#xff1a; 无条件查询&#xff1a;I/O端口总是准备好接受主机的输出数据&#xff0c;或是总是准备好向主机输入数据&#xff0c;而CPU在需要时随时直接利用I/O指令访问相应的I/O端口&#xff0c;实现与外设的数据交换 …

解决uni-app progress控件不显示问题

官方代码&#xff1a; <view class"progress-box"><progress :percent"80" show-info activeColor"red" stroke-width"10" /> </view> 进度条并不在页面中显示&#xff0c;那么我们需要给进度条加上宽高style"…

Appium安装及配置(Windows环境)

在做app相关自动化测试&#xff0c;需要使用appium来做中转操作&#xff0c;下面来介绍一下appium的环境安装配置 appium官方文档&#xff1a;欢迎 - Appium Documentation 一、下载appium 下载地址&#xff1a;https://github.com/appium/appium-desktop/releases?page3 通…

对未知程序所创建的 PDF 文档的折叠书签层级全展开导致丢签的一种解决方法

对需要经常查阅、或连续长时间阅读的带有折叠书签的 PDF 文档展开书签层级&#xff0c;提高阅览导航快捷是非常有必要的。 下面是两种常用书签层级全展开的方法 1、 FreePic2Pdf 1 - 2 - 3 - 4 - 5 - 6&#xff0c;先提取后回挂 2、PdgCntEditor 载入后&#xff0c;直接保存…

如何选择国产数据库?

ORACLE的强大是全方位的,作为甲方DBA,喝喝咖啡,看看报纸,开开会,临听一下ORACLE ACE吹水! 作为国企的DBA, CTO.基本上国企都算是传统行业,都是跑ERP系统,进销存系统.客户关系系统.基本上都是B2B业务. 直接面对普通老百姓的互联网业务非常少. 核心业务都是使用ORACLE,少量互联网…

官网:管它日薄西山or蒸蒸日上,气质这块,必须拿捏死死的。

在日薄西山的时候&#xff0c;网站建设面临着许多困难和挑战。市场竞争激烈&#xff0c;用户需求多样化&#xff0c;技术更新迅速&#xff0c;这些都要求我们在网站建设中拥有高尚的气质。 而在蒸蒸日上的时刻&#xff0c;网站建设同样需要我们拿捏好气质。只有坚持下去&#…

VScode远程连接linux服务器开发,误删了文件怎么找回。

VScode远程连接linux服务器开发&#xff0c;误删了代码文件 因为远程服务器大家都在用&#xff0c;没有足够权限去折腾。找遍了没找到方法&#xff0c;就告诉我远程的文件本地没有缓存啊&#xff01;我就差点开始重写代码了。 后来被我发现了TIMELINE功能&#xff0c;这个功能…

瓦罗兰特国际服 马来西亚服低价区有哪些 瓦罗兰特低价区排行

瓦罗兰特国际服 马来西亚服低价区有哪些 瓦罗兰特低价区排行 瓦罗兰特作为当今游戏圈内热度最高的一款游戏&#xff0c;在全世界范围内都有着许多的游戏玩家。游戏基础玩法延续了FPS经典的5v5玩法&#xff0c;同时开发商在不同的游戏角色上添加了不同的技能&#xff0c;使得游…

Qt xml学习之calculator-qml

1.功能说明&#xff1a;制作简易计算器 2.使用技术&#xff1a;qml,scxml 3.项目效果&#xff1a; 4.qml部分&#xff1a; import Calculator 1.0 //需要引用对应类的队友版本 import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 1.4 import QtScxml…

Springboot JVM监控 通过Promethus

Springboot内置了对Prometheus得支持&#xff0c;可以监测得点有&#xff1a; JVM各指标参数&#xff08;GC&#xff0c;堆&#xff0c;非堆等&#xff09;接口调用次数&#xff0c;延时系统内存&#xff0c;IO&#xff0c;CPU使用率 部署Prometheus和Grafana 准备一台2核4G…

HackTheBox-Machines--Sense

Popcorn 测试过程 1 信息收集 服务器开启80、443端口 80端口 访问 80 跳转到 443 – https://10.129.196.51/ &#xff0c;该页面是 pfSense 登录界面&#xff0c;默认密码是&#xff1a; admin/pfSense&#xff0c;使用默认账号密码登录失败 目录扫描 ./gobuster dir -u htt…

JavaWeb笔记整理+图解——Filter过滤器

欢迎大家来到这一篇章——Filter过滤器 监听器和过滤器都是JavaWeb服务器三大组件(Servlet、监听器、过滤器)之一,他们对于Web开发起到了不可缺少的作用。 ps:想要补充Java知识的同学们可以移步我已经完结的JavaSE笔记,里面整理了大量详细的知识点和图解,可以帮你快速掌…

期权高频交易能做吗?期权可以频繁交易吗?

今天带你了解期权高频交易能做吗&#xff1f;期权可以频繁交易吗&#xff1f;在期权交易市场&#xff0c;大部分人都知道不能频繁交易&#xff0c;就连不少投资新手都知道频繁交易是大忌&#xff0c;是错误的&#xff0c;是应该避免的。所以是不行的。 期权高频交易能做吗&…

学习信号和槽(1)

信号和槽函数 一、了解信号和槽的概念二、信号和槽的使用2.1、第一种方法2.2、第二种方法2.3、第三种方法2.4、第四种方法2.5、第五种方法 一、了解信号和槽的概念 信号&#xff08;Signal&#xff09;&#xff1a;就是在特定条件下被发射的事件&#xff0c;比如QPushButton 最…

OpenPCDet

一.简介 源码链接&#xff1a; https://github.com/open-mmlab/OpenPCDethttps://github.com/open-mmlab/OpenPCDet OpenPCDet 是一套基于PyTorch实现的点云3D目标检测代码库。&#xff08;也是个框架&#xff09; 设计思想&#xff1a;点云数据集&#xff08;KITTI、NuSce…

[深度学习]yolov10+bytetrack+pyqt5实现目标追踪

【简介】 利用YOLOv10、ByteTrack和PyQt5实现目标追踪是一个强大的组合&#xff0c;可以为用户提供一个交互式的实时目标追踪界面。以下是一个简化版的实现思路描述&#xff1a; 首先&#xff0c;YOLOv10是一个先进的目标检测算法&#xff0c;能够准确识别视频或图像中的目标…

注意力机制新突破!自适应Attention性能优越,可缝合到多种任务中

自适应注意力对比传统注意力机制&#xff0c;可以显著提高深度学习模型在处理复杂数据上的效率和准确性。 这种机制的核心在于&#xff1a;通过计算输入数据中不同部分之间的相关性或重要性&#xff0c;为这些数据部分分配不同的注意力权重&#xff0c;从而让模型能够更加专注…

xcode开发swift允许发送http请求设置

Xcode 现在新建项目默认只支持HTTPS请求&#xff0c;认为HTTP请求不安全&#xff0c;所以不支持。但是开发环境一般都是http模式&#xff0c;所以需要单独配置才可以访问。 需要到项目的设置里面&#xff0c;点击info&#xff0c;如果没有App Transport Security Setting这一项…