C++——位图与布隆过滤器

目录

一,位图

1.1 关于大量数据的问题

1.2 位图概念

1.3 位图模拟实现

 1.4 位图的应用

 1.5 位图优缺点

二,布隆过滤器

2.1 一些场景

2.2 布隆过滤器概念

2.3 布隆过滤器模拟实现和测试

2.4 布隆过滤器查找

2.5 布隆过滤器删除

2.6 布隆过滤器优缺点


一,位图

1.1 关于大量数据的问题

先看一个面试题:

给40亿个不重复的未排序的无符号整数,然后如何快速判断一个数是否在这40亿个数中

 以我们前面的知识,最先想到的有两种方法:1,排序+二分查找  2,存到哈希表和红黑树中再查

但是题目中给的是40亿个整数,我们先算下需要的内存:1G就是1024MB,是1024*1024KB,是1024*1024*1024Byte,相当于10亿Byte,所以,40亿个数需要4*40Byte大概就是16G的空间,所以由于空间的限制,上面两种方法就都可以去掉了。

所以我们需要一种更好的方法去存储这40亿个整数

1.2 位图概念

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

比如上面的题目,判断数据在不在,那么可以使用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位位1,代表存在,为0表示不存在,如下图:

1.3 位图模拟实现

namespace bit
{
	template<size_t N>
	class bitset
	{
	public:
		bitset()
		{
			_bits.resize(N / 8 + 1, 0);//8+1,多开一个
			//这样开空间的 {(0       7)(8       15)(16       23)(24       31)...}
		}                    //  char      char         char
		//插入
		void set(size_t x)
		{
			size_t i = x / 8; //一个数组里面有很多个char,每个char有8个比特位,先算x需要被映射到哪个8比特位上
			size_t j = x % 8; //再算在该8比特位里面的位置,整个过程可以理解为看电影时,先找在哪一排,再找具体的位置
			_bits[i] |= (1 << j);
			//假如x是10,10/8=1,找到第一个char,再模8就是2,然后将1往左移两格,向高位移,比如00000001,往左移就是00000010
			//这样的话,我们就用非常小的空间代价把大量数据存起来了
		}
		//删除
		void erset(size_t x)
		{
			size_t i = x / 8;
			size_t j = x % 8;
			_bits[i] &= ~(1 << j);//按位取反,1与0为0完成删除,0与0还是0,不变
		}
		//判断在不在
		bool test(size_t x)
		{
			size_t i = x / 8;
			size_t j = x % 8;
			return _bits[i] & (1 << j); //如果存在,那1与1就是1,返回1;如果不在,0与1就是0,返回0
			//return( _bit[i] >> j) & 1;
		}
	private:
		vector<char> _bits;
	};

	void test_bit_set1()
	{
		bitset<100> bs1;
		bs1.set(10);
		bs1.set(11);
		bs1.set(15);

		cout << bs1.test(10) << endl;
		cout << bs1.test(11) << endl;
		cout << bs1.test(15) << endl;
		bs1.erset(10);
		bs1.erset(11);
		bs1.erset(15);

		cout << bs1.test(10) << endl;
		cout << bs1.test(11) << endl;
		cout << bs1.test(15) << endl;
	}

	void test_bit_set2()
	{
		bitset<-1> bs1; //给的是整形的最大值
		bitset<0xFFFFFFFF> bs2;
	}
}

 1.4 位图的应用

先看下下面几个题目:

①给100亿个整数,设计算法找到只出现一次的整数

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

③一个文件有100亿个整数,给1G内存,请设计算法找到出现次数不超过2的所有整数

上面的题目都有个共同点,就是和1,2和大于2的数字有关,换成位就是01,10和11,所以我们可以用一个封装了两个位图的类来解决,对于问题一,在类插入时,如果是00则表示第一次出现,01表示出现过一次,10出现两次,10后的不考虑;后面的两个问题也是同样的解决逻辑

namespace bit
{	
    template<size_t N>
	class twobitset
	{
	public:
		void set(size_t x)
		{
			bool inset1 = _bs1.test(x);
			bool inset2 = _bs2.test(x);

			//00
			if (inset1 == false && inset2 == false) //还未出现
			{
				// -> 01
				_bs2.set(x);
			}
			else if (inset1 == false && inset2 == true) //第一次出现
			{
				// -> 10
				_bs1.set(x);
				_bs2.erset(x);
			}
			else if (inset1 == true && inset2 == false) //第二及以上次出现
			{
				// -> 11
				_bs1.set(x);
				_bs2.set(x);
			}
		}

		void print_onse_num()
		{
			for (size_t i = 0; i < N; ++i)
			{
				if (_bs1.test(i) == false && _bs2.test(i) == true)
				{
					cout << i << endl;
				}
			}
		}

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

	void test_bit_set3()
	{
		int a[] = { 3, 4, 5, 2, 3, 4, 4, 4, 4, 12, 77, 65, 44, 4, 44, 99, 33, 33, 33, 6, 5, 34, 12 };
		twobitset<100> bs;
		for (auto e : a)
		{
			bs.set(e);
		}

		bs.print_onse_num();
	}

 1.5 位图优缺点

优点:位图的存储和查找效率非常快,而且可以节省很多空间

缺点:可以看出,位图只能存储可以转化成位的整数,如果是其他类型的数就不能使用位图,比如浮点数,string等自定义类型

所以为了使位图的设计能够支持其他类型的数据,就有了下面要介绍的布隆过滤器

二,布隆过滤器

2.1 一些场景

直接看图:

 又比如我们生活中的场景:

我们在注册账号时:

2.2 布隆过滤器概念

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

2.3 布隆过滤器模拟实现和测试

struct HashBKDR
{
	//BKDR字符串转int
	size_t operator()(const string& key)
	{
		size_t val = 0;
		for (auto ch : key)
		{
			val *= 131;
			val += ch;
		}

		return val;
	}
};
struct HashAP
{
	// AP
	size_t operator()(const string& key)
	{
		size_t hash = 0;
		for (size_t i = 0; i < key.size(); i++)
		{
			if ((i & 1) == 0)
			{
				hash ^= ((hash << 7) ^ key[i] ^ (hash >> 3));
			}
			else
			{
				hash ^= (~((hash << 11) ^ key[i] ^ (hash >> 5)));
			}
		}
		return hash;
	}
};
struct HashDJB
{
	// DJB
	size_t operator()(const string& key)
	{
		size_t hash = 5381;
		for (auto ch : key)
		{
			hash += (hash << 5) + ch;
		}

		return hash;
	}
};

//降低冲突,一个值映射一个位置容易误判,映射多个位置可以降低误判
// N表示准备要映射N个值
//哈希函数个数,代表一个值映射几个位,哈希函数越多,误判率越低,但是需要的空间消耗越多
template<size_t N,
	class K = string, class Hash1 = HashBKDR, class Hash2 = HashAP, class Hash3 = HashDJB> 
class BloomFilter
{
public:
	void Set(const K& key)
	{
		size_t hash1 = Hash1()(key) % (_ratio * N);
		_bits->set(hash1);

		size_t hash2 = Hash2()(key) % (_ratio * N);
		_bits->set(hash2);

		size_t hash3 = Hash3()(key) % (_ratio * N);
		_bits->set(hash3);
	}

	bool Test(const K& key)
	{
		size_t hash1 = Hash1()(key) % (_ratio * N);
		if (!_bits->test(hash1))
			return false; // 准确的

		size_t hash2 = Hash2()(key) % (_ratio * N);
		if (!_bits->test(hash2))
			return false; // 准确的

		size_t hash3 = Hash3()(key) % (_ratio * N);
		if (!_bits->test(hash3))
			return false;  // 准确的

		return true; // 可能存在误判
	}

private:
	const static size_t _ratio = 5;
	bit::bitset<_ratio* N>* _bits = new bit::bitset<_ratio * N>;
	//由于std库中这个东东是用静态数组实现的,一旦数据过大就会栈溢出,所以使用new的方式将数据转移到堆上,就可以解决了
};

测试一

void TestBloomFilter1()
{
	BloomFilter<10> bf;
	string arr1[] = { "苹果", "西瓜", "阿里", "美团", "苹果", "字节", "西瓜", "苹果", "香蕉", "苹果", "腾讯" };

	for (auto& str : arr1)
	{
		bf.Set(str);
	}

	for (auto& str : arr1)
	{
		cout << bf.Test(str) << “ ”;
	}
	cout << endl << endl;

	string arr2[] = { "苹果111", "西瓜", "阿里2222", "美团", "苹果dadcaddxadx", "字节", "西瓜sSSSX", "苹果 ", "香蕉", "苹果$", "腾讯" };

	for (auto& str : arr2)
	{
		cout << str << ":" << bf.Test(str) << endl;
	}
}

 测试二

void TestBloomFilter2()
{
	srand(time(0));
	const size_t N = 100000;
	BloomFilter<N> bf;
	cout << sizeof(bf) << endl;//因为报栈溢出,查看bf的大小

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

	for (size_t i = 0; i < N; ++i)
	{
		v1.push_back(url + std::to_string(1234 + i)); //让每个字符不一样
	}

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

	// 相似
	std::vector<std::string> v2;
	for (size_t i = 0; i < N; ++i)
	{
		std::string url = "http://www.cnblogs.com/-clq/archive/2021/05/31/2528153.html";
		url += std::to_string(rand() + i);
		v2.push_back(url);
	}

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

	std::vector<std::string> v3;
	for (size_t i = 0; i < N; ++i)
	{
		string url = "zhihu.com";
		url += std::to_string(rand() + i);
		v3.push_back(url);
	}

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

2.4 布隆过滤器查找

布隆过滤器不是为了解决冲突,而是为了降低冲突!

将一个元素用多个哈希函数映射到一个位图中,因此被映射到位置的比特位一定为1,所以在查找时,分别计算每个哈希值对应的比特位置存储的是否为0,只要有一个为0,表面该元素一定不在哈希表中,如果都不为0,那么可能存在,因此存在误判

2.5 布隆过滤器删除

布隆过滤器不能直接支持删除,因为在删除一个元素时会影响其他元素

2.6 布隆过滤器优缺点

优点:

①增加和查询的时间复杂度为O(K),K为哈希函数个数,一般都是一位数,因此和数据量大小无关

②哈希函数相互之间没有关系,方便硬件运算

③布隆过滤器不存储元素本身,所以在某些保密要求较高的场合有很大优势

④在能承受一定误判时,布隆过滤器比其他数据结构有很大的空间与时间优势

⑤数据量很大时,布隆过滤器可以表示全集,其他数据结构不能

缺点:

①有误判,即存在假阳性,不能准确判断元素是否在集合中,但也有补救方法

②不能获取元素本身

③大多数情况不能删除元素

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

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

相关文章

Arduino 串口绘图仪简单使用

1、工具所在位置 串口绘图仪实际上是从预设的串口获取值并将其绘制在xy轴图每获取到一组数据向左滑动一个单位&#xff0c;读取数据的速度起快&#xff0c;滑动就越快。 Y轴代表来自串口的值&#xff0c;可以是单个也可以是一组 。在读取串口数据时遇到"\n"&#xf…

Redis缓存过期淘汰策略详讲

前言 查看redis最大占用内存 1&#xff09;命令查看 config get memory2&#xff09;进入redis配置文件&#xff0c;查看maxmemory vim /myredis/redis.conf3&#xff09;redis默认内存多少可用 如果不设置最大内存大小或者设置最大内存大小为0&#xff0c;在64位操作系统…

【开源】SpringBoot框架开发高校学生管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 学生管理模块2.2 学院课程模块2.3 学生选课模块2.4 成绩管理模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 学生表3.2.2 学院课程表3.2.3 学生选课表3.2.4 学生成绩表 四、系统展示五、核心代码5.1 查询课程5.2 新…

牛客——牛可乐的翻转游戏(状压,dfs)

链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 来源&#xff1a;牛客网 题目描述 牛可乐发明了一种新型的翻转游戏&#xff01; 在一个有 nnn 行 mmm 列的棋盘上&#xff0c;每个格子摆放有一枚棋子&#xff0c;每一枚棋子的颜色要么是黑色&#xff0c;要么是白色。…

CentOS 8 安装配置 Hadoop3.3.6 伪分布式安装方式(适用于开发和调试)

1.配置服务器ssh免密登录&#xff0c;否则后面启动会报错&#xff1a;尝试通过SSH连接到主机出现认证错误的提示 配置服务器ssh免密登录&#xff1a; 1.生成SSH密钥对&#xff08;如果尚未生成&#xff09;&#xff1a; 执行下面的命令生成密钥对&#xff0c;一直回车即可 ssh…

Intellij Idea的数据库工具 DataGrip

DataGrip DataGrip&#xff1a; IDEA自带&#xff0c;非常好用。智能提示很强大&#xff0c;快捷键跟IDEA自身一致。 如果下载不了 DataGrip&#xff0c;也可以直接用 IDEA 自带的。 常用的快捷键 alt8&#xff1a; 打开数据库Service ctrlshiftF10&#xff1a;打开常用的数…

记一次CPU有规律飙高的线上问题排查过程

一、背景 最近在计费系统模块和灰度发布相关的功能已经基本交付,在这个间隙中,领导说有个线上问题需要排查下, 问题的场景比较有意思,排查过程中也有一些成长,这里记录一下。 二、排查过程 2.1 查看pinpoint 监控 首先根据领导的反馈看pinpoint中的JVM的CPU日志: CP…

基于Vue2用keydown、setTimeout事件实现连续按键(连击)任意键(或组合键)3秒触发自定义事件(以F1键为例)

核心代码 <template></template> <script> export default {created() {//监听弹起快捷键addEventListener("keyup", this.keyup);},destroyed(d) {//移除监听弹起快捷键removeEventListener("keyup", this.keyup);},methods: {keyup(…

ES节点故障的容错方案

ES节点故障的容错方案 1. es启动加载逻辑1.1 segment和translg组成和分析1.2 es节点启动流程1.3 es集群的初始化和启动过程 2. master高可用2.1 选主逻辑2.1.1 过滤选主的节点列表2.1.2 Bully算法2.1.2 类Raft协议2.1.3 元数据合并 2.2 HA切换 3. 分片高可用3.1 集群分片汇报3.…

2.0 Zookeeper 安装配置

Linux 安装 zookeeper 下载地址为: Apache ZooKeeper。 选择一稳定版本&#xff0c;本教程使用的 release 版本为3.4.14&#xff0c;下载并安装。 打开网址 https://www.apache.org/dyn/closer.lua/zookeeper/zookeeper-3.4.14/zookeeper-3.4.14.tar.gz&#xff0c;看到如下界…

HTTP1.1、HTTP2、HTTP3

HTTP1.1 HTTP/1.1 相比 HTTP/1.0 性能上的改进&#xff1a; 使用长连接的方式改善了 HTTP/1.0 短连接造成的性能开销。支持管道&#xff08;pipeline&#xff09;网络传输&#xff0c;只要第一个请求发出去了&#xff0c;不必等其回来&#xff0c;就可以发第二个请求出去&…

函数的连续与间断【高数笔记】

【连续】 分类&#xff0c;分几个&#xff1f;每类特点&#xff1f; 连续条件&#xff0c;是同时满足还是只需其一&#xff1f; 【间断】 分类&#xff0c;分几个大类&#xff0c;又分几个小类&#xff1f;每类特点&#xff1f; 间断条件&#xff0c;是同时满足还是只需其一&am…

Msql-数据库死锁

实验案例 CREATE TABLE t1_deadlock ( id int(11) NOT NULL, name varchar(100) DEFAULT NULL, age int(11) NOT NULL, address varchar(255) DEFAULT NULL, PRIMARY KEY (id), KEY idx_age (age) USING BTREE, KEY idx_name (name) USING BTREE ) ENGINEInnoDB DEFAULT CHARS…

Unity类银河恶魔城学习记录1-14 AttackDirection源代码 P41

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili PlayerPrimaryAttackState.cs using System.Collections; using System.Co…

计算机毕业设计 | SSM 医药信息管理系统(附源码)

1&#xff0c; 概述 1.1 课题背景 本系统由说书客面向广大民营药店、县区级医院、个体诊所等群体的药品和客户等信息的管理需求&#xff0c;采用SpringSpringMVCMybatisEasyui架构实现&#xff0c;为单体药店、批发企业、零售连锁企业&#xff0c;提供有针对性的信息数据管理…

OPC UA 信息模型云库简介

OPC基金会宣布推出与清洁能源和智能制造创新研究所&#xff08;CESMII&#xff09;共同开发的全球可用的UA云库。凭借其多云架构&#xff0c;UA 云库见证了所有主要云供应商利用开放接口的贡献&#xff0c;并可用于共享、查找和协作 OPC UA 信息模型。如今&#xff0c;UA云库已…

vue2.0+使用md-edit编辑器

前言&#xff1a;小刘开发过程中&#xff0c;如果是博客项目一般是会用到富文本。众多富文本中&#xff0c;小刘选择了markdown&#xff0c;并记录分享了下来。 # 使用 npm npm i kangc/v-md-editor -Smain.js基本配置import VueMarkdownEditor from kangc/v-md-editor; import…

【AI绘画+Midjourney平替】Fooocus:图像生成、修改软件(Controlnet原作者重新设计的UI+Windows一键部署)

代码&#xff1a;https://github.com/lllyasviel/Fooocus windows一键启动包下载&#xff1a;https://github.com/lllyasviel/Fooocus/releases/download/release/Fooocus_win64_2-1-831.7z B站视频教程&#xff1a;AI绘画入门神器&#xff1a;Fooocus | 简化SD流程&#xff0c…

国内唯一!通义灵码入选全球智能编码助手使用率 TOP 榜单

近日&#xff0c;在国内知名科技媒体 InfoQ 研究中心发布的《中国软件技术发展洞察和趋势预测报告 2024》中提到&#xff0c;随着 AI 和大模型技术的普及&#xff0c;开发者智能编码助手的使用习惯已经养成&#xff0c;其中&#xff0c;开发者使用的智能编码助手产品使用率超过…

【网络安全】URL解析器混淆攻击实现ChatGPT账户接管、Glassdoor服务器XSS

文章目录 通配符URL解析器混淆攻击实现ChatGPT账户接管通配符URL解析器混淆攻击实现Glassdoor服务器缓存XSS 本文不承担任何由于传播、利用本文所发布内容而造成的任何后果及法律责任。 本文将基于ChatGPT及Glassdoor两个实例阐发URL解析器混淆攻击。 开始本文前&#xff0c;…