C++进阶:哈希(2)位图与布隆过滤器

news2024/12/23 20:22:13

目录

  • 1. 位图(bitset)
    • 1.1 引子:海量整形数据的处理
    • 1.2 结构描述
    • 1.3 位图实现
    • 1.4 位图相关题目练习
  • 2. 布隆过滤器(BloomFilter)
    • 2.1 引子:海量非int类型数据处理(string)
    • 2.2 结构描述
    • 3.3 布隆过滤器的实现
    • 3.4 相关练习

1. 位图(bitset)

1.1 引子:海量整形数据的处理

  1. 背景问题:40亿个无序的无符号整数,如何快速判断一个无符号整形是否存在?
  2. 当我们进行大量数据处理时,内存不足以一次性将全部数据读取处理,我们应该如何解决,接下来,我们就来进行相关的学习。

1.2 结构描述

  1. 当数据量较小时,内存能够一次性读取所有数据:
    <1> 排序 + 二分查找
    <2> set + find
  2. 当数据量过大,内存无法一次性读取并处理所有数据,在只需要查找的背景下,我们能否将相关数据状态信息进行压缩,使得内存存储的代价大大降低呢?
  3. 计算机中,可以标识数据状态的最小单位为bit位,而在数据只有整形的情况下,可以直接将每个数字映射一一对应的bit位,物理上,通过开辟一段指定连续内存空间来映射存储,即位级别的哈希表,我们称之为位图。
  4. 位图的方式,可以大大较少需要消耗的内存空间,约42亿整形数据范围经过转换仅需大小512的内存空间。
  5. 开辟空间空间时,我们无法以bit位为单位进行开辟,但可以通过计算机内置类型来间接申请开辟,下面的具体实现我们采用int类型。

在这里插入图片描述

1.3 位图实现

  1. 位图结构
//int类型数据
//unsigned_int类型最大数据范围:UINT_MAN,-1,0x0ffffffff
template<size_t N>//非类型模板参数确定需开辟bit位数量
class bit_set
{
public:

	//构造
	bit_set()
	{
		//N为bit位数量,转换为整形需除32换算
		//仅除32开辟不够
		_data.resize(N / 32 + 1, 0);
	}

	//bit位置1,非类型模板参数定义的类,类型不带模板参数
	void set(size_t pos);

	//bit位置0
	void reset(size_t pos);

	//测试某个bit位的状态
	bool test(size_t pos);

private:
	vector<int> _data;//vector内为动态数组
};

  1. 操作实现
//bit位置1
void set(size_t pos)
{
	assert(pos <= N);

	size_t hashi = 1;
	
	//第几个数组元素
	int i = pos / 32;
	//哪一个bit位
	int j = pos % 32;
	//大端机与小端机的数据存储方式不同
	//但位左移右移的操作并不会因此受影响
	//编程语言中的左移右移概念,指的是向计算机的高位,低位移动
	hashi <<= j;

	_data[i] |= hashi;
}

//bit位置0
void reset(size_t pos)
{
	assert(pos <= N);

	size_t hashi = 1;

	int i = pos / 32;
	int j = pos % 32;
	
	hashi = ~(hashi << j);

	_data[i] &= hashi;
}

//测试某个bit为的状态
bool test(size_t pos)
{
	assert(pos <= N);

	size_t hashi = 1;

	int i = pos / 32;
	int j = pos % 32;

	hashi <<= j;

	hashi &= _data[i];

	return hashi;
}

1.4 位图相关题目练习

  1. 100亿个int类型的数据,查找其中只出现一次的数据
    答:使用两个位图嵌套封装的方式,实现可以使用两个bit位表示多种状态的数据结构,从而来完成筛选。
template<size_t N>
class two_bit_set
{
public:
	
	//置1
	void set(size_t pos)
	{
		if (a.test(pos) == 0 && b.test(pos) == 0)//一次
		{
			//0
			b.set(pos);//1
		}
		else if(a.test(pos) == 0 && b.test(pos) == 1)//两次
		{
			a.set(pos);//1
			b.reset(pos);//0
		}
		else//三次以上
		{
			a.set(pos);//1
			b.set(pos);//1
		}
	}

	//置0
	void reset(size_t pos)
	{
		a.reset(pos);
		b.reset(pos);
	}

	size_t test(size_t pos)
	{
		if (a.test(pos) == 0 && b.test(pos) == 0)
		{
			return 0;
		}
		else if (a.test(pos) == 0 && b.test(pos) == 1)
		{
			return 1;
		}
		else
		{
			return 2;
		}
	}

private:
	bit_set<N> a;
	bit_set<N> b;
};
  1. 现有两个文件,其中分别有100亿个int类型的数据,现内存大小为1G,如何找到两个文件的交集
    答: 将两个文件中的数据分别set入两个位图当中,然后同步遍历两个位图,查找交集
    补充:当数据量较小时,可以使用set容器,去重后,使用去重算法遍历
  1. 现有一个文件,其中有着100亿个int类型的数据,现内存大小为1G,如何找出重复次数不超过两次的数据
    答:位图映射,多个bitset嵌套,表示多种状态
  1. 现有一个文件,有着100亿个int类型的数据,内存大小为512MB,如何找到只出现一次的所有数据
    答:因为内存空间相对来说严重不足,我们无法一次性创建出映射包括所有int范围的位图,所以,我们只能较少位图的大小,将数据切分,分批次处理,一次只处理一定数据范围内的数据。

在这里插入图片描述

2. 布隆过滤器(BloomFilter)

2.1 引子:海量非int类型数据处理(string)

  1. 问题背景: 当存在大量非int类型的数据,诸如,string,内存空间无法容纳处理,有因为非整形数据,也无法直接使用位图映射时,我们该如何对其中的相应数据进行查询

2.2 结构描述

  1. 在之前的学习中,我们学习与简单实现了哈希表这一数据结构,在key映射的操作上,其中对于非int类型数据的处理,采用了哈希函数转换,将非int类型数据转换为int类型,而后再进行映射。
  2. 对于大量非int类型数据的处理,我们也采用哈希函数的方式,转换key值将其存储在位图当中,进行数据信息的压缩,但由于并非1:1直接映射,同时数据量又非常大,所以此种哈希函数的映射方式很大可能存在着误判(将原本不存在的数据,判断为存在)。
  3. 因为哈希函数key值映射的不稳定性,key值大概率可能发生越界情况,所以,对此的处理采用key值越界回绕映射的方式。
  4. 因为存在误判的可能,当只有单个哈希函数时,这种误判的概率是极高的,所以,我们一般采用多个哈希函数共同映射。
  5. 经过查阅资料,得到如下一个的插入数据个数与位图开辟空间大小之间的数学公式,而位图开辟的空间越大,key值得误判率越低。

在这里插入图片描述

  1. 此种非int类型,多哈希函数位图映射的数据结构,我们就称之为布隆过滤器(BloomFilter)
  2. 因为多哈希函数共同映射,每个数据得key值之间可能存在交集,所以布隆过滤器不支持置0删除key值的操作。

在这里插入图片描述

3.3 布隆过滤器的实现

  1. 哈希函数
//BKDR
struct HashBKDR
{
	size_t operator()(const string& s)
	{
		size_t hash = 0;
		for (auto ch : s)
		{
			hash *= 131;
			hash += ch;
		}

		return hash;
	}
};

//AP
struct HashAP
{
	size_t operator()(const string& s)
	{
		size_t hash = 0;
		for (size_t i = 0; i < s.size(); i++)
		{
			if ((i & 1) == 0) // 偶数位字符
			{
				hash ^= ((hash << 7) ^ (s[i]) ^ (hash >> 3));
			}
			else              // 奇数位字符
			{
				hash ^= (~((hash << 11) ^ (s[i]) ^ (hash >> 5)));
			}
		}

		return hash;
	}
};

//DJB
struct HashDJB
{
	size_t operator()(const string& s)
	{
		size_t hash = 5381;
		for (auto ch : s)
		{
			hash = hash * 33 ^ ch;
		}

		return hash;
	}
};
  1. 具体实现
//非类型模板参数声明先于模板参数
template<size_t M, class K = string, class HashFunc1 = HashBKDR, class HashFunc2 = HashAP, class HashFunc3 = HashDJB>
class BloomFilter
{
public:
	
	//添加
	void set(K key)
	{
		//key值可能越界,需进行回绕
		size_t key1 = hs1(key) % N;
		size_t key2 = hs2(key) % N;
		size_t key3 = hs3(key) % N;

		bs.set(key1);
		bs.set(key2);
		bs.set(key3);
	}
	
	//布隆过滤器不支持删除,可能会同时影响其他值

	//查询
	bool test(K key)
	{
		//匿名对象
		size_t key1 = HashFunc1()(key) % N;
		size_t key2 = HashFunc2()(key) % N;
		size_t key3 = HashFunc3()(key) % N;

		//存在有误判,不存在无误判
		if (bs.test(key1) == false)
			return false;
		if (bs.test(key2) == false)
			return false;
		if (bs.test(key3) == false)
			return false;

		return true;
	}


private:
	//非类型模板参数必须是整形家族,且为const修饰
	static const size_t N = 8 * M;//计算公式,M为插入数据个数,N为开辟空间大小
	bit_set<N> bs;
	HashFunc1 hs1;
	HashFunc2 hs2;
	HashFunc3 hs3;
};

3.4 相关练习

  1. 两个文件分别有100亿个query(查询请求,可以简单理解为字符串,一个query的大小为50byte),现只有1G内存
    <1> 如何大致找出两个文件的交集
    <2> 如何精确找出两个文件的交集

在这里插入图片描述

  1. 答:
    <1> 将文件至内存足够一次性容纳的大小,分批次读取
    <2> 但哈希切分的方式,并非是按照文件大小来切分,所以导致文件的大小存在不确定性
    <3> 当文件存在有较多相似,相同内容时,哈希切分后还会存在大型文件,内存仍无法容纳
    <4> 位图具有自动去重的特性,当出现大型子文件时,我们可以现进行内容读取,当读取过程中出现异常,那么,就证明文件体积过大的原因并非相同内容堆积所引起,此时,我们只需要再切换另一种哈希函数来进行切分文件,重复上述步骤即可

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

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

相关文章

【Linux:环境变量】

环境变量一般是指在操作系统中用来指定操作系统环境的一些参数 常见的环境变量&#xff1a; PATH 指定可执行程序的搜索路径 系统级的文件&#xff1a;/etc/bashrc 用户级文件&#xff1a;~/.bashrc ~/.bash_profile HOME 指定用户的主要工作目录&#xff08;当前用…

react18【系列实用教程】useReducer —— 升级版的 useState (2024最新版)

useReducer 可看做升级版的 useState &#xff0c;其强大之处在于&#xff0c;可以自定义复杂的响应式变量修改逻辑。 useReducer 语法 useReducer 是 hook 函数 第一个参数&#xff08;必要&#xff09;&#xff1a; 自定义的 reducer 函数&#xff08;详见下文介绍&#xff…

英伟达发布 VILA 视觉语言模型,实现多图像推理、增强型上下文学习,性能超越 LLaVA-1.5

前言 近年来&#xff0c;大型语言模型 (LLM) 的发展取得了显著的成果&#xff0c;并逐渐应用于多模态领域&#xff0c;例如视觉语言模型 (VLM)。VLM 旨在将 LLM 的强大能力扩展到视觉领域&#xff0c;使其能够理解和处理图像和文本信息&#xff0c;并完成诸如视觉问答、图像描…

crossover24中文破解版百度云免费下载 crossover永久免激活汉化包安装使用教程 crossover24激活码分享

原则上&#xff0c;我们不提倡各位使用破解版&#xff0c;这是处于对知识产权的保护&#xff0c;也是为了各位的长远利益。使用正版你可以获得更优质的服务和完善的产品功能。 但仍然有部分用户由于预算、使用习惯等原因&#xff0c;需要破解版。所以本文不讲原则&#xff0c;…

异步编程CompletableFuture总结

文章目录 1. 简介&#xff1a;2. 比较1、传统方式2、使用CompletableFuture&#xff1a;异步执行返回值 3、组合处理&#xff1a;anyOfallof : 4. 异步回调&#xff1a;thenAcceptthenApplywhenComplete等同于 thenAccepthandel()等同于thenApply 5. 常用方法&#xff1a;1、su…

【SQL】SQL常见面试题总结(3)

目录 1、聚合函数1.1、SQL 类别高难度试卷得分的截断平均值&#xff08;较难&#xff09;1.2、统计作答次数1.3、得分不小于平均分的最低分 2、分组查询2.1、平均活跃天数和月活人数2.2、月总刷题数和日均刷题数2.3、未完成试卷数大于 1 的有效用户&#xff08;较难&#xff09…

QCustomplot---动态图

QCustomplot绘制动态曲线图-游标及鼠标跟踪显示数值_qcustomplot 游标-CSDN博客 m_timer new QTimer(this);connect(m_timer,SIGNAL(timeout()),this,SLOT(slotTimeout()));m_timer->start(50); void MainWindow::slotTimeout() {static int p0;static int i0;double m,m1…

C语言 | Leetcode C语言题解之第92题反转链表II

题目&#xff1a; 题解&#xff1a; struct ListNode *reverseBetween(struct ListNode *head, int left, int right) {// 因为头节点有可能发生变化&#xff0c;使用虚拟头节点可以避免复杂的分类讨论struct ListNode *dummyNode malloc(sizeof(struct ListNode));dummyNode…

C++ | Leetcode C++题解之第92题反转链表II

题目&#xff1a; 题解&#xff1a; class Solution { public:ListNode *reverseBetween(ListNode *head, int left, int right) {// 设置 dummyNode 是这一类问题的一般做法ListNode *dummyNode new ListNode(-1);dummyNode->next head;ListNode *pre dummyNode;for (i…

使用make_blobs生成数据并使用KNN机器学习算法进行分类和预测以及可视化

生成数据 使用make_blobs生成数据并使用matplotlib进行可视化 完整代码&#xff1a; from sklearn.datasets import make_blobs # KNN 分类器 from sklearn.neighbors import KNeighborsClassifier # 画图工具 import matplotlib.pyplot as plt # 数据集拆分工具 from sklea…

Synchronize 底层实现原理

1 、加锁实现原理 public class SynchronizedTest {public void get(){synchronized (this){ // 这个是同步代码块System.out.println("你好呀");}}public synchronized void f(){ //这个是同步方法System.out.println("Hello world");}public s…

Python | Leetcode Python题解之第91题解码方法

题目&#xff1a; 题解&#xff1a; class Solution:def numDecodings(self, s: str) -> int:n len(s)# a f[i-2], b f[i-1], c f[i]a, b, c 0, 1, 0for i in range(1, n 1):c 0if s[i - 1] ! 0:c bif i > 1 and s[i - 2] ! 0 and int(s[i-2:i]) < 26:c aa,…

QT多线程的使用

目录 一.介绍 二.第一种多线程方式 1.创建一个线程子类&#xff0c;继承QT中的QThread 2.重新父类的run( )方法 3.在线程中创建子线程对象 4.run( )方法 5.启动子线程 三.第二种多线程方式 1.创建一个新类&#xff08;这个类是QObject的派生&#xff09; 2.在这个类中…

QT:QML与C++交互

目录 一.介绍 二.pro文件添加模块 三.h文件 四.cpp文件 五.注册 六.调用 七.展示效果 八.代码 1.qmlandc.h 2.qmlandc.cpp 3.main.cpp 4.qml 一.介绍 在 Qt 中&#xff0c;QML 与 C 交互是非常重要的&#xff0c;因为它允许开发人员充分利用 QML 和 C 各自的优势&…

JVM运行时内存:程序计数器

文章目录 1. 程序计数器的作用2. 程序计数器的基本特征3. 程序计数器的问题 运行时内存整体结构如下图所示: 1. 程序计数器的作用 为了保证程序(在操作系统中理解为进程)能够连续地执行下去&#xff0c;CPU必须具有某些手段来确定下一条指令的地址。而程序计数器正是起到这种作…

【idea】idea插件 通过文档注释生成swagger,easyexcel注解

前言&#xff1a;博主的第二个idea插件审核通过啦 (第一个插件是equals inspection) idea的plugin marketplace搜swagger excel doc即可下载安装 ​功能&#xff1a;通过文档注释生成easy Excel或swagger注解 &#xff08;快捷键altins&#xff09; ​实际场景&#xff1a;vo返…

实锤,阿里云盾会拦截百度云防护的IP!

今天凌晨&#xff0c;一位站长联系上云加速客服&#xff0c;反馈说&#xff0c;网站突然出现了502的情况。 在检查云防护子域名配置没有问题、本地强制回源没有问题的情况下&#xff0c;我们得出结论是要么服务器内防火墙拦截了云防护的IP段&#xff0c;要么服务器商拦截了云防…

Android 10.0 Launcher3定制folder文件夹2x2布局之三foldericon的2x2的布局后拖拽只能停放在右边的问题修复

1.前言 在10.0的系统rom产品定制化开发中,在对Launcher3的folder文件夹功能定制中,要求folder文件夹跨行显示,就是 2x2布局显示,默认的都是占1格的,现在要求占4格显示,系统默认是不支持显示4格的,所以接下来需要分析相关的 功能,然后来实现这个功能 2.Launcher3定制fo…

点量云流分享:关于实时渲染云推流技术

提到云串流或者云推流很多人可能和游戏关联起来&#xff0c;其实这个技术的应用领域不仅仅是游戏&#xff0c;还有云上旅游、考古、智慧园区、智慧城市、虚拟仿真等等行业。其解决的问题是将一些大型的3D应用程序放在云端&#xff0c;程序在运行的时候也是在云端&#xff0c;这…

Python学习之路 | Python基础语法(一)

数据类型 Python3 中常见的数据类型有&#xff1a; Number&#xff08;数字&#xff09;String&#xff08;字符串&#xff09;bool&#xff08;布尔类型&#xff09;List&#xff08;列表&#xff09;Tuple&#xff08;元组&#xff09;Set&#xff08;集合&#xff09;Dict…