【C++进阶】哈希的应用 --- 布隆过滤器

在这里插入图片描述

👦个人主页:@Weraphael
✍🏻作者简介:目前学习C++和算法
✈️专栏:C++航路
🐋 希望大家多多支持,咱一起进步!😁
如果文章对你有帮助的话
欢迎 评论💬 点赞👍🏻 收藏 📂 加关注✨


目录

  • 一、问题引入
  • 二、布隆过滤器的概念
  • 三、布隆过滤器的实现
      • 3.1 基本结构
      • 3.2 插入
      • 3.3 查找
      • 3.4 删除
      • 3.5 测试
      • 3.6 优化
  • 四、总结
  • 五、代码

一、问题引入

随着玩王者荣耀的用户越来越多,想要取一个心悦的名字是非常难的。当名字重复的时候,系统非常快的提示“名字已被使用”,那么它是如何做到快速查找名字是否被使用了呢?

在这里插入图片描述

在如此大的数据下,首先可以想到使用位图:

  • 但由于位图的数据必须是size_t的,那么需要先将名字(字符串)映射成整型,然后再存入位图中。然而,我们对位图开空间是根据数据范围的,但字符串的长度是可变的,使用位图来表示长度不固定的数据会带来很大的挑战。比如,开少了可能引发冲突,那么就会导致误判。比如说李白和百里都占同一个比特位并且都标记成1,当有人将修改了百里这一名称,那么其实就是reset操作,那么,李白也同样会被reset,那么就会有第二个人可以取李白这个名字。

  • 并且字符串在计算机中是以字符集编码的方式存储的,一个字符可能由一个或多个字节组成。而位图一般用于表示集合中元素的存在与否,对于字符集编码来说,字符的种类和编码范围非常广泛,使用位图来表示所有可能的字符将需要非常大的存储空间,并且不实际。

这时候就需要我们本文中的主角: 布隆过滤器Bloomfilter

二、布隆过滤器的概念

布隆过滤器是由布隆在1970年提出的 一种紧凑型的、比较巧妙的概率型数据结构,特点是高效地插入和查询。可以用来告诉你 “某样东西一定不存在或者可能存在”

布隆过滤器是如何做到的呢?

这里举一个小故事:假设布隆有一天要去线下见网友,前提是他不知道网友的任何特征,那怎么办?只能发一个消息问对方今天的穿搭,而网友说今天带了一个黑色的帽子,然而布隆一眼望去,今天穿戴黑色帽子的人也特别的多,于是再次向网友询问更多的特征来过滤掉更多穿搭相识的人。

于是布隆过滤器就是 使用多个哈希函数,将一个数据映射到位图结构中,也就是 一个数据映射位图的多个位置哈希与位图结合,即布隆过滤器)。只有当映射的比特位全为1,则说明字符串存在。

在这里插入图片描述

三、布隆过滤器的实现

3.1 基本结构

既然需要多个哈希函数,这里我只模板中添加三个哈希函数的模板参数,以及待存储的数据类型,不过布隆过滤器最常见存储的类型是字符串,所以可以给一个缺省值。

在这里插入图片描述

显然,这三个 哈希函数 的选择是十分重要的,我们在这里提供三种较为优秀的哈希函数(字符串哈希算法),分别是 BKDRHashAPHash 以及 DJBHash

在这里插入图片描述

struct BKDRHash
{size_t operator()(const string& str){register size_t hash = 0;for  (auto& ch : str){hash = hash * 131 + (size_t)ch;    }return hash;}
};struct APHash
{size_t operator()(const string& str){size_t hash = 0;for (auto e : str){if (((size_t)e & 1) == 0){hash ^= ((hash << 7) ^ (size_t)e ^ (hash >> 3));}else{hash ^= (~((hash << 11) ^ (size_t)e ^ (hash >> 5)));}}return hash;}
};struct DJBHash
{size_t operator()(const string& str){if (str.empty())return 0;size_t hash = 5381;for (auto e : str){hash += (hash << 5) + (size_t)e;}return hash;}
};template<size_t N, class K = string, class Hashfunc1 = BKDRHash, class Hashfunc2 = APHash, class Hashfunc3 = DJBHash>
class bloomfilter
{
public:private:bitset<N> _bt;
};

3.2 插入

步骤:

  • 通过三个哈希函数计算不同的哈希值

  • 最后再调用bitset中的set在位图中标记即可

在这里插入图片描述

3.3 查找

判断在不在也非常简单,我们可以再次计算出三个哈希值,只要它们对应的二进制位上是1,说明要查找的字符串存在,只要有一个为0,说明要查找的字符串一定不存在。

在这里插入图片描述

这里需要注意的是:布隆过滤器判断不在是准确的,判断 是不准确的

比如,“李白“映射了位置0 1 3,”百里“映射了位置3 5 7,虽然这两个字符串不会相互影响,但原本”妲己“不存在位图中,并且通过调用test发现三个位置都是1,那么就会误判存在。

在这里插入图片描述

因此 布隆过滤器并不是非常可靠,那该怎么办呢?有人想到了使用 布隆过滤器 + 数据库 的方式进行双重验证

如果 布隆过滤器 判断字符串不存在,那么就是真的不存在,因为这是绝对准确的;但如果在布隆过滤器中存在,可以去数据库里搜索,再反馈给用户。

3.4 删除

布隆过滤器支持删除吗?其实一般是不支持的。因为支持删除会存在重大问题,删除一个值会影响其他字符串。

在这里插入图片描述

虽然成功删除了 “李白”,但同时也影响了 “百里”,当要验证“百里”是否存在时,会被判断为不存在,所以布隆过滤器,一般是不支持删除操作的。

3.5 测试

在这里插入图片描述

3.6 优化

如果真的误判存在了那该怎么办呢?有的人想增加哈希函数的个数,那么问题来了,增加多少个合适呢?

还有一个比较合适的方案:扩大布隆过滤器的长度,使数据更分散,来降低误判的概率

那么如何选择合适的布隆过滤器的长度呢?对此有大佬做出了研究:

在这里插入图片描述

我们可以来估算一下,假设用3个哈希函数,即K = 3ln2 的值我们取 0.7,那么 mn 的关系大概是 m = n×k/ln2=4.2n ,也就是过滤器长度应该是插入元素个数的 4 -5倍

在这里插入图片描述

四、总结

优点:

  1. 增加和查询元素的时间复杂度为:O(K), (K为哈希函数的个数,一般比较小),与数据量大小无关

  2. 哈希函数相互之间没有关系,方便硬件并行运算

  3. 布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势

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

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

  6. 使用同一组散列函数的布隆过滤器可以进行交、并、差运算

缺点:

  1. 有误判率,不能准确判断元素是否在集合中(补救方法:再建立一个白名单,存储可能会误判的数据)

  2. 不能获取元素本身

  3. 一般情况下不能从布隆过滤器中删除元素

实际应用场景:

  1. 注册时对于 昵称、用户名、手机号的验证

  2. 减少磁盘 IO 或者网络请求,因为一旦一个值必定不存在的话,我们可以不用进行后续昂贵的查询请求

五、代码

本篇博客相关代码:点击跳转

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

macOS上实现「灵动岛」效果

自从Apple iPhone推出了「灵动岛」功能后&#xff0c;用户们就被其优雅的设计和强大的功能所吸引。然而&#xff0c;作为macOS用户&#xff0c;我们一直在等待这一功能能够在我们的设备上实现。现在&#xff0c;随着新的应用程序的推出&#xff0c;我们终于可以在我们的Mac上体…

300分钟吃透分布式缓存-25讲:Redis是如何处理容易超时的系统调用的?

BIO 线程简介 Redis 在运行过程中&#xff0c;不可避免的会产生一些运行慢的、容易引发阻塞的任务&#xff0c;如将内核中的文件缓冲同步到磁盘中、关闭文件&#xff0c;都会引发短时阻塞&#xff0c;还有一些大 key&#xff0c;如一些元素数高达万级或更多的聚合类元素&#…

C++篇 语 句

到目前为止&#xff0c;我们只见过两种语句&#xff1a; return 语句和表达式语句。根据语句对执行顺 序的影响&#xff0c;C 语言其余语句大多属于以下 3 大类。 选择语句&#xff1a; if 语句和 switch 语句。循环语句&#xff1a; while 语句&#xff0c; do...while 语句和…

部署LVS+Keepalived高可用群集(抢占模式,非抢占模式,延迟模式)

目录 一、LVSKeepalived高可用群集 1、实验环境 2、 主和备keepalived的配置 2.1 yum安装ipvsadm和keepalived工具 2.2 添加ip_vs模块并开启ipvsadm 2.3 修改keepalived的配置文件 2.4 调整proc响应参数&#xff0c;关闭linux内核的重定向参数响应 2.5 将主服务器的kee…

山泉还可以申请商标不,现阶段通过率如何!

在32类类别啤酒饮料是许多生产水企业主要申请注册的类别&#xff0c;那现在山泉在这个类别还可以申请注册商标不&#xff0c;山泉在这个类别基本上是通用词&#xff0c;首先是需要前面词具有显著性&#xff0c;没的相同或近似才可以。 经普推知产老杨检索发现&#xff0c;在32…

Redis作为缓存的数据一致性问题

背景 使用Reids作为缓存的原因&#xff1a; 在高并发场景下&#xff0c;传统关系型数据库的并发能力相对比较薄弱&#xff08;QPS不能太大&#xff09;&#xff1b; 使用Redis做一个缓存。让用户请求先打到Redis上而不是直接打到数据库上。 但是如果出现数据更新操作&#xff…

springboot256基于springboot+vue的游戏交易系统

游戏交易系统设计与实现 摘 要 在如今社会上&#xff0c;关于信息上面的处理&#xff0c;没有任何一个企业或者个人会忽视&#xff0c;如何让信息急速传递&#xff0c;并且归档储存查询&#xff0c;采用之前的纸张记录模式已经不符合当前使用要求了。所以&#xff0c;对游戏交…

Vue快速开发一个主页

前言 这里讲述我们如何快速利用Vue脚手架快速搭建一个主页。 页面布局 el-container / el-header / el-aside / el-main&#xff1a;https://element.eleme.cn/#/zh-CN/component/container <el-container><el-header style"background-color: #4c535a"…

使用GitOps自动化推动AI/ML工作流程

作为一名深耕自动化和人工智能领域的开发人员&#xff0c;我们逐渐认识到尖端工具和方法之间的显着协同作用&#xff0c;这些协同作用突破了可能性的界限。在这次探索中&#xff0c;我们想分享一个概念&#xff0c;它不仅彻底改变了我们的软件开发和基础设施管理方法&#xff0…

VRRP与BFD在项目中的结合使用

学习目标&#xff1a; 1. VRRP双网关热备份怎样部署&#xff1f; 2. BFD是一种怎样的检测技术&#xff1f; 3. VRRP与BFD联动实现故障的快速切换&#xff1b; 虚拟一个192.168.1.1的网关&#xff1a; 虚拟路由器冗余协议&#xff1a;VRRP 人为调节角色选举 流量分担是可以的&…

【C/C++】常量指针与指针常量的深入解析与区分(什么是const int * 与 int * const ?)

目录 一、前言 二、const 的简单介绍 三、常量指针 &#x1f50d;介绍与分析 &#x1f4f0;小结与记忆口诀 四、指针常量 &#x1f50d;介绍与分析 &#x1f4f0;小结与记忆口诀 五、总结与提炼 六、共勉 一、前言 在【C/C】的编程中&#xff0c;指针与const关键字的组合…

阿珊详解Vue Router的守卫机制

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…