C++哈希hash:位图、布隆过滤器的实现及应用

一、位图实现

1.1位图的原理

所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用
来判断某个数据存不存在的。 当我们想查找某一个数据是否存在或者是否处于某种状态时,相比于直接对存放数据的容器进行遍历查找,与原存放数据的容器所建立映射关系的位图来进行位运算所查找的方式相比,很明显是位图更加快速便捷的。
这里首先实现了单个位图的增删查,然后以其为基础构造出一个内部包含两个位图用于允许一个位置被映射多次的two_bit_set。
#pragma once
#include<iostream>
#include<vector>
#include<assert.h>
using namespace std;
namespace gazbitset
{template<size_t N>class bitset{public:bitset(){_bits.resize(N/32+1,0);}//将x映射的位置标记成1void set(size_t x){assert(x <= N);size_t i = x / 32;size_t j = x % 32;_bits[i] |= (1 << j);}// 把x映射的位标记成0void reset(size_t x){assert(x <= N);size_t i = x / 32;size_t j = x % 32;_bits[i] &= ~(1 << j);}//查找一个值是否在位图中,在就返回1,不在返回0bool test(size_t x){assert(x <= N);size_t i = x / 32;size_t j = x % 32;return _bits[i] & (1 << j);}private:vector<int> _bits;};template<size_t N>class two_bit_set{public:void set(size_t x){if (b1.test(x) == false && b2.test(x) == false){b2.set(x);}else if (b1.test(x) == false && b2.test(x) == true){b1.set(x);b2.reset(x);}}//检测只出现一次的数bool testone(size_t x){if (b1.test(x) == false && b2.test(x)==true){return true;}return false;}private:bitset<N> b1;bitset<N> b2;};
}

1.2位图应用

1. 快速查找某个数据是否在一个集合中。
2. 排序 + 去重。
3. 求两个集合的交集、并集等。
4. 操作系统中磁盘块标记。

二、布隆过滤器

当我们在各大游戏或社交平台注册账号时,有的平台昵称往往是不允许重复的,那当输入昵称时,系统是如何立马就能进行判断查找出是否存在重复的呢?

根据所学知识,通常可以想到两种方法:

1. 用哈希表存储用户记录,缺点:浪费空间
2. 用位图存储用户记录,缺点:位图一般只能处理整形,如果内容编号是字符串,就无法处理
了。
两种方法各有优缺,但如果将哈希和位图结合起来,就可以完美的解决此问题,此时就可以得到一种全新的结构:布隆过滤器。
布隆过滤器概念
布隆过滤器是由布隆(Burton Howard Bloom)在1970年提出的 一种紧凑型的、比较巧妙的概率型数据结构,特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”,它是用多个哈希函数,将一个数据映射到位图结构中。此种方式不仅可以提升查询效率,也可以节省大量的内存空间
ef5bfde119794d8b8ae55cb19e60406e.png

 2.1布隆过滤器的原理

讲述布隆过滤器的原理之前,我们先思考一下,通常你判断某个元素是否存在用的是什么?应该大多数人会回答 HashMap 吧,确实可以将值映射到 HashMap 的 Key,然后可以在 O(1) 的时间复杂度内返回结果,效率奇高。但是 HashMap 的实现也有缺点,例如存储容量占比高,考虑到负载因子的存在,通常空间是不能被用满的,而一旦你的值很多例如上亿的时候,那 HashMap 占据的内存大小就变得很可观了。

还比如说你的数据集存储在远程服务器上,本地服务接受输入,而数据集非常大不可能一次性读进内存构建 HashMap 的时候,也会存在问题。

2.2布隆过滤器的查找

9f7a93c5bae944e497666675b213001c.png

 布隆过滤器是一个 bit 向量或者说 bit 数组,如果我们要映射一个值到布隆过滤器中,我们需要使用多个不同的哈希函数生成多个哈希值,并对每个生成的哈希值指向的 bit 位置 1,例如针对值 “C+五条” 通过三个不同的哈希函数分别生成了哈希值 1、4、7,则上图转变为:

c0417d8f05414639b320781913422064.png

我们现在再存一个值 “CSDN”,如果哈希函数返回 3、4、8 的话,图继续变为: 

801916c3ab424b74b16e3c12e3ea7079.png

值得注意的是,4 这个 bit 位由于两个值的哈希函数都返回了这个 bit 位,因此它被覆盖了。现在我们如果想查询 “dianping” 这个值是否存在,哈希函数返回了 1、5、8三个值,结果我们发现 5 这个 bit 位上的值为 0,说明没有任何一个值映射到这个 bit 位上,因此我们可以很确定地说 “dianping” 这个值不存在。而当我们需要查询 “baidu” 这个值是否存在的话,那么哈希函数必然会返回 1、4、7,然后我们检查发现这三个 bit 位上的值均为 1,那么我们可以说 “baidu” 存在了么?答案是不可以,只能是 “baidu” 这个值可能存在。

这是为什么呢?答案跟简单,因为随着增加的值越来越多,被置为 1 的 bit 位也会越来越多,这样某个值 “taobao” 即使没有被存储过,但是万一哈希函数返回的三个 bit 位都被其他值置位了 1 ,那么程序还是会判断 “taobao” 这个值存在。

注意:布隆过滤器如果说某个元素不存在时,该元素一定不存在,如果该元素存在时,该元素可
能存在,因为有些哈希函数存在一定的误判。

2.2布隆过滤器的删除

布隆过滤器不能直接支持删除工作,因为在删除一个元素时,可能会影响其他元素。
比如:删除上图中"tencent"元素,如果直接将该元素所对应的二进制比特位置0,“baidu”元素也被
删除了,因为这两个元素在多个哈希函数计算出的比特位上刚好有重叠。
一种支持删除的方法:将布隆过滤器中的每个比特位扩展成一个小的计数器,插入元素时给k个计
数器(k个哈希函数计算出的哈希地址)加一,删除元素时,给k个计数器减一,通过多占用几倍存储
空间的代价来增加删除操作。
缺陷
1. 无法确认元素是否真正在布隆过滤器中
2. 存在计数回绕

2.3如何选择哈希函数个数和布隆过滤器长度

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

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

2208e397dc4f4cbcb7555add8e164d3b.png

三、布隆过滤器的实现

#pragma once
#include<bitset>
#include<string>
#include<iostream>
using namespace std;
struct HashFuncBKDR//第一个哈希函数
{// BKDRsize_t operator()(const string& s){size_t hash = 0;for (auto ch : s){hash *= 131;hash += ch;}return hash;}
};struct HashFuncAP//第二个哈希函数
{// APsize_t operator()(const string& s){size_t hash = 0;for (size_t i = 0; i < s.size(); i++){if ((i & 1) == 0) // 偶数位字符{hash ^= ((hash << 7) ^ (s[i]) ^ (hash >> 3));}else              // 奇数位字符{hash ^= (~((hash << 11) ^ (s[i]) ^ (hash >> 5)));}}return hash;}
};struct HashFuncDJB//第三个哈希函数
{// DJBsize_t operator()(const string& s){size_t hash = 5381;for (auto ch : s){hash = hash * 33 ^ ch;}return hash;}
};template<size_t N,class K=string,class Hash1 = HashFuncBKDR,class Hash2 = HashFuncAP,class Hash3 = HashFuncDJB>
class bloomfilter
{
public:void set(const K& key){size_t hash1 = Hash1()(key) % M;size_t hash2 = Hash2()(key) % M;size_t hash3 = Hash3()(key) % M;_bs->set(hash1);_bs->set(hash2);_bs->set(hash3);}bool Test(const K& key){size_t hash1 = Hash1()(key) % M;if (_bs->test(hash1) == false)return false;size_t hash2 = Hash2()(key) % M;if (_bs->test(hash2) == false)return false;size_t hash3 = Hash3()(key) % M;if (_bs->test(hash3) == false)return false;return true; // 存在误判(有可能3个位都是跟别人冲突的,所以误判)}
private:static const size_t M = 10 * N;std::bitset<M>* _bs = new std::bitset<M>;
};

布隆过滤器的优缺点

优点:

1. 增加和查询元素的时间复杂度为:O(K), (K为哈希函数的个数,一般比较小),与数据量大小无关。
2. 哈希函数相互之间没有关系,方便硬件并行运算。
3.布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势。
4.在能够承受一定的误判时,布隆过滤器比其他数据结构有这很大的空间优势。
5. 数据量很大时,布隆过滤器可以表示全集,其他数据结构不能。
6. 使用同一组散列函数的布隆过滤器可以进行交、并、差运算。
缺点:
1. 有误判率,即存在假阳性(False Position),即不能准确判断元素是否在集合中(补救方法:再
建立一个白名单,存储可能会误判的数据)。
2. 不能获取元素本身。
3. 一般情况下不能从布隆过滤器中删除元素 。
4. 如果采用计数方式删除,可能会存在计数回绕问题。

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

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

相关文章

Redis是单线程还是多线程?(面试题)

1、Redis5及之前是单线程版本 2、Redis6开始引入多线程版本&#xff08;实际上是 单线程多线程 版本&#xff09; Redis6及之前版本&#xff08;单线程&#xff09; Redis5及之前的版本使用的是 单线程&#xff0c;也就是说只有一个 worker队列&#xff0c;所有的读写操作都要…

最新2024年增强现实(AR)营销指南(完整版)

AR营销是新的最好的东西&#xff0c;就像元宇宙和VR营销一样。利用AR技术开展营销活动可以带来广泛的利润优势。更不用说&#xff0c;客户也喜欢AR营销&#xff01; 如果企业使用AR&#xff0c;71%的买家会更多地购物。40%的购物者准备在他们可以在AR定制的产品上花更多的钱。…

【nodejs ubuntu】nodejs版本过老的更新方法

使用apt方法安装的node.js版本过于老了&#xff0c;以至于我没法用npm下载hexo 下面是更新方法 参考了这篇文章 然后就可以成功安装了

【计算机网络】物理层

文章目录 第二章 物理层一、 物理层的基本概念1. 物理层接口特性 二、数据通信基础1. 典型的数据通信模型2. 数据通信相关术语3. 设计数据通信系统要考虑的3个问题4. 三种通信方式5. 串行传输&并行传输6. 同步传输&异步传输7. 码元8. 数字通信系统数据传输速率的两种表…

FFmpeg拉取RTSP流并定时生成10秒短视频

生成效果: 视频时长为10秒 生成格式为FLV 输出日志: 完整实现代码如下: 需要在Mac和终端先安装FFmpeg brew install ffmpeg CMake文件配置: cmake_minimum_required(VERSION 3.27) project(ffmpeg_open_stream) set(CMAKE_CXX_STANDARD 17)#头文件包目录 include_director…

C语言牛客网BC-37 牛牛的圆(求面积)

题目如下 代码实现 #include<stdio.h> int main() { float r 0;float s 0;scanf("%f",&r);s 3.14*r*r;printf("%.2f",s);return 0; } 创作不易&#xff0c;点点关注&#xff0c;感谢支持&#xff01;&#xff01;&#xff01;

IDEA设置代码自动提示不区分大小写

点击File–>Settings–>Editor --> General --> Code Completion&#xff0c;取消勾选Match case&#xff0c;即可实现代码自动提示不区分大小写

利用RWKV-Runner初步感受一下ai的世界

最近又听到群里的高手在讨论RWKV-Runner&#xff0c;于是没忍住&#xff0c;就想试试&#xff0c;没想到第一关就卡住了。 从群里大咖上传的RWKV-Runner_windows_x64.exe文件开始吧&#xff0c;又找了个虚拟机&#xff0c;直接放在桌面上运行一下&#xff0c;结果就跳出一堆文…

Godot 学习笔记(5):彻底的项目工程化,解决GodotProjectDir is null+工程化范例

文章目录 前言GodotProjectDir is null解决方法解决警告问题根本解决代码问题测试引用其实其它库的输出路径无所谓。 工程化范例环境命名规范Nuget项目结构架构代码ISceneModelIOC服务 测试GD_Extension 通用扩展TestUtils GD_ProgramTestServiceMainSceneModel Godot对应的脚本…

Godot.NET C# 工程化开发(1):通用Nuget 导入+ 模板文件导出,包含随机数生成,日志管理,数据库连接等功能

文章目录 前言Github项目地址&#xff0c;包含模板文件后期思考补充项目设置编写失误环境visual studio 配置详细的配置看我这篇文章 Nuget 推荐NewtonSoft 成功Bogus 成功Github文档地址随机生成构造器生成构造器接口(推荐) 文件夹设置Nlog 成功&#xff01;Nlog.configNlogHe…

C++初阶:STL容器list的使用与初版自实现

目录 1. list的接口与使用1.1 默认成员函数1.2 迭代器与容量相关成员函数1.3 存储数据操作相关成员函数1.4 其他list操作成员函数 2. list的自实现2.1 list的自实现功能2.2 list的结点结构2.3 list的迭代器2.3 list的结构2.4 list迭代器的运算符重载2.5 list的成员函数 3. cons…

专题二_滑动窗口(1)

目录 209. 长度最小的子数组 解析 题解 3. 无重复字符的最长子串 解析 题解 1004. 最大连续1的个数 III 解析 题解 209. 长度最小的子数组 209. 长度最小的子数组 - 力扣&#xff08;LeetCode&#xff09; 解析 题解 class Solution { public:int minSubArrayLen(int…

Medium 级别 DOM 型 XSS 攻击演示(附链接)

环境准备 DVWA 靶场https://eclecticism.blog.csdn.net/article/details/135834194?spm1001.2014.3001.5502 测试 打开 DVWA 靶场并登录&#xff0c;找到 DOM 型 XSS 页面&#xff08;笔者这里是 Medium 级别&#xff09; 跟 Low 级别一样&#xff0c;直接上手 <script…

vue多语言包i18n

1.安装 如果是vue2直接安装8.2.1版本&#xff0c;否则会出现版本不匹配的错误 npm install vue-i18n8.2.1 --save2.文件编辑 在src目录下创建文件 en.js export const h {system: "Background management system",loginOut:"LoginOut",LayoutSet:Layout …

双通道内存@DDR5多通道内存

文章目录 多通道内存DDR4及以前的内存的双通道DDR5往后的双通道和多通道半位宽4通道组合 其他组合测试 DDR5介绍概览重要Features特点 总结 多通道内存 DDR4及以前的内存的双通道 双通道内存是一种内存架构设计&#xff0c;通过在主板上配置两个或多个独立且同时工作的内存控制…

MySQL高阶SQL语句(二)

文章目录 MySQL高阶SQL语句&#xff08;二&#xff09;一、MySQL常用查询1、子查询1.1 语法1.1.1 结合select语句查询1.1.2 结合insert语句查询1.1.3 结合update语句查询1.1.4 结合delete语句查询1.1.5 在in前面添加not1.1.6 exists关键字 2、别名 二、MySQL视图1、视图介绍1.1…

【uniapp】uniapp实现免密登录

文章目录 一、概要二、整体架构流程三、技术名词解释四 、技术细节1.存取token有效期&#xff1f;2.使用setStorageSync而不使用setStorage&#xff1f;3.使用onLaunch而不使用全局路由&#xff1f; 一、概要 打开一个网页或小程序的时候&#xff0c;我们有时候会自动进入主页…

那些王道书里的题目-----计算机网络篇

注&#xff1a;仅记录个人认为有启发的题目 p155 34.下列四个地址块中&#xff0c;与地址块 172.16.166.192/26 不重叠&#xff0c;且与172.16.166.192/26聚合后的地址块不会引入多余地址的是&#xff08;&#xff09; A.172.16.166.192/27 B.172.16.166.128/26 …

MySQL高级sql语句

目录 一、子查询 1.1查询分数大于80的记录 1.2将test3里的记录全部删除&#xff0c;重新插入class表的记录 ​编辑1.3将meixi的分数改为20 1.4删除分数大于80的记录 1.5删除分数不是大于等于80的记录 1.6查询如果存在分数等于80的记录则计算class的字段数 2.子查询&…

cuda python课程中“使用 Numba 的 CUDA Python 的自定义核函数和内存管理“一个大坑

总觉得自己的算法没问题&#xff0c;最终还是测试结果不符。 错误出现在一个很顺眼的位置。 解决方案 在最终测验阶段有一个看起来没问题不需要任何修改的Cell&#xff0c;就是这一句&#xff0c;看起来没什么毛病 d_histogram_out cuda.device_array_like(histogram_out)需…