[C++]哈希应用-布隆过滤器快速入门

ops/2024/10/18 5:49:06/

布隆过滤器

布隆过滤器(Bloom Filter)是一个由布隆在1970年提出的概率型数据结构,它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器的主要特点是高效的插入和查询,可以用于检索一个元素是否在一个集合中。

原理:

  • 布隆过滤器是采用位图的方式来记录数据的存在与否
  • 将要存储的数据同时映射到位图的多个节点上
  • 当寻找数据时,只需将要找的数据进行映射,观察全部映射节点的情况,就可以判断数据存在与否

其核心功能如下(此处以数据库使用布隆过滤器为例):

在这里插入图片描述

解释:

对于一个数据的查找,会先去布隆过滤器中进行多个映射点的查询。会有如下情况发生:

  1. 只有当每个映射点都存在(对应位置为1)的时候,才能说明该数据可能存在,下一步就去Database中进一步确认(因为冲突的原因,所以可能某个数据的每个映射点虽然都为1,但是该数据并不存在其中)。
  2. 如果有任何一个映射点不存在(对应位置为0)的时候,就说明该数据并不在存储空间内。

如何选择映射

布隆的核心就是最大限度减少冲突问题,进而在高查找效率的情况下,减少寻找错误的可能性!

现在提出一个问题:如果我只采用一个映射(每个数据只映射到一个位图节点上),由于哈希的局限性以及开辟空间的有限性,就会出现多个数据映射到同一个位置上

所以布隆过滤器就采用了多映射的方案(即一个值映射到多个位图节点上),大大缩小了冲突的可能性

优点:

  1. 多于不同的数据,他们在某个哈希函数下可能映射到了同一个位置,但是对于多个映射全相同的概率极小,这就使得哈希冲突变小
  2. 由于数据的查询一般采用轮询的方式(或者树形结构)导致时间复杂度较大,而布隆过滤器的查询效率是大O(1)(忽略极个别的全冲突情况)

缺点:

  1. 不存在漏报,但存在误报(即对于数据存在的判断有失准确性)。但严格来说该缺点不算大事
  2. 不能在布隆过滤器中删除元素,因为这会影响到别的与该位图节点有关联的数据(但可以改造他,使他可以实现删除操作)

哈希函数的选择:

可以采用BKDRAP等哈希函数

选择的哈希函数的前提是:需要保证映射后的值能合理放到位图中的对应位置上(如果你映射出来一个:164gxap3,那么你该放入位图中的哪个位置呢?是吧)

应用场景

  1. 快速检索元素:布隆过滤器能够快速地检索一个元素是否在一个集合中,而无需访问整个集合。这大大提高了查询效率,尤其是在处理大数据集时。
  2. 网页URL去重:在搜索引擎或爬虫系统中,布隆过滤器可以用于过滤已经访问过的网页URL,避免重复访问和存储。
  3. 垃圾邮件过滤:布隆过滤器可以存储已知的垃圾邮件发送者的地址或特征,当接收到新邮件时,可以通过检查邮件地址或内容是否在布隆过滤器中来判断其是否为垃圾邮件。
  4. 数据库查询优化:在数据库中,布隆过滤器可以用于减少不必要的磁盘查找操作。例如,当查询一个不存在的行或列时,布隆过滤器可以快速判断该数据是否存在于数据库中,从而避免不必要的磁盘访问。
  5. 恶意代码检测:在网络安全领域,布隆过滤器可以用于检测恶意代码的僵尸网络或分析不断变化的市场数据。通过存储已知的恶意代码特征或行为模式,布隆过滤器可以快速判断新发现的代码是否为恶意代码。
  6. 生物信息学应用:布隆过滤器可以用于快速查找DNA测序数据中的基因序列,以及在其他生物学和遗传学领域如蛋白质组学、转录组学和基因组学等进行数据分析。
  7. 非结构化大数据分析:在企业数据分析中,布隆过滤器可以帮助企业快速检测网站中的指定元素(如URL中的关键字、用户搜索的关键字等),从而找出其中的趋势和模式,帮助企业更好地投资和发展。
  8. 机器学习应用:在机器学习领域,布隆过滤器可以用于快速处理海量数据,提取特征并构建模型。由于布隆过滤器的快速查询能力,它可以大大提高模型训练和预测的效率。

代码实现以及成果演示

存储的元素:“hello”, “world”, “你好”, “no thank”, “why”, “4”, “-78”

查找的元素:“world”,“你好”,“早上好”,“no thank”,“8”,“4”,“0”,“-78”

分别采用了BKDR、AP、DJB三个哈希函数

uint64_t BKDR_Hash(const char* str) {uint64_t seed = 131; // 31 131 1313 13131 131313 etc..  uint64_t hash = 0;while (*str) {hash = hash * seed + (*str++);}return hash;
}
uint64_t AP_Hash(const char* str)
{uint64_t hash = 0;int i;for (i = 0; *str; i++){if ((i & 1) == 0){hash ^= ((hash << 7) ^ (*str++) ^ (hash >> 3));}else{hash ^= (~((hash << 11) ^ (*str++) ^ (hash >> 5)));}}return (hash & 0x7FFFFFFF);
}
uint64_t DJB_Hash(const char* str)
{uint64_t hash = 5381;while (*str){hash += (hash << 5) + (*str++);}return (hash & 0x7FFFFFFF);
}
int main()
{bitset<10000> st;vector<string> vstr{"hello", "world", "你好", "no thank", "why", "4", "-78"};for (int i = 0; i < vstr.size(); i++){st.set(BKDR_Hash(vstr[i].c_str()) % st.size());st.set(AP_Hash(vstr[i].c_str()) % st.size());st.set(DJB_Hash(vstr[i].c_str()) % st.size());}vector<string> vfind{"world","你好","早上好","no thank","8","4","0","-78"};for (int i = 0; i < vfind.size(); i++){cout << vfind[i];if (st[BKDR_Hash(vfind[i].c_str()) % st.size()]&& st[AP_Hash(vfind[i].c_str()) % st.size()]&& st[DJB_Hash(vfind[i].c_str()) % st.size()]){cout << " exist" << endl;}else{cout << " not exixt" << endl;}}return 0;
}

查找结果:

world exist
你好 exist
早上好 not exixt
no thank exist
8 not exixt
4 exist
0 not exixt
-78 exist

优化成可实现删除操作

对于前面的布隆过滤器的写法,有一个很明显的问题:对于数据的删除是不可实现的,因为对任意一个数据映射位置的删除,由于哈希冲突,都可能导致影响到别的数据的映射关系

所以我们采用引用计数+扩展位图的方式来实现删除操作,具体操作如下:

采用引用计数的方案,对每一个哈希映射值都采用引用计数。每新增一个映射关系就++,减少一个映射关系就- -

如此一来,对位图也需要有所更改:增大位图空间开销,比如用8个bit表示一个映射关系,而在这8个bit中就可以用引用计数实现0-255的可映射数量


http://www.ppmy.cn/ops/37998.html

相关文章

射频无源器件之耦合器

一. 耦合器的作用 在射频电路中,射频耦合器将一路微波功率按比例分成几路,用于检测或监测信号,如功率测量和波检测,还可改变信号的幅度、相位等特性,以满足不同的通信需求。根据输入与耦合端的功率差,常被分为5dB、6dB、10dB等耦合器。射频耦合器的类型主要包括定向耦合…

uniapp引入vant组件库

在 UniApp 中引入 Vant 组件库的完整步骤通常如下&#xff1a; 安装 Vant&#xff1a; 首先&#xff0c;你需要通过 npm 或 yarn 安装 Vant。打开项目的根目录&#xff0c;然后在命令行中执行以下命令&#xff1a; 使用 npm&#xff1a; npm install vant 或者使用 yarn&…

JUC下的ScheduledThreadPoolExecutor详解

ScheduledThreadPoolExecutor是Java并发编程框架中一个强大且灵活的线程池实现&#xff0c;专为定时与周期性任务而设计。作为ThreadPoolExecutor的子类&#xff0c;它不仅继承了线程池管理的高效与灵活性&#xff0c;还内置了基于优先级队列的延迟任务调度机制&#xff0c;支持…

数据库(MySQL)—— 函数

数据库&#xff08;MySQL&#xff09;—— 函数 字符串函数数值函数日期函数流程函数 我们今天来看MySQL中为我们提供了哪些函数&#xff1a; MySQL中的函数主要分为以下四类&#xff1a; 字符串函数、数值函数、日期函数、流程函数。 字符串函数 函数功能CONCAT(S1, S2, ……

6.Nginx

Nginx反向代理 将前端发送的动态请求有Nginx转发到后端服务器 那为何要多一步转发而不直接发送到后端呢&#xff1f; 反向代理的好处&#xff1a; 提高访问速度&#xff08;可以在nginx做缓存&#xff0c;如果请求的是同样的接口地址&#xff0c;这样就不用多次请求后端&#…

LeetCode763:划分字母区间

题目描述 给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段&#xff0c;同一字母最多出现在一个片段中。 注意&#xff0c;划分结果需要满足&#xff1a;将所有划分结果按顺序连接&#xff0c;得到的字符串仍然是 s 。 返回一个表示每个字符串片段的长度的列表。 …

Unity初级---初识生命周期

1. Awake() &#xff1a;唤醒函数&#xff0c;最先执行的函数&#xff0c;只执行一次&#xff0c;当脚本文件挂载的对象被激活时调用 2. OnEnable() &#xff0c;OnDisable()&#xff1a;当脚本启用和禁用时触发&#xff0c;可执行多次&#xff0c;触发的前提是脚本挂载的对象…

第四十节实现主人公的技能释放功能(二)实现技能按钮

看看我们今天要实现的效果是&#xff0c;当我们按下数字1快捷键&#xff0c;我们的技能按钮会进入倒计时&#xff0c;如下图演示&#xff1a; 一、新建场景和根节点设置 新建场景&#xff0c;选择TextureButton作为根节点&#xff0c;重名为SpellButton&#xff0c;保存场景…