当前位置: 首页 > ops >正文

位图与布隆过滤器

前言

在前面两个章节中我们详细讲解了哈希表的实现,今天我们就来看一看哈希的应用,也就是位图与布隆过滤器。我将与题目相结合来分别实现这两种应用。

位图

题目

给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。
本题中数据量太大,把数据遍历一遍时间消耗太大了,存入红黑树或者哈希表中数据太大,在存储时会消耗GB级内存,这种实现方式不够优秀。
有一种方法可以解决此问题,判断数在不在,说明他有两种状态,而在计算机中刚好可以用0 1来表示这两种状态,再仔细想想一个整型int,四个字节,有32个比特位,每个比特位不是0就是1,刚好能满足这个条件。
假设我们有N个数需要查询且 最大数字为N,现在我们只需要开 N/32+1个int大小类型的空间即可,再把每个数字 映射到位图中,如果 存在就把该比特位 设为1,不存在为0。相比最上面的方法,极大的压缩了空间,下面我来画图给大家解释一下。

 比如说有一个数33,该如何考虑存放呢?

1.首先应该先找到在这个数组的第几个整形中。

2.找到在这个整形的第几位。

这类似于进制问题,找到在第几个整形,只需要用33/32,得到1,也就是下标为一的整形

找到在第几位只需要33%32便查到了在第几位。

这就是 33的位置,通过这个例子,我们知道每一个数字都能一一对应一个比特位,这样我们就能准确找到该位置,判断是0还是1来确定存不存在。现在的问题就是如何让那个比特位变成0或者1,通常我们都采用逻辑与和逻辑或来实现。

1.将该位置变为1:


 

我们只需要将此位置与1进行或等,其余部分为0即可,该如何实现呢?其实也很简单,前面我们算出了他在第几个比特位,如在第j位,现在我们只需要将该位置所的整形或等上(1<<j)便可以实现该位置为1,其他位置保持不变。

2.将该位置变为0:

要想让此位置变成0,我们只需要将此位置与0进行与运算,其他位置全为1,不影响其他位,具体方法是将该位置所的整形与等上~(1<<j)。


依照上面的方法将所有数据存入到位图中后,当查找时,只需要在位图中找到该位置,看比特位是否为1即可。需要注意的是,开空间时应该开N/32+1个大小的空间,因为c++中/号是整数除法向下取整。

概念

所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用
来判断某个数据存不存在的。

实现

 具体代码如下:

	template<size_t N>class bitset{public:bitset(){//_bits.resize(N/32+1, 0);_bits.resize((N>>5) + 1, 0);}void set(size_t x){size_t i = x / 32;size_t j = x % 32;_bits[i] |= (1 << j);}void reset(size_t x){size_t i = x / 32;size_t j = x % 32;_bits[i] &= ~(1 << j);}bool test(size_t x){size_t i = x / 32;size_t j = x % 32;return _bits[i] & (1<<j);}private:vector<int> _bits;};

这是简单的位图的实现,有时候还需要进行变形,如,找出海量数据中出现不超过两次的数字。

其实和这题道理一样,还是通过比特位来进行查找,我们可以开两个位图,那么在位图的相应位置会出现00 01 10 11四种情况,插入所有数据后再遍历一遍就可以找到这些数字。

布隆过滤器

解析

位图这个思想可以适用于整形,但是如果数据是字符串string就不太好实现,那么我们在哈希表中的用到的字符串哈希算法便可以很好的起到作用,我们把string通过算法转换为整形就能很好的映射到位图中,但是不同于整形的是,整形是一一对应的,而字符串转换后的值可能还是出现哈希冲突的情况,我们说过,哈希冲突是无法消除的,只能尽可能避免。

比如这种情况apple并不存在,但再查询时发现该位置为1,就会出现误判的情况。(insert的插入导致了该位置为1,但正好apple通过转换后也是映射到这个位置) 

这时布隆便想到了一种方法,我们可以把string通过算法转换为多个不同的整形,实现一对多的映射关系,这样就能大大提高准确率。实际中,通常用三个不同的算法进行转换。

在这种方法下,即使出现了交叉,还可以通过其他的位置来进行判断,这样就能提高准确率。

现在我们来想一个问题,我查询一个值,得到的结果有两种:

一个是存在,一个是不存在,那么在结果的角度看哪种结果是可靠的呢?

 还是拿这个图片来鞭尸,我们发现返回存在这种情况是不可靠的,因为它可能出现映射位置相同的情况,但该字符串并没有在位图中。返回不存在是可靠的,因为如果不存在,查找该位置的比特位为0,说明不可能有值在这里存在过。

实现

现在我们来进行实现,我们用三个比较优秀的算法来对字符串进行处理。如下:

struct BKDRHash
{size_t operator()(const string& key){// BKDRsize_t hash = 0;for (auto e : key){hash *= 31;hash += e;}return hash;}
};struct APHash
{size_t operator()(const string& key){size_t hash = 0;for (size_t i = 0; i < key.size(); i++){char ch = key[i];if ((i & 1) == 0){hash ^= ((hash << 7) ^ ch ^ (hash >> 3));}else{hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));}}return hash;}
};struct DJBHash
{size_t operator()(const string& key){size_t hash = 5381;for(auto ch : key){hash += (hash << 5) + ch;}return hash;}
};

 下面开始我们的过滤器实现

template<size_t N, class K = string,class HashFunc1 = BKDRHash,class HashFunc2 = APHash ,class HashFunc3 = DJBHash>
class BloomFilter
{
public:void Set(const K& key){size_t hash1 = HashFunc1()(key) % N;size_t hash2 = HashFunc2()(key) % N;size_t hash3 = HashFunc3()(key) % N;_bs.set(hash1);_bs.set(hash2);_bs.set(hash3);/*cout << hash1 << endl;cout << hash2 << endl;cout << hash3 << endl << endl;*/}// 一般不支持删除,删除一个值可能会影响其他值// 非要支持删除,也是可以的,用多个位标记一个值,存引用计数void Reset(const K& key);bool Test(const K& key){// 判断不存在是准确的size_t hash1 = HashFunc1()(key) % N;if (_bs.test(hash1) == false)return false;size_t hash2 = HashFunc2()(key) % N;if (_bs.test(hash2) == false)return false;size_t hash3 = HashFunc3()(key) % N;if (_bs.test(hash3) == false)return false;// 存在误判的return true;}private:bit::bitset<N> _bs;
};

判断时,如果有一个位为0,直接返回false,而且是准确的,都不为0返回true,但存在误判。

这就是位图和布隆过滤器的实现,创作不易,恳求点赞关注谢谢谢谢谢谢谢谢

http://www.xdnf.cn/news/8819.html

相关文章:

  • 历年北京邮电大学保研上机真题
  • DAY36打卡@浙大疏锦行
  • c/c++怎样编写可变参数函数.
  • Scratch游戏 | 枪战游戏
  • 鸿蒙开发:了解$$运算符
  • 检索增强生成(RAG)完全入门指南
  • Gartner报告解读《Technical Professionals Need to Track 5 ImportantLLM Developments》
  • 【网络安全】轻量敏感路径扫描工具
  • 54页 @《人工智能生命体 新启点》中國龍 原创连载
  • 07_模型训练篇-Torchvision(中):数据增强,让数据更加多样性
  • 处处可见的FOC驱动电机技术
  • Java集合框架基础知识点全面解析
  • 《仿盒马》app开发技术分享-- 定位获取(端云一体)
  • go1.24 通过汇编深入学习map引入swiss table后的源码
  • orzdba.gz 下载解压使用教程:MySQL/InnoDB 监控命令参数详解与实战技巧
  • 8天Python从入门到精通【itheima】-41~44
  • 基于Deepseek视觉语言模型识别与训练九宫格验证码
  • PrivaZer隐私保护软件:守护隐私,优化系统
  • 【Android】System分区应用自带库与原生库同名问题分析
  • PPO算法详解
  • 第八章:数据幻域 · 状态与响应的涌动之力
  • 【音视频开发】音视频基础概念
  • 技术第一篇:odoo18 的登录认证机制
  • a+b+c+d==0(用哈希表进行优化)
  • 进行性核上性麻痹患者饮食指南:防呛咳、补营养的科学吃法
  • Java NPE为什么不会导致进程崩溃(CoreDump)
  • 同为科技 智能PDU产品选型介绍 EN10/G801FLR
  • 多角色多端状态控制与锁控制
  • Java Web
  • 一周学会Pandas2之Python数据处理与分析-Pandas2数据合并与对比-df.combine_first():填充合并