【C++】:位图、布隆过滤器、哈希分割

news2024/11/27 17:56:35

朋友们、伙计们,我们又见面了,本期来给大家解读一下位图、布隆过滤器、哈希分割,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成!

C 语 言 专 栏:C语言:从入门到精通

数据结构专栏:数据结构

个  人  主  页 :stackY、

C + + 专 栏   :C++

Linux 专 栏  :Linux

​ 

目录

1. 位图

1.1 概念

1.2 简单实现

1.3 应用

2. 布隆过滤器

2.1 布隆过滤器的提出

2.2 布隆过滤器的概念

2.3 布隆过滤器的插入

2.4 布隆过滤器的查找

2.5 布隆过滤器的删除 

2.6 布隆过滤器的优缺点 

3. 哈希分割

3.1 哈希分割的应用

4. 布隆过滤器的应用


1. 位图

1.1 概念

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

位图参考文档

1.2 简单实现

【题目】:给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中?

解决该题有以下三种方法:

1. 暴力求解(O(N))

2. 排序(O(N*logN)) + 二分(logN)

3. 位图解决

数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在。

首先我们得找到key在位图中对应的位置,那么这个位置与key的映射关系是怎么样的呢?

首先我们得找到key在第几个整型中,所以需要用key/32获取在第几个整型中,用i来表示。然后还需要知道在整型中的第几个比特位,所以需要用key%32,用j来表示。然后根据对应比特位的值是0还是1来判断在不在。

如果我们要向位图中进行插入,那么需要找到key对应的比特位,然后将1左移j位,然后与该位置按位或,即可将对应的比特位改为1。

如果需要删除,那么就将1左移j位后的值按位取反,然后与对应的位置按位与,即可将对应的比特位改为0。

代码实现:

#pragma once
#include <vector>
namespace ywh
{
	//位图
	//N是需要多少比特位
	template<size_t N>
	class bit_set
	{
	public:
		bit_set()
		{
			_bits.resize(N / 32 + 1, 0);
		}
		//插入
		void set(size_t x)
		{
			size_t i = x / 32;    //第几个整型
			size_t j = x % 32;    //第几个比特位
			_bits[i] |= (1 << j); //对应的比特位改为1
		}
		//删除
		void reset(size_t x)
		{
			size_t i = x / 32;    //第几个整型
			size_t j = x % 32;    //第几个比特位
			_bits[i] &= (~(1 << j)); //对应的比特位改为0
		}
		//判断
		bool test(size_t x)
		{
			size_t i = x / 32;    //第几个整型
			size_t j = x % 32;    //第几个比特位
			return _bits[i] & (1 << j);
		}
	private:
		vector<int> _bits;
	};
}

1.3 应用

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

在位图中我们表示一个数存在,则对应的比特位是1,那么我们可以使用两个比特位表示一个数出现的次数:

       出现0次 -> 00

       出现1次 -> 01

       出现2次及以上 -> 10

那么在插入中就需要做一改进

在插入时对应的是00,那么就改为01,如果是01,就改为10。

代码实现:

namespace count_num
{
	template<size_t N>
	class Oncebitset
	{
	public:
		//插入
		void set(size_t x)
		{
			size_t i = x / 32;    //第几个整型
			size_t j = x % 32;    //第几个比特位
			if (_bs1[x] == false && _bs2[x] == false)   //00
			{
				_bs2.set(x);   //对应的比特位改为1      //01
			}
			else if (_bs1[x] == false && _bs2[x] == true) //01
			{
				_bs1.set(x);   //对应的比特位改为1
				_bs2.reset(x); //对应的比特位改为0     //10
			}
		}
		void PrintOnce()
		{
			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. 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?

将这两个文件分别映射到位图中,若一个值在这两个位图中都存在,那么便是交集。


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

同样的还是使用两个比特位来表示一个数出现的次数:

       出现0次 -> 00

       出现1次 -> 01

       出现2次 -> 10

       出现3次及以上 -> 11

2. 布隆过滤器

2.1 布隆过滤器的提出

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

2.2 布隆过滤器的概念

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

 从上图也不难看出布隆过滤器存在一定的误判,例如:x和w映射的其中一个位置冲突,y和w映射的其中一个位置也冲突。因此布隆过滤器判断一个值不存在是准确的,判断一个数存在是不准确的。

2.3 布隆过滤器的插入

布隆过滤器的目的就是为了在位图的基础上处理一些非整型的数据,那么根据三种对应的映射关系将对应位置的比特位改为1即可。

//三种对应的映射关系
struct BKDRHash
{
	size_t operator()(const string& key)
	{
		size_t hash = 0;
		for (auto e : key)
		{
			hash *= 31;
			hash += e;
		}
		return hash;
	}
};
struct APHash
{
	size_t operator()(const 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 string& key)
	{
		size_t hash = 5381;
		for (auto ch : key)
		{
			hash += (hash << 5) + ch;
		}
		return hash;
	}
};

//布隆过滤器
template<size_t N, class K = string,   
	class Hashfunc1 = BKDRHash(), 
	class Hashfunc2 = APHash(), 
	class Hashfunc3 = DJBHash()>
class Bloom_filter
{
public:
	//插入
	void Set(const K& key)
	{
		size_t hash1 = BKDRHash()(key) % N;
		size_t hash2 = APHash()(key) % N;
		size_t hash3 = DJBHash()(key) % N;

		_bits.set(hash1);
		_bits.set(hash2);
		_bits.set(hash3);
	}
private:
	bitset<N> _bits;
};

2.4 布隆过滤器的查找

查找这里就存在误判了,如果判断这个值不存在,那么对应的映射位置的比特位为0,如果判断一个值存在,那么对应的位置的比特位为1,但是这些对应位置是一对多的关系,所以就会出现误判。

//判断
	bool Test(const K& key)
	{
		size_t hash1 = BKDRHash()(key) % N;
		if (_bits.test(hash1) == false)
			return false;
		size_t hash2 = APHash()(key) % N;
		if (_bits.test(hash2) == false)
			return false;
		size_t hash3 = DJBHash()(key) % N;
		if (_bits.test(hash2) == false)
			return false;
		//存在误判
		return true;
	}

2.5 布隆过滤器的删除 

布隆过滤器其是不支持删除的,因为映射对应的比特位并不是一对一的关系,很有可能两个key对应位置其中一个或两个比特位是一样的,那么你要删除其中一个值,肯定要将这个值对应的比特位全部置为0,此时就会出现问题,另外一个值指向的比特位也被置为了0,也会被删除掉。但是可以通过引用计数的方式来实现一个删除的接口,记录映射在该比特位上key的个数,如果只有一个可以直接置为0,如果有多个不能置为0。

2.6 布隆过滤器的优缺点 

优点:

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

缺点:

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

3. 哈希分割

哈希分割也叫哈希切分,所谓的哈希分割就是通过哈希思想,将大量的数据划分为具有类似属性的小块数据,例如:将一整个大的文件分成具有不同属性的若干个小文件,然后对这些小文件进行操作。

3.1 哈希分割的应用

例:给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址?

首先可以肯定的是这个文件是不能加载到内存中的,那么就可以根据哈希切分将这100G的大文件划分为100个小文件,然后加载到内存中进行操作:

① 通过哈希算法将每一个IP地址转化为一个整数i,然后取模100,存放到对应的i号文件中。

② 通过哈希切分之后的这些若干个小文件就可以加载到内存中,通过容器map来进行统计次数,从而找到出现次数最多的IP地址。

4. 布隆过滤器的应用

布隆过滤器适合处理一些非整形的数据,那么在一些情况下,布隆过滤器的作用还是很明显的:

例:给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?


首先有100亿个query,假设一个query大小为50字节

1GB 约等于 10亿字节

100亿个query的大小就是5000亿字节 约等于 500GB

有两个文件,总共1000GB的大小。

那么内存肯定是存不下的,所以我们首先想到的就是哈希切分:

①  通过哈希算法分别将这两个文件中的每一个query转化为一个整数i,然后取模500,存放到对应的Ai号文件和Bi号文件中。

②  通过哈希切分之后的这些若干个小文件就可以加载到内存中,然后将Ai文件对应的Bi文件加载到内存,分别插入到一个setA和setB中,再找交集。

注意:可能存在切分之后的这些小文件大小还是不足以放到内存中,那么可以再对这个小文件换一种哈希算法再次分割

朋友们、伙计们,美好的时光总是短暂的,我们本期的的分享就到此结束,欲知后事如何,请听下回分解~,最后看完别忘了留下你们弥足珍贵的三连喔,感谢大家的支持!  

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

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

相关文章

Kingfisher — 快速灵活的公共数据库下载工具

Kingfisher — 快速灵活的公共数据库下载工具 Kingfisher 是一个快速灵活的程序&#xff0c;用于从公共数据库下载序列文件 (及其元数据注释)&#xff0c;包括 European Nucleotide Archive (ENA)&#xff0c; NCBI SRA&#xff0c;亚马逊 AWS 和谷歌云。它的输入是一个或多个 …

Fluke ADPT 连接器新增对福禄克万用 Fluke 17B Max 的支持

所需设备&#xff1a; 1、Fluke ADPT连接器&#xff1b; 2、Fluke 17B Max&#xff1b; Fluke 17B Max拆机图&#xff1a; 显示界面如下图&#xff1a; 并且可以将波形导出到EXCEL: 福禄克万用表需要自己动手改造&#xff01;&#xff01;&#xff01;

2024年【危险化学品经营单位安全管理人员】考试报名及危险化学品经营单位安全管理人员考试资料

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 危险化学品经营单位安全管理人员考试报名是安全生产模拟考试一点通总题库中生成的一套危险化学品经营单位安全管理人员考试资料&#xff0c;安全生产模拟考试一点通上危险化学品经营单位安全管理人员作业手机同步练习…

C#(C Sharp)学习笔记_循环语句【七】

什么是循环语&#xff1f; 循环语句是由循环体及循环的终止条件两部分组成的。 在不少实际问题中有许多具有规律性的重复操作&#xff0c;因此在程序中就需要重复执行某些语句。一组被重复执行的语句称之为循环体&#xff0c;能否继续重复&#xff0c;决定循环的终止条件。循环…

摸索设计模式的魅力:从策略模式看软件设计的智慧-灵活应对变化的艺术

设计模式专栏&#xff1a;http://t.csdnimg.cn/U54zu 目录 一、案例场景1.1 一坨坨代码实现1.2 存在的问题 二、使用策略模式解决问题2.1 使用策略模式重构代码2.2 克服了问题 三、模式讲解3.1 结构图及说明3.2 实现步骤和注意事项3.3 适用场景 四、优势和局限性4.1 优势4.2 局…

IP地址+子网掩码+CIDR学习笔记

目录 一、IP地址 1、表示方法&#xff1a; 2、特殊IP地址 二、子网掩码 1、判断网络位和主机位 2、子网划分 三、无分类编址CIDR 1、CIDR路由汇聚 汇聚规则&#xff1a; 汇聚ID&#xff1a; 2、最佳路由匹配原则 一、IP地址 1、表示方法&#xff1a; 机器中存放的…

【报告解析】OpenAI Sora视频模型官方报告全解析 | 效果,能力以及基本原理

省流版 1 核心数据处理将视频数据整合成一个一个的Patch&#xff0c;方便统一训练数据&#xff0c;利用扩散Transformer架构 2 功能效果除了可以实现基础的文生视频外&#xff0c;实际上还有非常惊艳的视频延展&#xff0c;视频编辑&#xff0c;视频连接等多种功能&#xff0…

防御保护--防病毒网关

网络安全之防病毒网关--恶意软件 按照传播方式分类 病毒 病毒 --- 一种基于硬件和操作系统的程序&#xff0c;具有感染和破坏能力&#xff0c;这与病毒程序的结构有关。病毒攻击的宿主程序是病毒的栖息地&#xff0c;它是病毒传播的目的地&#xff0c;又是一个感染的出…

Python算法题集_二叉树的层序遍历

Python算法题集_二叉树的层序遍历 题102&#xff1a;二叉树的层序遍历1. 示例说明2. 题目解析- 题意分解- 优化思路- 测量工具 3. 代码展开1) 标准求解【DFS递归】2) 改进版一【BFS迭代】3) 改进版二【BFS迭代循环】 4. 最优算法 本文为Python算法题集之一的代码示例 题102&am…

变分自编码器(VAE)PyTorch Lightning 实现

✅作者简介&#xff1a;人工智能专业本科在读&#xff0c;喜欢计算机与编程&#xff0c;写博客记录自己的学习历程。 &#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&…

Tcl 过程

一个Tcl过程就是Tcl脚本定义的一个命令。可以使用proc命令定义新的过程。Tcl还提供了处理变量作用域的特殊命令&#xff0c;这些命令允许使用引用而非值传递参数&#xff0c;并能把新的Tcl控制结构实现为过程。 一、proc与return 过程由proc命令创建, 其中参数{a b} 中的大括…

【Win10 触摸板】在插入鼠标时禁用触摸板,并在没有鼠标时自动启用触摸板。取消勾选连接鼠标时让触摸板保持打开状态,但拔掉鼠标后触摸板依旧不能使用

出现这种问题我的第一反应就是触摸板坏了&#xff0c;但是无意间我换了一个账户发现触摸板可以用&#xff0c;因此推断触摸板没有坏&#xff0c;是之前的账户问题&#xff0c;跟系统也没有关系&#xff0c;不需要重装系统。 解决办法&#xff1a;与鼠标虚拟设备有关 然后又从知…

【解决(几乎)任何机器学习问题】:超参数优化篇(超详细)

这篇文章相当长&#xff0c;您可以添加至收藏夹&#xff0c;以便在后续有空时候悠闲地阅读。 有了优秀的模型&#xff0c;就有了优化超参数以获得最佳得分模型的难题。那么&#xff0c;什么是超参数优化呢&#xff1f;假设您的机器学习项⽬有⼀个简单的流程。有⼀个数据集&…

Vulnhub靶机:DC5

一、介绍 运行环境&#xff1a;Virtualbox 攻击机&#xff1a;kali&#xff08;10.0.2.15&#xff09; 靶机&#xff1a;DC5&#xff08;10.0.2.58&#xff09; 目标&#xff1a;获取靶机root权限和flag 靶机下载地址&#xff1a;https://download.vulnhub.com/dc/DC-5.zi…

Rust 数据结构与算法:4栈:用栈实现进制转换

2、进展转换 将十进制数转换为二进制表示形式的最简单方法是“除二法”&#xff0c;可用栈来跟踪二进制结果。 除二法 下面实现一个将十进制数转换为二进制或十六进制的算法&#xff0c;代码如下&#xff1a; #[derive(Debug)] struct Stack<T> {size: usize, // 栈大…

SG7050EEN晶体振荡器SPXO规格书

频率范围:25mhz ~ 500mhz供电电压:2.5 V型。/ 3.3 V类型功能:输出使能(OE)外形尺寸:7.0 5.0 1.5 mm输出:LV-PECL低相位抖动:50fs型。(f0 156.25 MHz)工作温度:-40℃~ 105℃ 规范

二叉树相关OJ题

创作不易&#xff0c;感谢三连&#xff01;&#xff01; 一、选择题 1、某二叉树共有 399 个结点&#xff0c;其中有 199 个度为 2 的结点&#xff0c;则该二叉树中的叶子结点数为&#xff08; &#xff09; A.不存在这样的二叉树 B.200 C.198 D.199解析&#xff1a;选B&…

致创新者:聚焦目标,而非问题

传统的企业创新管理方式常常导致组织内部策略不协调、流程低效、创新失败率高等问题。而创新运营作为企业管理创新的新模式&#xff0c;通过整合文化、实践、人员和工具&#xff0c;提高组织创新能力。已经采用创新运营的公司报告了一系列积极的结果&#xff0c;如市场推出速度…

最长连续手牌 - 华为OD统一考试

OD统一考试&#xff08;C卷&#xff09; 分值&#xff1a; 200分 题解&#xff1a; Java / Python / C 题目描述 有这么一款单人卡牌游戏&#xff0c;牌面由颜色和数字组成&#xff0c;颜色为红、黄、蓝、绿中的一种&#xff0c;数字为 0−9 中的一个。游戏开始时玩家从手牌中…

力扣hot3--并查集+哈希

第一想法是排个序然后遍历一遍&#xff0c;but时间复杂度就超啦 并查集居然与哈希结合了&#xff08;&#xff09; 已经好久没用过并查集了&#xff0c;&#xff0c;&#xff0c;我们用哈希表f_node中来记录原结点的父节点&#xff0c;其中key是原结点&#xff0c;value是父节点…