位图|布隆过滤器模拟实现|STL源码剖析系列|手撕STL

news2024/11/16 15:59:22

 今天博主给大家带来位图和布隆过滤器的模拟实现。


前言

那么这里博主先安利一下一些干货满满的专栏啦!

手撕数据结构https://blog.csdn.net/yu_cblog/category_11490888.html?spm=1001.2014.3001.5482这里包含了博主很多的数据结构学习上的总结,每一篇都是超级用心编写的,有兴趣的伙伴们都支持一下吧!
算法专栏https://blog.csdn.net/yu_cblog/category_11464817.html这里是STL源码剖析专栏,这个专栏将会持续更新STL各种容器的模拟实现。

STL源码剖析https://blog.csdn.net/yu_cblog/category_11983210.html?spm=1001.2014.3001.5482


位图和布隆过滤器

位图

位图(Bitmap)通常指的是使用位(bit)作为最小单位存储和处理数据的数据结构或技术。位图可以用来表示一组二进制标志或位状态,并且可以有效地压缩存储大量布尔信息。

位图最常见的用途之一是表示集合或标志的状态。例如,可以使用位图来表示一个包含多个元素的集合,其中每个元素对应位图中的一个位。如果位的值为1,则表示该元素在集合中;如果位的值为0,则表示该元素不在集合中。

在C语言中,可以使用无符号整数类型(如unsigned intunsigned long)或数组来实现位图。

布隆过滤器

布隆过滤器(Bloom Filter)是一种用于高效判断一个元素是否属于一个集合的概率型数据结构。它基于位图(Bitmap)的概念,但使用了多个哈希函数来实现更高的查找效率。

布隆过滤器由一个位数组和多个哈希函数组成。初始时,所有位数组的值都被设置为0。当要向布隆过滤器中插入一个元素时,该元素经过多个哈希函数的计算,得到多个哈希值。然后将对应的位数组位置设置为1。当需要判断一个元素是否在集合中时,同样经过多个哈希函数的计算,检查对应的位数组位置是否都为1。如果有任何一位为0,则可以确定该元素不在集合中;如果所有位都为1,则表示该元素可能在集合中(存在误判的概率)。

布隆过滤器的主要优势是其高效的插入和查询操作。它的时间复杂度是O(k),其中k是哈希函数的数量,通常是一个较小的常数。布隆过滤器的空间复杂度也相对较低,只受到位数组的大小和哈希函数数量的影响。

然而,布隆过滤器也有一些限制。首先,存在一定的误判率,即在判断元素是否在集合中时,有一定的概率会出现错误的判断。其次,无法删除已插入的元素,因为删除操作会影响其他元素的判断结果。因此,布隆过滤器适用于对查询速度要求较高、可以容忍一定误判率的场景,如缓存、防止重复操作等。

需要根据具体的应用场景和数据特点来选择使用布隆过滤器,并在设计时注意误判率的控制和容量估算,以达到最佳效果。

BitSet.h

#pragma once

#include<vector>
#include<iostream>
using namespace std;

//位图特点
//1.快、节省空间
//2.相对局限,只能映射处理整型



//用char -- 一个char位置存8位
//怎么找位置,比如20
//20/8=2表示放在第几个char上
//20%8=4表示放在这个char的第几个位置
namespace yfc
{
	template<size_t N>
	class bit_set
	{
	public:
		bit_set()
		{
			_bits.resize(N / 8 + 1, 0);//+1可以保证空间一定够
		}
		void set(size_t x)
		{
			//把x的位置设置成1
			size_t i = x / 8;
			size_t j = x % 8;
			//怎么把_bit[i]的第j位弄成1呢
			//用一个或运算!
			_bits[i] |= (1 << j);
		}
		void reset(size_t x)
		{
			//把x的位置设置成0
			size_t i = x / 8;
			size_t j = x % 8;
			_bits[i] &= ~(1 << j);
		}
		bool test(size_t x)
		{
			//看这一位是0还是1
			size_t i = x / 8;
			size_t j = x % 8;
			return _bits[i] & (1 << j);
		}
	private:
		vector<char> _bits;
	};
	void test_bit_set1()
	{
		bit_set<100>bs1;
		bs1.set(8);
		bs1.set(9);
		bs1.set(20);

		cout << bs1.test(8) << endl;
		cout << bs1.test(9) << endl;
		cout << bs1.test(20) << endl;

		bs1.reset(8);
		bs1.reset(9);
		bs1.reset(20);

		cout << bs1.test(8) << endl;
		cout << bs1.test(9) << endl;
		cout << bs1.test(20) << endl;
	}
	void test_bit_set2()
	{
		//这三种写法都可以
		bit_set<-1>bs1;//-1的写法是最好的 -- -1对应的size_t就是全1
#if 0
		bit_set<0xffffffff>bs2;
		bit_set < 1024 * 1024 * 1024 * 4 - 1> bs3;
#endif
		//我们打开看任务管理器 -- 是可以看到是512MB左右的
	}




	//面试题2
	//我们用两个位图就行 -- 两个位图对应位置一起表示状态
	template<size_t N>
	class twobitset
	{
	private:
		bit_set<N>_bs1;
		bit_set<N>_bs2;
	public:
		void set(size_t x)
		{
			//要先判断一下
			bool inSet1 = _bs1.test(x);
			bool inSet2 = _bs2.test(x);
			if (inSet1 == false && inSet2 == false)
			{
				//00->01
				_bs2.set(x);
			}
			else if (inSet1 == false && inSet2 == true)
			{
				//01->10
				_bs1.set(x);
				_bs2.reset(x);
			}
		}
		void print_once_num()
		{
			for (size_t i = 0; i < N; i++)
			{
				if (_bs1.test(i) == false && _bs2.test(i) == true)
				{
					cout << i << " ";
				}
			}
			cout << endl;
		}
	};
	void test_bit_set3()
	{
		int a[] = { 1,2,3,4,5,6,7,8,9,10,12,10,9,8,6,5,3,2,1 };
		twobitset<100>bs;
		for (auto e : a)
		{
			bs.set(e);
		}
		bs.print_once_num();
	}
	//面试题3
	//也是用两个位图
	//第一个是文件1的映射
	//第二个是文件2的映射
	//映射位都是1的值就是交集

	//面试题4
	//其实和2是一样的,00/01/10/11就行
}

BloomFilter.h

#pragma once

//布隆过滤器

#include"BitSet.h"
#include<algorithm>
#include<string>
using namespace std;



namespace yfc
{	
	template<class K = string>
	struct HashBKDR
	{
		size_t operator()(const K& key)
		{
			size_t val = 0;
			for (auto ch : key)
			{
				val *= 131;
				val += ch;
			}
			return val;
		}
	};
	template<class K = string>
	struct HashAP
	{
		size_t operator()(const K& 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;
		}
	};
	template<class K = string>
	struct HashDJB
	{
		size_t operator()(const K& key)
		{
			size_t hash = 5381;
			for (auto ch : key)
			{
				hash += (hash << 5) + ch;
			}
			return hash;
		}
	};

	template<size_t N, class K = string,
		class Hash1 = HashBKDR<string>,
		class Hash2 = HashAP<string>,
		class Hash3 = HashDJB<string>>
	class BloomFilter
	{
	private:
		const static size_t _ratio = 5;
		bit_set<_ratio* N> _bits;
		//如果使用std::bitset
		//考虑放到堆上new一个
		//因为std::bit有个隐藏的bug会把栈撑爆
	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;//可能存在误判 -- 上面的不在是准确的
		}
	};
	void testBloomFilter1()
	{
		BloomFilter<10>bf;
		string arr[] = { "苹果","西瓜","阿里","美团","苹果","字节","西瓜","苹果","香蕉","苹果","腾讯" };
		for (auto& str : arr)
		{
			bf.set(str);
		}
		for (auto& str : arr)
		{
			cout << bf.test(str) << endl;
		}
	}
	//测误判率的性能测试
	void TestBloomFilter2()
	{
		srand(time(0));
		const size_t N = 100000;
		BloomFilter<N> bf;
		cout << sizeof(bf) << endl;

		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;
	}
}

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

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

相关文章

并发编程 - 利用Event Bus模式实现目录文件变化捕捉

文章目录 Pre需求CodeDirectoryTargetMonitorFileChangeEventFileChangeListener测试 Pre 并发编程 - Event Bus 设计模式 需求 JDK自1.7版本后提供了WatchService类&#xff0c;该类可以基于事件通知的方式监控文件或者目录的任何变化&#xff0c;文件的改变相当于每一个事件…

Java继承和多态

文章目录 继承继承概念继承的语法 super关键字super和this继承方式多态多态的概念多态实现条件 重写重写和重载的区别 继承 Java中使用类对现实世界中实体来进行描述&#xff0c;类经过实例化之后的产物对象&#xff0c;则可以用来表示现实中的实体&#xff0c;事物之间可能会…

Linux 内核级通用内存池 —— kmalloc 体系

目录 kmalloc 内存池中都有哪些尺寸的内存块 kmalloc 内存池如何选取合适尺寸的内存块 kmalloc 内存池的整体架构 KMALLOC_RECLAIM 表示需要分配可以被回收的内存&#xff0c;RECLAIM 类型的内存页&#xff0c;不能移动&#xff0c;但是可以直接回收&#xff0c;比如文件缓存页…

【Linux】第一个Linux小程序——进度条

今天为大家带来一篇关于在Linux上编写的进度条小程序的博客。 正文 我们在日常生活中使用电子产品时&#xff0c;经常会遇到加载的过程&#xff0c;这时候这些加载界面总是会附带有一些进度条&#xff0c;这些进度条是加载进度的可视化图形&#xff0c;这篇文章我们就在Linux系…

基于云原生网关的全链路灰度实践

作者&#xff1a; 倪海峰&#xff08;海迩&#xff09; 前言 随着企业规模的不断扩大&#xff0c;传统单体应用已很难进一步支持业务的发展&#xff0c;业务的迭代速度已经难以满足业务的增长&#xff0c;此时企业会对应用系统做微服务化的改造&#xff0c;降低业务的耦合度&…

keepalived脑裂

keepalived脑裂及解决方法&#xff1f; 一.keepalived的脑裂是如何产生的&#xff1f;二、HAProxy1.HAProxy概念2.HAProxy主要特性3.HAProxy负载均衡策略 4.LVS nginx HAProxy的区别5.编译部署HAProxy 一.keepalived的脑裂是如何产生的&#xff1f; 脑裂&#xff1a;指在一个高…

Elasticsearch【优化、案例】(八)-全面详解(学习总结---从入门到深化)

目录 Elasticsearch集群_测试集群状态 Elasticsearch集群_故障应对&水平扩容 Elasticsearch优化_磁盘选择 Elasticsearch优化_分片策略 Elasticsearch优化_内存设置 Elasticsearch案例_需求说明 Elasticsearch案例_ES自动补全 Elasticsearch案例_创建索引 Elastic…

多元分类预测 | Matlab 鲸鱼算法(WOA)优化xgboost的分类预测模型,多特征输入模型,WOA-xgboost分类预测

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 多元分类预测 | Matlab 鲸鱼算法(WOA)优化xgboost的分类预测模型,多特征输入模型,WOA-xgboost分类预测 多特征输入单输出的二分类及多分类模型。程序内注释详细,直接替换数据就可以用。程序语言为matlab,程序可…

MySQL查询作业

一、单表查询练习 1、查询出部门编号为30的所有员工 2、所有销售员的姓名、编号和部门编号。 3、找出奖金高于工资的员工。 4、找出奖金高于工资60%的员工。 5、找出部门编号为10中所有经理&#xff0c;和部门编号为20中所有销售员的详细资料。 6、找出部门编号为10中…

【三】部署zabbix-proxy代理服务器和高可用,以及监控windows系统和java应用

zabbix代理服务器和高可用 1.部署zabbix代理服务器1.1 代理端zabbix-proxy配置1.2 客户端zabbix-agent配置1.3 zabbix-proxy总结 2. 部署Zabbix高可用集群2.1 主节点zabbix-server配置2.2 备节点zabbix-server配置2.3 客户端zabbix_agent配置2.4 Zabbix高可用集群总结 3.Zabbix…

JavaScript异步编程:(回调函数、Promise、async/await、Generator)

文章目录 前言1. 回调函数1.1. 回调函数的基本概念和使用方法1.2. 回调函数的优缺点和注意事项1.3. 回调地狱和如何避免 2. Promise2.1. Promise 的基本概念和使用方法2.2. Promise 的状态和状态转换2.3. Promise 的链式调用和错误处理2.4. Promise.all 和 Promise.race 的使用…

MySQL数据库中对表进行创建,插入数据并对数据进行选择

目录 1.根据此图进行建表并插入数据 2.对表进行以下操作 a:显示所有职工的基本信息 b:查询所有职工所属部门的部门号&#xff0c;不显示重复的部门号 c:求出所有职工的人数 d:列出最高工和最低工资 e:列出职工的平均工资和总工资 f:创建一个只有职工号、姓名和参加工作的…

123.HTML5+CSS3完结_使用Netlify收取表单

Netlify也可以做表单接受&#xff1a; 我们启动一下 修改下表单 ● 接着在我们的网站输入并提交表单 ● 之后会有一个提示&#xff0c;提示我们提交成功 然后就能在Netlify接受到用户的表单 ● 当然这个表单只能接受100个&#xff0c;但是作为实验也够用了 到此&a…

文字磨练课程:提高编辑和校对效率的方法

提高编辑和校对效率&#xff0c;可以使你更有效地完成写作任务&#xff0c;提升文章质量。以下是一些方法&#xff0c;可以帮助你在编辑和校对过程中提高效率。 1.设定目标和计划 在开始编辑和校对前&#xff0c;设定明确的目标和计划。这可以帮助你集中注意力&#xff0c;提…

【SQL应知应会】表分区(一)• MySQL版

欢迎来到爱书不爱输的程序猿的博客, 本博客致力于知识分享&#xff0c;与更多的人进行学习交流 本文收录于SQL应知应会专栏,本专栏主要用于记录对于数据库的一些学习&#xff0c;有基础也有进阶&#xff0c;有MySQL也有Oracle 分区表 • MySQL版 一、分区表1.非分区表2.分区表2…

整齐有序!统一命名文件,高效管理数据轻松实现!

在数字化时代&#xff0c;我们每天都与大量文件打交道&#xff0c;文件名杂乱无章、难以辨识的情况是司空见惯的。这不仅浪费我们宝贵的时间&#xff0c;还可能导致信息混乱和数据丢失。但是&#xff0c;抛开这一切困扰吧&#xff01;现在&#xff0c;我们向您介绍一个简单却强…

C++—string类

本期我们来学习C的string&#xff0c;本期内容相当的多&#xff0c;且有一定难度&#xff0c;需要大家静下心来看 目录 1.标准库中的string 1.1string类的介绍 1.2 string类的常用接口 构造函数、析构函数、赋值、拷贝构造 npos push_back append operator[ ] size …

什么是ASPICE认证

ASPICE&#xff1a; “AutomotiveSoftware ProcessImprovement and CapacityDetermination”&#xff0c;即汽车软件过程改进及能力评定。它是一个过程模型&#xff0c;由过程和能力度两个维度构成&#xff0c;用于评价汽车行业软件设计开发的能力水平。 ASPICE的6个级别&…

基于simulink进行场景变化检测(附源码)

一、前言 此示例演示如何及时分割视频。此示例中的算法可用于检测视频流中的重大变化&#xff0c;例如广告开始和结束的时间。场景变换在广告和营销中被广泛应用。通过改变场景&#xff0c;可以吸引消费者的注意力&#xff0c;传达产品或服务的特点和优势。例如&#xff0c;将…

包揽七项葵花奖 参编多项标准 萤石领跑智能家居+物联网云平台行业

7月9日&#xff0c;2023第七届“葵花奖”智能家居评选颁奖盛典在广交会展馆举行&#xff0c;萤石网络一举斩获7项重磅奖项。同时&#xff0c;萤石作为参编单位&#xff0c;受邀参与了《智能门锁测评标准》发布仪式及《智能开关测评标准》启动会&#xff0c;再次彰显了其在智能家…