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

【C++】布隆过滤器的概念与特点解析

 

🌈 个人主页:谁在夜里看海.

🔥 个人专栏:《C++系列》《Linux系列》

⛰️ 天高地阔,欲往观之。

目录

00.引入

01.布隆过滤器的概念

特点1:极低的内存消耗

特点2:快速查询

特点3:假阳性误判(禁止删除)

02.布隆过滤器的底层实现


00.引入

上一篇博客介绍了位图这一数据结构,它可以在大量整数中快速查找某一数据是否存在,并且内存占用率很低(例如,查找40亿个整数只需0.5G空间)。博客链接跳转->

那么问题来了,如果我要查找的数据类型是字符串或其他类型,位图还能用吗?

分析:

位图本质上是一种哈希结构,哈希结构在存储元素时是根据元素的哈希值进行存储的,整数在位图中进行存储时,哈希值就是它本身,因此也不会产生哈希冲突;如果字符串想要用位图进行存储的话,需要计算出对应的哈希值,就要通过哈希函数来实现。

但是有一个问题,那就是哈希冲突

最优秀的哈希函数也只能减少产生冲突的可能性,而不能完全避免冲突,那么在位图中,哈希冲突会造成什么后果呢?

例如:字符串“AaAaA”与“BBBBB”通过某哈希函数计算所得的哈希值都是5,事实上,“AaAaA”是存在的元素,而“BBBBB”并不存在,将所有元素存入位图后,bit位5的bool值为1(因为“AaAaA”的哈希值为5)此时就会对“BBBBB”产生误判,即“BBBBB”不存在但是通过位图会判断其存在

当然了,为了应对哈希冲突,我们可以对位图进行扩容(通常是二倍扩容),并更新哈希函数重新计算哈希值来化解冲突,但是这种扩容的方式背离了位图的初衷:高效的内存利用。频繁的扩容必然导致内存的浪费,那么面对字符串的存储问题,该怎么解决呢?下面就要介绍布隆过滤器了。

01.布隆过滤器的概念

哈希冲突的产生原理就是,不同的元素有相同的哈希值,一般来说,一个元素对应一个哈希值,但是布隆(Burton Howard Bloom)告诉你,一个元素可以有多个哈希值:

一个元素经过多个不同哈希函数得到多个哈希值,将这些哈希值都作为该元素的映射,这样就大大减小了哈希冲突的可能性。两个不同的元素要想发生冲突,它们每个哈希值都得相同,并且可以通过增加哈希函数的办法,让这种可能性继续降低。

这是某位大佬总结出来的哈希函数的个数与误判率的关系:

说到底,布隆过滤器还是没办法完全解决哈希冲突的问题,还是会导致误判,但是布隆过滤器的以下特点使其具有重要意义:

特点1:极低的内存消耗

布隆过滤器拥有和位图一样的底层结构,使其使用极少的内存空间就可以支持大量数据的“存在性”查询。

特点2:快速查询

布隆过滤器通过简单的位运算就可以完成元素的“存在性”判断,速度非常之快,这种高效性在实时性要求高的场景(如网络请求过滤、黑名单检测)中尤为有用。

特点3:假阳性误判(禁止删除)

布隆过滤器虽然存在误判,但是可以将误判率控制在一个可以接受的范围内,并且布隆过滤器的误判是假阳性误判,不会存在假阴性误判,解释:

假阳性误判就是,将不存在的元素误判为存在,例如:由于A和B的哈希值全部相同,A存在就会导致B被误判

而假阴性误判就是,将存在的元素误判为不存在,查询某一元素是否存在,就要看它对应的比特位bool值是否都为1,只要该位置的bool值不被修改,就不会造成假阴性误判。元素的插入不会造成修改,但是元素的删除会造成修改。

A与B有一个哈希值7相同,B的删除会使7的bool值置0,这就会导致A被误判为不存在,即假阴性误判,为了避免这种情况发生,布隆过滤器不允许删除元素。

为什么布隆过滤器允许假阳性误判但不允许假阴性误判的产生呢,来看下面这一个场景:

为了方便用户之间的搜索,某应用不允许用户的id重复,现在用布隆过滤器存储着所有用户id的存储情况,用户注册时如果预输入id已存在,则要求更换id(可能预输入id并不存在,但是发生假阳性误判),由于假阴性误判的杜绝,用户的id一定不会相同(已存在的id肯定不会被误判为不存在,而被新用户注册)

02.布隆过滤器的底层实现

布隆过滤器的底层结构与位图相同,只不过前者用多个哈希值表示1个元素,而且后者只用1个哈希值对应1个元素

 // 将which比特位置1
 void set(size_t which)
 {
	 if (which > _bitCount)
	 	return;
	 size_t index = (which >> 5);
	 size_t pos = which % 32;
	 _bit[index] |= (1 << pos); // 将对应bit位改为1
 }
 // 将which比特位置0
 
 void Set(const K& key)
 {
     size_t len = X*N;
     size_t index1 = HashFunc1()(key) % len;
     size_t index2 = HashFunc2()(key) % len;
     size_t index3 = HashFunc3()(key) % len;

     _bs.set(index1);
     _bs.set(index2);
     _bs.set(index3);
 }

哈希函数参考:

struct BKDRHash
{
    size_t operator()(const string& s)
    {
        // BKDR
        size_t value = 0;
        for (auto ch : s)
        {
            value *= 31;
            value += ch;
        }
        return value;
    }
};
struct APHash
{
    size_t operator()(const string& s)
    {
        size_t hash = 0;
        for (long 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 DJBHash
{
    size_t operator()(const string& s)
    {
        size_t hash = 5381;
        for (auto ch : s)
        {
            hash += (hash << 5) + ch;
        }
        return hash;
    }
};

以上就是对布隆过滤器的详细介绍与说明,欢迎指正~

码文不易,还请多多关注支持,这是我持续创作的最大动力!


http://www.kler.cn/a/380495.html

相关文章:

  • EsChatPro 接入国内 DeepSeek 大模型
  • 深入探讨 Go 中的高级表单验证与翻译:Gin 与 Validator 的实践之道20241223
  • kkfileview代理配置,Vue对接kkfileview实现图片word、excel、pdf预览
  • hCaptcha 图像识别 API 对接说明
  • docker部署微信小程序自动构建发布和更新
  • 【Qt】对象树(生命周期管理)和字符集(cout打印乱码问题)
  • 数据结构 之 线索二叉树(七)
  • 如何对数据库的表字段加密解密处理?
  • Maven resrouce下filtering作用说明
  • jupyter notebook的调试
  • 什么情况下,不推荐建立索引?
  • PDF Reader Pro for mac激活版 PDF编辑阅读器
  • gRPC 一种现代、开源、高性能的远程过程调用 (RPC) 可以在任何地方运行的框架
  • 电脑开机显示无信号然后黑屏怎么办?
  • 认识单双链表
  • conda下安装volitility3
  • C++优选算法六 模拟
  • 5G工业网关的主要功能有哪些?天拓四方
  • 单体架构的 IM 系统设计
  • Hadoop简介及单点伪分布式安装
  • 使python输出带上颜色
  • 数据结构与算法教学视频+pdf+刷题手册(python+c+java+javascript)个人分享~
  • FlinkCDC-MYSQL批量写入
  • OceanBase V4.3.3,首个面向实时分析场景的GA版本发布
  • 【漏洞复现】某最新版快递微信小程序系统存在前台任意文件读取漏洞
  • HTML 标签属性——<a>、<img>、<form>、<input>、<table> 标签属性详解