哈希与位图的结合--布隆过滤器与哈希切分

news2024/12/25 9:25:22

上一章讲了位图,我们知道了在海量数据中查找一个数是否存在,可以用每一个比特位标识。

但是位图只能处理整数,要是字符串或者其它的呢,位图便无法处理了,这个时候便需要用到布隆过滤器了.

目录

布隆过滤器提出

布隆过滤器概念

布隆过滤器的实现以及最优值

 哈希切分


布隆过滤器提出

我们在看b站视频时,它会给我们不停地推荐新的内容,它每次推荐时要去重,去掉那些已经看过的内容。问题来了,b站推荐系统如何实现推送去重的? 用服务器记录了用户看过的所有历史记录,当b站推荐系统推荐视频时会从每个用户的历史记录里进行筛选,过滤掉那些已经存在的记录。 如何快速查找呢?
1. 用哈希表存储用户记录,缺点:浪费空间
2. 用位图存储用户记录,缺点:位图一般只能处理整形,如果内容编号是字符串,就无法处理了。
3. 将哈希与位图结合,即布隆过滤器

布隆过滤器概念

布隆过滤器(Bloom Filter)是一种概率型的数据结构,用于快速判断一个元素是否存在于一个集合中。它基于位图(或称为位数组)和多个哈希函数构成。

布隆过滤器的主要目标是在存储空间相对较小的情况下,实现高效的成员查询。它通过使用多个哈希函数将要查询的元素映射到位图中的多个位上。当一个元素要插入布隆过滤器时,通过多个哈希函数计算出多个索引位置,并将对应位设置为1。当判断一个元素是否存在时,再次通过多个哈希函数计算出多个索引位置,并检查对应位是否都为1。如果有任何一个位为0,则可以确定该元素一定不存在于集合中;如果所有位都为1,则表示该元素可能存在于集合中,但有一定的误判率

注意,只有判断一个数据存在的时候才会有误判。

因为如果判断一个数据它存在了,但是它可能和别的数据冲突了,即别的数据占的这些位恰好是这个数据的那些位。这样就会造成误判。

我举个例子可以说明一下:

而判断一个数据不存在这是绝对不可能误判的,因为不存在说明别的数据都还没有占用这些位,一定没有和这个数据相同的数据存在,因为如果存在了,对应的所有位就都是1了,此时便会判它存在了.

这个误判是不可能去掉的,但是可以降低误判的概率。比如增加多个哈希函数。

这样的话,每个数据对应的位就会多了,这样重合的概率就会更低。

例如之前一个数据对应两个位,现在对应了三个位,是不是重合的概率又降低了呢?

虽然原来我们两个位冲突了,现在又多了一个位,又多了一个机会,此时便相当于降低了误判率。

但是也不能盲目的增加哈希函数,具体会有一个合适的值.

 如果映射函数的越多,空间消耗就会增多,就失去了原本的优势

这个会被应用在哪些地方呢?

比如访问一个网站,会有黑名单用户,每个用户访问的时候,都需要判断一下是不是黑名单用户。

我们如果每次都要去数据库中查找,那么经过网络传输等过程会使效率变得非常低下,所以是不被采用的。

这个时候便用到了我们的布隆过滤器,每当有用户访问,先查找存在不存在,有两种情况:

1.存在:如果存在,我们才再次需要去数据库中查找,因为存在会有误判,万一人家不是黑名单用户,你却误判成了黑名单,这当然是不合理的,这个时候才去数据库中查找。

2.不存在:不存在那就说明这个人一定不是黑名单用户了,因为不存在不会有误判,直接返回即可:

还有一个我们常见的情景,就是在注册申请一个账号的时候,比如输入昵称的时候,有的后面会立马显示“此昵称已被占用”。这其实就是应用了布隆过滤器。

当你输入一个名字的时候,如果利用布隆过滤器查找,发现存在了,会给你提示已经存在,但是这个名字一定是存在的吗?那不一定,毕竟有可能是误判。

但是你也不会察觉到,占用就占用了呗,然后换一个就行。当然这也对这个公司有好处,报总比不报好,万一就是已经存在的呢,这样就不好了。

但是不存在的话,就不会报,因为它一定不存在了。这也是前面一直所说的.

布隆过滤器的实现以及最优值

由于C++STL库中并没有布隆过滤器,所以得靠我们自己手动实现。

首先由于布隆过滤器的数据类型不再只是整数,所以我们要利用模板。

可以定义K为数据的类型,那么我们每次需要给位图开多大空间呢?

总不能一上来开41亿,比如就100个数据,开这么大就有点浪费了,所以再给出一个非类型模板参数N,代表数据的个数,然后在成员变量中定义一个_ratio表示倍数,每次开N*_ratio的空间即可。

可能有同学会有疑问,之前位图不是说开多少空间取决于数据范围吗?这个怎么不用?

因为位图采用的是直接定址法,它这个数据是多少,它就会对应在位图的相应位置,所以数据范围的大小决定了开多少空间。

而布隆过滤器是对每个数据采用了哈希映射,而且是多个哈希映射,这样当然不需要多么大的空间了。比如一个数据1,和99999999,采用了哈希映射后,1对应的位图中是1,2,3,而99999999对应的位图是1,3,4、这个只是一个假设,目的是理解为什么不用根据数据范围来开空间。

然后毋庸置疑,就是对应的哈希函数模板,那么具体定义多少个哈希映射函数呢

不仅如此,布隆过滤器(内部其实就是一个位图)的长度也影响了误判率,那么长度为多少合适呢?

这个有一套数学理论和实验证明,这里我们只看一下最终结果和公式:

“很显然,过小的布隆过滤器很快所有的 bit 位均为 1,那么查询任何值都会返回“可能存在”,起不到过滤的目的了。布隆过滤器的长度会直接影响误报率,布隆过滤器越长其误报率越小。

另外,哈希函数的个数也需要权衡,个数越多则布隆过滤器 bit 位置位 1 的速度越快,且布隆过滤器的效率越低;但是如果太少的话,那我们的误报率会变高”

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

那么具体该如何选择k和m的值是最优的呢?

有两个公式:

根据这两个公式可以选出最优的m和k的值。

然后下面就是布隆过滤器的代码,代码不唯一的,具体根据m和k的值进行调整。

//N表示准备要映射N个值.
template<size_t N,class K,
class Hash1,class Hash2,class Hash3>
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;
	bitset<_ratio*N> _bits;
};

这是主体部分的代码,可以看到以上代码中用了3个哈希函数,具体每个哈希算法怎么实现,则由自己进行决定。也可以在网上搜索一些哈希相关的算法。

然后我们定义3个类,每个类中重载()运算符,然后进行哈希算法的实现。最后创建对象时,把这三个类传入即可。

下面讲解两道布隆过滤器相关的题目:

1、 如何扩展BloomFilter使得它支持删除元素的操作?

首先我们要知道,布隆过滤器一般情况下是不能被删除的。大家想一想,为什么?

因为位图中的某一个位可能是两个字符串的位,即它们有公共比特位,例如这张图:

如果我们想删除百度,即将百度的对应的位全部置为0,b站也会受到影响, 最后只剩下1个位了。

这样在查找的时候,便也查找不到b站了,所以对其他数据也造成影响了。

那么我们能不能强行改造一下呢?

当然是可以的,我们另创建一个位来保存每个位置1的个数count,若每次删除,则将count减1。

如果count为0说明此时已经没有数据再占用这个位了,便可以置为0了,否则,count--即可.

当然这么做会增加了空间的消耗,会削弱布隆过滤器的优势,与其有这些空间存这个,不如用这些空间来降低一些误判率呢.

 哈希切分

先来看下面这个问题:

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

以上这张图已经说明了,我们可以将100G数据中的每个ip地址由哈希函数转为整数,再进行切分到不同的桶中,这样每个桶中都有对应的ip地址了。

那么我们该如何统计每个桶中的ip地址的个数呢?

当然是利用map进行统计,如果超过1G了呢?这里也会出现两种情况:

1.这个桶中不相同的ip很多,由于每次遇到不同的ip值,map都要进行插入,所以map统计不了。

2.这个桶中相同的ip很多,虽然有很多的ip,但是由于很多是相同的,所以直接在key对应的value上加1节课即可,并不会消耗空间。

直接使用map中的insert将每一个桶的元素插入到map中。

情况一:如果insert插入失败,说明空间不足,new节点失败,抛出异常。解决方法是换个哈希函数,递归再次对这个冲突桶进行切分。

情况二:map可以正常统计。

这样找到map中value值最高的即可。

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

近似算法:将一个文件中的数据全部set进布隆过滤器中,然后分别用另一个文件的数据test比对,可以淘汰一定不是交集的部分。当然淘汰完剩下的那部分数据中,也会有非交集的存在。

精确算法:这个可以参考第一个问题的哈希切分方法。

思路是:利用相同的哈希函数(记住一定相同!)将两个文件的数据进行哈希切分,最后每个文件都会切分出许多小文件。然后我们分别比对下标相同的小文件,找出交集即可。

例如A文件切分出了A0,A1,A2...A999.

B文件切分出了B0,B1,B2....B999.

我们直接A0和B0,A1和B1,...A999和B999进行比较即可,因为两个文件使用了相同的哈希函数,所以对于两个文件相同的数据,经过切分后,一定会在编号相同的桶中.

然后再利用哈希表或set都可以,先将一个文件全部插入,经过去重后,再用另一个文件进行比对,找出交集即可。

 

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

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

相关文章

斯坦福发布最新LLM排行榜AlpacaEval,微软WizardLM登顶开源模型第一

斯坦福发布最新LLM排行榜AlpacaEval&#xff0c;微软WizardLM登顶开源模型第一 文章目录 Part 1. 众多LLM排行榜Part 2. AlpacaEval 技术细节2.1 AlpacaEval 评估效果2.2 如何使用AlpacaEval评估模型 Part 3. 微软 WizardLM 登顶开源模型第一3.1 关于 WizadLM 与 Evol-Instruc…

PostgreSQL使用localhost可以连接,使用IP无法连接

问题描述&#xff1a;PostgreSQL使用localhost可以连接&#xff0c;使用IP无法连接 默认情况下&#xff0c;刚安装完成的 postgresSQL12 无法使用 数据库连接工具&#xff08;如postman&#xff09;连接。需要为其修改配置&#xff0c;开放连接权限。 修改pg_hba.conf 增加…

【js小案例】视频倍数播放、计算机、待办事项管理

视频倍数播放示例图&#xff1a; 视频倍数播放代码&#xff1a; <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>控制视频播放速度</title> </head> <body><video id"myVideo" width&quo…

c语言内存

程序是保存在硬盘中的&#xff0c;要载入内存才能运行&#xff0c;CPU也被设计为只能从内存中读取数据和指令。 对于CPU来说&#xff0c;内存仅仅是一个存放指令和数据的地方&#xff0c;并不能在内存中完成计算功能&#xff0c; 如&#xff1a;计算abc,必须将a,b,c都读取到CPU…

解锁生成式AI万亿规模市场,亚马逊云科技有效降低AIGC门槛

ChatGPT一声惊雷&#xff0c;让全球见识到了生成式AI的威力。当前&#xff0c;生成式AI进入一个爆发时刻&#xff0c;并在许多领域中展现出它的无限潜力。那么&#xff0c;在这轮生成式AI大爆发中&#xff0c;企业应当如何抓住机遇&#xff0c;顺应这一波时代的潮水&#xff0c…

PHP:数据库中设置文本长度,通过js去限制前台文本长度。扩展:数据类型的限制

效果图 如上图&#xff1a;当测试111的长度超过数据库中限制的长度&#xff0c;进行提示&#xff0c;并且自动将多余部分截掉 HTML代码 <!-- 附加属性 --> <div class"text-nav-1 " id"append1"> <div >append1</div><input…

如何使经纬度标注在图框内部

在生成经纬网格之后&#xff0c;如果标注了经纬度&#xff0c;仔细查看图框边缘&#xff0c;可以看到标注的经纬度出现在了图框的外面&#xff0c;这样显得不是很美观&#xff0c;我们可以通过偏移的方法让其回到图框内部&#xff0c;这里为大家介绍一下具体的操作方法&#xf…

达梦数据库 SQL交互式查询工具打不开问题处理

目录 1、开始菜单找到 “SQL交互式查询工具”。 2、 右键进入 打开文件位置。 3、右键进入属性&#xff0c;找到目标位置 4、进入我的电脑&#xff0c;访问该地址&#xff0c;并授予此地址权限 1、开始菜单找到 “SQL交互式查询工具”。 2、 右键进入 打开文件位置。 3、右…

【ARM Coresight 及 DS-5 介绍 2 - ARM Coresight 介绍】

文章目录 1.1 ARM Coresight 介绍1.1.1 ARM Coresight 发展历史 1.2 ARM Coresight 框架介绍1.1.1 Trace 通路1.1.3 Debug 通路1.1.4 Trigger 通路 1.1 ARM Coresight 介绍 ARM Coresight是ARM公司提供的一种调试和跟踪技术&#xff0c;用于ARM处理器的调试和性能分析。它通过…

根据ABAP字符寻找程序

知识来之不易&#xff0c;还请多点赞&#xff01; SE38执行程序RPR_ABAP_SOURCE_SCAN

实现流程化办公,该说不说还得借力低代码开发框架

在科技的推动下&#xff0c;流程化办公已经成为潮流。如何实现流程化办公&#xff0c;让越来越多的企业打通各部门之间的协作&#xff0c;实现高效率发展&#xff1f;借力低代码开发框架&#xff0c;让那遥不可及的梦想变为现实&#xff0c;跟传统操作方式比起来&#xff0c;低…

Pandas库如何在导出表格的时候去掉索引列(隐藏索引列)

import pandasdata {sku1:[1,2,3],sales:[11,22,33], } doc pandas.DataFrame(data)file_path rC:\Users\Zhao\Desktop\test1.xlsx doc.to_excel(file_path,indexFalse) # indexFalse 可以隐藏 索引列

从php5.6到golang1.19-文库App性能跃迁之路

作者 | 百度文库App 导读 本文深入浅出地分享了百度文库App服务端技术栈从PHP迁移至Go的实战经验&#xff0c;包含了技术选型、基础建设、流量迁移的具体方案&#xff0c;以及核心项目案例的重构实践。 全文6209字&#xff0c;预计阅读时间16分钟。 01 动机 长期以来&#xff…

异地使用PLSQL远程连接访问Oracle数据库【内网穿透】

文章目录 前言1. 数据库搭建2. 内网穿透2.1 安装cpolar内网穿透2.2 创建隧道映射 3. 公网远程访问4. 配置固定TCP端口地址4.1 保留一个固定的公网TCP端口地址4.2 配置固定公网TCP端口地址4.3 测试使用固定TCP端口地址远程Oracle 转载自cpolar极点云文章&#xff1a;公网远程连接…

【036】读懂C++的强制类型转换static_cast、const_cast、dynamic_cast以及reinterpret_cast

C的强制类型转换 引言一、类型转换简介二、上行、下行转换的概述三、static_cast 静态类型转换四、dynamic_cast 静态类型转换&#xff08;推荐使用&#xff09;五、const_cast 常量转换六、reinterpret_cast 重新解释转换&#xff08;最不安全&#xff09;总结 引言 &#x1f…

微信支付(一):小程序支付(go+gin+内网穿透)

一、前置条件 &#xff08;1&#xff09;go语言&#xff0c;1.18 &#xff08;2&#xff09;Gin、第三方依赖包&#xff1a;gopay【github.com/go-pay/gopay/alipay】https://github.com/go-pay/gopay/blob/main/doc/wechat_v3.md &#xff08;3&#xff09;微信支付相关信息…

Ubuntu安装:显卡驱动、CUDA、Anaconda

Ubuntu安装&#xff1a;显卡驱动、CUDA、Anaconda 摘要1.安装NVIDIA显卡驱动2.安装CUDA3.安装Anaconda Windows环境安装CUDA和Pytorch见&#xff1a;Pytorch入门&#xff1a;3.安装 环境&#xff1a;x86_64 Linux ubuntu18 4.150.0-20-generic 摘要 本篇博客对Ubuntu系统安装…

#消防知识#自动灭火系统是什么?

自动灭火系统是指能够在发生火灾时自动检测、控制和扑灭火灾的系统&#xff0c;包括自动喷水灭火系统、气体灭火系统、干粉灭火系统、气溶胶灭火系统等。不同的自动灭火系统有不同的组成部件、工作原理和适用范围&#xff0c;以下是一些简要的介绍&#xff1a;• 自动喷水灭火系…

汽车远程升级(OTA)定义与技术体系

1.汽车OTA定义 1.1. OTA概述 OTA&#xff08;Over-the-air technology&#xff09;是一种通过无线方式而不是使用电缆或其他本地连接进行数据传输的远程升级。能够实现对现有性能/功能的优化、新功能推送等。OTA技术最早应用于PC&#xff0c;而后在手机上普及&#xff0c;终结…

Go语言程序设计(二)常量、变量、布尔类型与运算符

一、常量、变量与命名规则 常量使用关键字const声明&#xff1b;变量可以使用关键字var声明&#xff0c;也可以使用快捷变量声明语法。Go语言可以自动推断出所声明变量的类型&#xff0c;但是如果需要显式指定其类型也是合法的&#xff0c;比如声明一种与Go语言的常规推断不同的…