C++哈希应用——位图布隆过滤器

news2024/11/26 0:54:54

布隆过滤器

C++布隆过滤器

文章目录

  • C++布隆过滤器
      • 概念
      • 实质用途
      • 控制误判率
      • 实现
        • 插入和查找
        • 布隆过滤器的删除
      • 布隆过滤器优点
      • 布隆过滤器缺陷
      • 相关大数据题目

用哈希表存储用户记录,缺点是需要消耗较大的内存;用位图存储用户记录,缺点是位图一般处理整形,内容是字符串或者自定义类型就很勉强。基于以上,若将哈希和位图结合,称为布隆过滤器,会不会把上面的问题都解决了呢?

概念

布隆过滤器是一种概率型数据结构。可以高效的插入和查询,然后告诉我们某个数据一定不在或者可能存在。它是用多个哈希函数,将一个数据映射到位图结构中。即可以提高查询效率,又可以节省内存空间。

若只用一个哈希函数来映射到位图上,那么可能会发生以下情况。字符串string先存在了,然后来了一个字符串str通过映射到位图上,由于"str"与"string"发生了冲突,那么位图反馈给"str"的结果是"str"已存在。

image-20230424185758754

若是用多个哈希函数进行映射,则会大大减少以上的情况。

当已经存在的数据通过两个哈希函数在位图上就有两个映射位置,新查询的的字符串"str"通过两个哈希函数映射,其中一个映射的位置与字符串"string"的一个映射位置发生冲突,但是字符串"str"还有一个映射位置是反馈不存在,那么字符串"str"就不存在。相比之前降低了误判率,但不能完全消除误判率。用的哈希函数越多,位图上要映射的位置就越多,相同的误判率就越低。

image-20230424190249938

布隆过滤器在这样的场景下就是通过映射多个位置,降低误判率

实质用途

当布隆过滤器判断一个数据存在可能是不准确的,因为这个数据通过多个哈希函数映射的位置可能都已经被1个或多个数据占用了,此时就需要进入数据库中查询。

当布隆过滤器判断一个数据不存在是准确的,因为数据映射的位置若被别的数据占用了,位图上的比特位会是1(没有被占用比特位上是0)

控制误判率

布隆过滤器过小,上面的所有的比特位被占用的比率(设置成1)就越大,此时布隆过滤器的误判率就越大,因此布隆过滤器的长度直接影响了误判率,布隆过滤器越大则误判率越小。

哈希函数的个数越多,单个数据需要映射到位图上的位置就需要越多,若此时布隆过滤器有过多的位置被设置成1,误判率就会很大,但哈希函数的个数太少,误判率也会很大。

那么如何选择布隆过滤器的长度和哈希函数的个数的权衡就直接控制了误判率

有大佬通过实验得出一下关系式
m = − n l n p / ( l n 2 ) 2 m=-nlnp/(ln2)^2 m=nlnp/(ln2)2

k = l n 2 m / 2 k=ln2m/2 k=ln2m/2

其中n为插入的元素个数,p为误判率,m为布隆过滤器长度,k为哈希函数个数

这里我们估算一下,如果使用3个哈希函数,(k=3),ln2近似取值0.7,那么m和n关系是m=4.2n(布隆过滤器的长度应为插入元素个数的4.2倍)

实现

因为插入布隆过滤器的元素有字符串,也有其他数据类型包括自定义类型,所以可以实现为一个模板类,只需要调用者提供把数据类型转化成整形的哈希函数即可。一般情况下布隆过滤器用来处理字符串类型,所以这里模板参数缺省值给string

布隆过滤器的成员一般是一个位图,所以还需要提供一个非类型模板参数N,给调用者指定位图的长度。

下面调用了三个综合评分最高的四个哈希算法(把字符串转化成整形)

struct BKDRHash
	{
		size_t operator()(const string& key)
		{
			size_t hash = 0;
			for (auto ch : key)
			{
				hash *= 131;
				hash += ch;
			}
			return hash;
		}
	};

	struct APHash
	{
		size_t operator()(const string& key)
		{
			unsigned int hash = 0;
			int i = 0;

			for (auto ch : key)
			{
				if ((i & 1) == 0)
				{
					hash ^= ((hash << 7) ^ (ch) ^ (hash >> 3));
				}
				else
				{
					hash ^= (~((hash << 11) ^ (ch) ^ (hash >> 5)));
				}

				++i;
			}

			return hash;
		}
	};

	struct DJBHash
	{
		size_t operator()(const string& key)
		{
			unsigned int hash = 5381;

			for (auto ch : key)
			{
				hash += (hash << 5) + ch;
			}

			return hash;
		}
	};

	struct JSHash
	{
		size_t operator()(const string& s)
		{
			size_t hash = 1315423911;
			for (auto ch : s)
			{
				hash ^= ((hash << 5) + ch + (hash >> 2));
			}
			return hash;
		}
	};

插入和查找

当元素插入到布隆过滤器时,需要把数据通过三个哈希函数计算映射到对应的位图上的位置设置成1(stl库中bitset中set的用法)

当用于检测某个数据是否在布隆过滤器中时,需要通过三个哈希函数计算得出数据映射在位图上的位置,然后判断这几个比特位:

若三个比特位全部被设置成1,就返回true表示数据存在(可能发生误判)

若只要有一个比特位没有被设置成1,立即返回false表示数据不存在(不存在是准确的)

template<size_t N,//最多存储的数据个数
	size_t X=6,//平均存储一个数据要开辟6个映射位
		class K=string,//数据类型的模板参数---缺省值给string
	    class HashFunc1=BKDRHash,
		class HashFunc2 = APHash,
		class HashFunc3 = DJBHash>
		//class HashFunc4 = JSHash>
	class BloomFilter
	{
	public:
		void Set(const K&key)
		{
			size_t hashi1 = HashFunc1()(key) %(N * X);
			
			size_t hashi2 = HashFunc2()(key) % (N * X);
			
			size_t hashi3 = HashFunc3()(key) % (N * X);
			_bts.set(hashi1);
			_bts.set(hashi2);
			_bts.set(hashi3);
			//size_t hashi4 = HashFunc4()(key) % (N * X);

   }

		bool Test(const K& key)
		{
			size_t hashi1 = HashFunc1()(key) % (N * X);
			if (!_bts.test(hashi1))//数据不在是确定的
			{
				return false;
			}
			size_t hashi2 = HashFunc2()(key) % (N * X);
			if (!_bts.test(hashi2))//数据不在是确定的
			{
				return false;
			}
			size_t hashi3 = HashFunc3()(key) % (N * X);
			if (!_bts.test(hashi3))//数据不在是确定的
			{
				return false;
			}

			return true;//可能存在误判--映射的几个位置都冲突,就会发生误判
		}

	private:
		std::bitset<N* X> _bts;//开辟最多存储的数据个数*平均存储一个数据要开辟的映射位
	};

事例

image-20230425003734590

实际上布隆过滤器只能降低误判率,而不能完全消除,多次实验后还是会有冲突的数据。

image-20230425004154159

布隆过滤器的删除

布隆过滤器一般不支持删除操作,理由如下:

布隆过滤器判断一个数据存在是不确定的(数据的存在可能是误判)

  1. 当要删除的数据存在布隆过滤器是误判时,删除该数据对应的位图上的比特位(把对应的比特位由1置0)会影响其他也映射到这些位置上的数据。
  2. 无论要删除的数据是否真的在位图上,删除该数据的操作都会影响到其他也映射到相同位置上的数据。

若确定要支持删除操作,当删除数据时最好进入数据库(磁盘)中确认数据是否存在,这个过程要通过文件IO流,这个过程相对缓慢,效率极低;另一种方法是位图上每个比特位都新增一个计数器,当有插入数据,映射到这个比特位上,计数器++,当要删除数据时,对应的比特位上的计数器–但这个方法会导致位图需要的内存成倍增加,代价巨大。所以一般而言布隆过滤器不支持删除操作。

布隆过滤器优点

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

布隆过滤器缺陷

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

相关大数据题目

给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件的交集?给出近似算法

解题思路:

先读取其中一个文件中的query,将其全部插入到布隆过滤器中。

再读取另一个文件只的query,依次判断每个query是否在布隆过滤器中,若存在,则是两个文件的交集,把交集再放到同一个文件中。但这个存储交集的文件还需要去重工作,把这个文件放到set或者map中进行去重。这个算法可能会存在误判—近似算法。

准确算法:

假设平均每个query是50byte,100亿个query合计500GB。由于我们只有1G内存,所以我们把一个文件的query通过hashfunc函数切分成400个文件。每个query作为key,通过hashfunc函数转化成整形i,i是多少就进入对应i的Ai或Bi文件。这样两个大文件的query都能切分到对应的小文件里。

image-20230425182947029

切分两个大文件是用的hashfunc函数要是一样的,这样通过hashfunc函数切分A文件和B文件出来的i是相同的,key对应的query大概率也是相同的(query可能会冲突)

现在只需要在A0和B0、A1和B1、A2和B2…小文件中寻找交集即是原本两个大文件的交集。

image-20230425183706828

理论上切分出来的每个小文件的平均大小是512M,因此我们可以将对应i的值其中一个的小文件加载到内存中放到set里,然后依次遍历另外一个小文件中的query,依次判断每个query是否在set容器中,若存在则是交集。

但因为切分文件时并不是平均切分的,所以切分出来的小文件大小有可能超过1G。

若对应i的值的两个小文件其中一个的大小没有超过1G,就把较小的那个小文件加载到内存中放进set里,然后遍历那个较大的,判断交集。

.(img-u5LNDvmc-1682421381710)]

理论上切分出来的每个小文件的平均大小是512M,因此我们可以将对应i的值其中一个的小文件加载到内存中放到set里,然后依次遍历另外一个小文件中的query,依次判断每个query是否在set容器中,若存在则是交集。

但因为切分文件时并不是平均切分的,所以切分出来的小文件大小有可能超过1G。

若对应i的值的两个小文件其中一个的大小没有超过1G,就把较小的那个小文件加载到内存中放进set里,然后遍历那个较大的,判断交集。

若对应i的值的两个小文件的大小都超过了1G,就按照上面切分方式再次切分这两个小文件。切分完后在判断是不是交集。

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

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

相关文章

P1039 [NOIP2003 提高组] 侦探推理

题目描述 明明同学最近迷上了侦探漫画《柯南》并沉醉于推理游戏之中&#xff0c;于是他召集了一群同学玩推理游戏。游戏的内容是这样的&#xff0c;明明的同学们先商量好由其中的一个人充当罪犯&#xff08;在明明不知情的情况下&#xff09;&#xff0c;明明的任务就是找出这…

Java-设计模式中事件与委托Java版本

目录 背景介绍 实现过程 类图 NS图 代码 客户端 业务封装类 委托类 事件类 猫类 老鼠类 运行结果 总结提升 背景介绍 相信大家在学习大话设计模式的时候都有接触过事件与委托&#xff0c;但是对于事件与委托具体的业务逻辑也不是很清楚&#xff0c;只能照猫画虎去使用…

SEO机制算是让我玩明白了

获取当前时间时间戳&#xff0c;返回遵循ISO 8601扩展格式的日期 new Date(Date.now()).toISOString() 使用moment库转换回来 this.moment(new Date(Date.now()).toISOString()).format("YYYY-MM-DD") js去掉富文本中html标签和图片 filterHtmlTag(val) {if(!val){…

Shell编程规范与使用

一、Shell脚本概述 1&#xff09;Shell的作用——命令解释器&#xff0c;“翻译官” Linux 系统中的 Shell 是一个特殊的应用程序&#xff0c;它介于操作系统内核与用户之间&#xff0c;充当 了一个“命令解释器”的角色&#xff0c;负责接收用户输入的操作指令&#xff08;命…

接口协作--apipost接口协作工具

接口协作 apipost支持接口在线协作编辑功能&#xff0c;打开apipost创业一个团队&#xff0c;在创建一个项目。 在把需要一起协作的人员添加到团队中 在进行项目编辑把需要进行协作的人员拉取到项目中 之后在进入项目创建接口就可以进行接口协作了

scratch猫捉老鼠 少儿编程 电子学会图形化编程scratch编程等级考试二级真题和答案解析2023年3月

目录 scratch猫捉老鼠 一、题目要求 1、准备工作 2、功能实现 二、案例分析

kafka调试脚本的使用

创建名称为test的topic且副本数量3&#xff0c;partition数量6 /etc/kafka/kafka/bin/kafka-topics.sh --create --bootstrap-server 10.1.60.112:9092 --replication-factor 3 --partitions 6 --topic test 查看名称为test的topic信息 /etc/kafka/kafka/bin/kafka-topics.sh -…

uniapp微信小程序图片预览PreviewImage

一、说明 功能&#xff1a;点击图片预览大图&#xff0c;并且可以通过滑动查看不同图片的预览大图。 点击预览大图后&#xff1a; 二、上代码 参考uniapp官方文档 其提供了预览大图的函数uni.previewImage(OBJECT). //放大查看推荐图片enlargePicture(index) {console.log…

【Unity-ML】Unity机器学习(一)

安装环境&#xff1a;Windows10 Anaconda3(64-bit)&#xff0c;网上很多教程&#xff0c;例如这个anaconda下载及安装(保姆级教程) - 知乎anaconda包管理器和环境管理器&#xff0c;强烈建议食用 1.下载官网下载太慢可选用镜像下载 官网下载&#xff1a; Anaconda | Individua…

Softing FiberXpert 700光纤测试套件助力一级多模和单模光纤认证

FiberXpert 700是用于多模和单模的四路波长测试套件&#xff0c;不仅可以对光纤链路进行直观、灵活和快速地认证&#xff0c;而且可以导出数据报告。 测试网络安装以确保其符合指定标准的过程称为认证&#xff0c;并且这通常需要纸质文件作为符合标准的证明。而FiberXpert 700光…

Docker 的数据管理

一、Docker 的数据管理 管理 Docker 容器中数据主要有两种方式&#xff1a;数据卷&#xff08;Data Volumes&#xff09;和数据卷容器&#xff08;DataVolumes Containers&#xff09;。 1&#xff0e;数据卷 数据卷是一个供容器使用的特殊目录&#xff0c;位于容器中。可将宿…

为什么说网络安全行业是IT的风口行业?

前言 2023年网络安全行业的前景看起来非常乐观。根据当前的趋势和发展&#xff0c;一些趋势和发展可能对2023年网络安全行业产生影响&#xff1a; 5G技术的广泛应用&#xff1a;5G技术的普及将会使互联网的速度更快&#xff0c;同时也将带来更多的网络威胁和安全挑战。网络安全…

eBPF技术介绍

前言 eBPF起源于linux内核&#xff0c;它可以以砂箱程序运行在操作系统内核的特权上下文&#xff0c;高效&#xff0c;安全&#xff0c;易于扩展而不需要修改内核源码或者加载内核模块。 操作系统一直是实现观测&#xff0c;安全和网络功能的最理想的地方&#xff0c;因为内核的…

Vue基本的内置指令

前言 除了常见的v-bind,v-for,v-if,v-on.v-model等&#xff0c;本次学习一些vue提供的其他内置指令 1 v-text 给标签插入文本&#xff0c;类似于插值语法 它会把全部的字符串当成文本去解析,不会当成标签的,哪怕写的是标签结构 效果和插值语法是一样的 插值语法比v-text更加…

P1037 [NOIP2002 普及组] 产生数

题目描述 给出一个整数 &#xfffd;n 和 &#xfffd;k 个变换规则。 规则&#xff1a; 一位数可变换成另一个一位数。规则的右部不能为零。 例如&#xff1a;&#xfffd;234,&#xfffd;2n234,k2。有以下两个规则&#xff1a; 2⟶52⟶5。3⟶63⟶6。 上面的整数 23423…

SpringBoot的配置和日志

1.配置文件的作用和意义 配置文件中配置整个项目中所有重要的数据&#xff0c;比如&#xff1a; 1.数据库的连接信息&#xff08;包含用户名和密码的设置&#xff09;&#xff1b; 2.项目的启动端口&#xff1b; 3.第三方系统的调用秘钥等信息&#xff1b; 4.用于发现和定位问…

docker--harbor私有仓库部署与管理

目录 第一章.搭建本地私有仓库 1.1.下载 registry 镜像 1.2.运行 registry 容器 1.3.Docker容器的重启策略如下&#xff1a; 1.4.简单操作 第二章. Harbor 简介 2.1.什么是Harbor 2.2.Harbor的特性 2.3.Harbor的构成 第三章.部署服务 3.1.环境部署 3.2.部署 Docker…

USART串口协议和USART串口外设(USART串口发送串口发送和接收)

1、通信接口 • 通信的目的&#xff1a;将一个设备的数据传送到另一个设备&#xff0c;扩展硬件系统 • 通信协议&#xff1a;制定通信的规则&#xff0c;通信双方按照协议规则进行数据收发 异步&#xff1a;需要双方约定一个频率 2、 硬件电路 • 简单双向串口通信有两根通信…

基于springboot和ajax的简单项目 013 ztree插件使用,这是关于修改和新增的

先写写的是menu_list.html文件上的内容。 01.在自动加载函数上写点击事件 $(".input-group-btn").on("click",".btn-delete",doDeleteObject).on("click",".btn-add,.btn-update",doLoadEditUI);02.登录函数&#xff1a; …

30秒预测10天全球天气,上海人工智能实验室发布气象大模型“风乌”效果超DeepMind

4月7日&#xff0c;上海人工智能实验室联合中国科学技术大学、上海交通大学、南京信息工程大学、中国科学院大气物理研究所及上海中心气象台发布全球中期天气预报大模型“风乌”。基于多模态和多任务深度学习方法构建&#xff0c;AI大模型“风乌”首次实现在高分辨率上对核心大…