【数据结构】布隆过滤器

news2024/12/23 20:54:26

布隆过滤器的提出

在注册账号设置昵称的时候,为了保证每个用户昵称的唯一性,系统必须检测你输入的昵称是否被使用过,这本质就是一个key的模型,我们只需要判断这个昵称被用过,还是没被用过。

  • 方法一:用红黑树或哈希表将所有使用过的昵称存储起来,当需要判断一个昵称是否被用过时,直接判断该昵称是否在红黑树或哈希表中即可。但红黑树和哈希表最大的问题就是浪费空间,当昵称数量非常多的时候内存当中根本无法存储这些昵称
  • 方法二:用位图将所有使用过的昵称存储起来,虽然位图只能存储整型数据,但我们可以通过一些哈希算法将字符串转换成整型,比如BKDR哈希算法。当需要判断一个昵称是否被用过时,直接判断位图中该昵称对应的比特位是否被设置即可。

位图虽然能够大大节省内存空间,但由于字符串的组合形式太多了,一个字符的取值有256种,而一个数字的取值只有10种,因此无论通过何种哈希算法将字符串转换成整型都不可避免会存在哈希冲突。

这里的哈希冲突就是不同的昵称最终被转换成了相同的整型,此时就可能会引发误判,即某个昵称明明没有被使用过,却被系统判定为已经使用过了,于是就出现了布隆过滤器。

布隆过滤器的概念

布隆过滤器是由布隆(Burton Howard Bloom)在1970年提出的一种紧凑型的、比较巧妙的概率型数据结构,特点是高效地插入和查询。

  • 布隆过滤器其实就是位图的一个变形和延申,虽然无法避免存在哈希冲突,但我们可以想办法降低误判的概率。

  • 当一个数据映射到位图中时,布隆过滤器会用多个哈希函数将其映射到多个比特位,当判断一个数据是否在位图当中时,需要分别根据这些哈希函数计算出对应的比特位,如果这些比特位都被设置为1则判定为该数据存在,否则则判定为该数据不存在。

  • 布隆过滤器使用多个哈希函数进行映射,目的就在于降低哈希冲突的概率,一个哈希函数产生冲突的概率可能比较大,但多个哈希函数同时产生冲突的概率可就没那么大了。

假设布隆过滤器使用三个哈希函数进行映射,那么“张三”这个昵称被使用后位图中会有三个比特位会被置1,当有人要使用“李四”这个昵称时,就算前两个哈希函数计算出来的位置都产生了冲突,但由于第三个哈希函数计算出的比特位的值为0,此时系统就会判定“李四”这个昵称没有被使用过。

在这里插入图片描述

但随着位图中添加的数据不断增多,位图中1的个数也在不断增多,此时就会导致误判的概率增加。

比如“张三”和“李四”都添加到位图中后,当有人要使用“王五”这个昵称时,虽然“王五”计算出来的三个位置既不和“张三”完全一样,也不和“李四”完全一样,但“王五”计算出来的三个位置分别被“张三”和“李四”占用了,此时系统也会误判为“王五”这个昵称已经被使用过了。

在这里插入图片描述

布隆过滤器的特点

  • 布隆过滤器判断一个数据存在可能是不准确的,因为这个数据对应的比特位可能被其他一个数据或多个数据占用了。

  • 布隆过滤器判断一个数据不存在是准确的,因为如果该数据存在那么该数据对应的比特位都应该已经被设置为1了。

如何控制误判率?

  • 很显然,过小的布隆过滤器很快所有的比特位都会被设置为1,此时布隆过滤器的误判率就会变得很高,因此布隆过滤器的长度会直接影响误判率,布隆过滤器的长度越长其误判率越小。

  • 此外,哈希函数的个数也需要权衡,哈希函数的个数越多布隆过滤器中比特位被设置为1的速度越快,并且布隆过滤器的效率越低,但如果哈希函数的个数太少,也会导致误判率变高。

那应该如何选择哈希函数的个数和布隆过滤器的长度呢,有人通过计算后得出了以下关系式:

在这里插入图片描述

其中k为哈希函数个数,m为布隆过滤器长度,n为插入的元素个数,p为误判率。

我们这里可以大概估算一下,如果使用3个哈希函数,即k的值为3, ln2的值我们取0.7,那么m和n的关系大概是m = 4 * n,也就是布隆过滤器的长度应该是插入元素个数的4倍。

布隆过滤器的实现

首先,布隆过滤器可以实现为一个模板类,因为插入布隆过滤器的元素不仅仅是字符串,也可以是其他类型的数据,只有调用者能够提供对应的哈希函数将该类型的数据转换成整型即可,但一般情况下布隆过滤器都是用来处理字符串的,所以这里可以将模板参数K的缺省类型设置为string。

布隆过滤器中的成员一般也就是一个位图,我们可以在布隆过滤器这里设置一个非类型模板参数N,用于让调用者指定位图的长度。

//布隆过滤器
template<size_t N, class K = string, class Hash1 = BKDRHash, class Hash2 = APHash, class Hash3 = DJBHash>
class BloomFilter {
public:
    //...
private:
    bitset<N> _bs;
};

实例化布隆过滤器时需要调用者提供三个哈希函数,由于布隆过滤器一般处理的是字符串类型的数据,因此这里我们可以默认提供几个将字符串转换成整型的哈希函数。

  • 这里选取将字符串转换成整型的哈希函数,是经过测试后综合评分最高的BKDRHash、APHash和DJBHash,这三种哈希算法在多种场景下产生哈希冲突的概率是最小的。
  • 此时本来这三种哈希函数单独使用时产生冲突的概率就比较小,现在要让它们同时产生冲突概率就更小了。

代码如下

struct BKDRHash {
    size_t operator()(const string &s) {
        size_t value = 0;
        for (auto ch: s) {
            value = value * 131 + ch;
        }
        return value;
    }
};

struct APHash {
    size_t operator()(const string &s) {
        size_t value = 0;
        for (size_t i = 0; i < s.size(); i++) {
            if ((i & 1) == 0) {
                value ^= ((value << 7) ^ s[i] ^ (value >> 3));
            } else {
                value ^= (~((value << 11) ^ s[i] ^ (value >> 5)));
            }
        }
        return value;
    }
};

struct DJBHash {
    size_t operator()(const string &s) {
        if (s.empty())
            return 0;
        size_t value = 5381;
        for (auto ch: s) {
            value += (value << 5) + ch;
        }
        return value;
    }
};

布隆过滤器的插入

布隆过滤器当中需要提供一个Set接口,用于插入元素到布隆过滤器当中。插入元素时,需要通过三个哈希函数分别计算出该元素对应的三个比特位,然后将位图中的这三个比特位设置为1即可。

代码如下

void Set(const K &key) {
    //计算出key对应的三个位
    size_t i1 = Hash1()(key) % N;
    size_t i2 = Hash2()(key) % N;
    size_t i3 = Hash3()(key) % N;

    //设置位图中的这三个位
    _bs.set(i1);
    _bs.set(i2);
    _bs.set(i3);
}

布隆过滤器的查找

布隆过滤器当中还需要提供一个Test接口,用于检测某个元素是否在布隆过滤器当中。检测时,需要通过三个哈希函数分别计算出该元素对应的三个比特位,然后判断位图中的这三个比特位是否被设置为1。

  • 只要这三个比特位当中有一个比特位未被设置则说明该元素一定不存在。
  • 如果这三个比特位全部被设置,则返回true表示该元素存在(可能存在误判)。

代码如下

bool Test(const K &key) {
    //依次判断key对应的三个位是否被设置
    size_t i1 = Hash1()(key) % N;
    if (_bs.test(i1) == false) {
        return false;//key一定不存在
    }

    size_t i2 = Hash2()(key) % N;
    if (_bs.test(i2) == false) {
        return false;//key一定不存在
    }

    size_t i3 = Hash3()(key) % N;
    if (_bs.test(i3) == false) {
        return false;//key一定不存在
    }

    return true;//key对应的三个位都被设置,key存在(可能误判)
}

布隆过滤器的删除

布隆过滤器一般不支持删除操作,原因如下:

  • 因为布隆过滤器判断一个元素存在时可能存在误判,因此无法保证要删除的元素确实在布隆过滤器当中,此时将位图中对应的比特位清0会影响其他元素。
  • 此外,就算要删除的元素确实在布隆过滤器当中,也可能该元素映射的多个比特位当中有些比特位是与其他元素共用的,此时将这些比特位清0也会影响其他元素。

如何让布隆过滤器支持删除?

要让布隆过滤器支持删除,必须要做到以下两点:

  1. 保证要删除的元素在布隆过滤器当中。比如刚才的呢称例子当中,如果通过调用Test函数得知要删除的昵称可能存在布隆过滤器当中后,可以进一步遍历存储昵称的文件,确认该昵称是否真正存在。
  2. 保证删除后不会影响到其他元素。可以为位图中的每一个比特位设置一个对应的计数值,当插入元素映射到该比特位时将该比特位的计数值++,当删除元素时将该元素对应比特位的计数值–即可。

可是布隆过滤器最终还是没有提供删除的接口,因为使用布隆过滤器本来就是要节省空间和提高效率的。在删除时需要遍历文件或磁盘中确认待删除元素确实存在,而文件IO和磁盘IO的速度相对内存来说是很慢的,并且为位图中的每个比特位额外设置一个计数器,就需要多用原位图几倍的存储空间,这个代价也是不小的。

布隆过滤器的优点

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

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

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

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

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

  • 使用同一组哈希函数的布隆过滤器可以进行交、并、差运算。

布隆过滤器的缺陷

  • 有误判率,即存在假阳性(False Position),即不能准确判断元素是否在集合中(补救方法:再自建一个白名单,存储可能会误判的数据)
  • 不能获取元素本身。
  • 一般情况下不能从布隆过滤器中删除元素。

布隆过滤器使用场景

使用布隆过滤器的前提是,布隆过滤器的误判不会对业务逻辑造成影响。

比如当我们首次访问某个网站时需要用手机号注册账号,而用户的各种数据实际都是存储在数据库当中的,也就是磁盘上面。

  • 当我们用手机号注册账号时,系统就需要判断你填入的手机号是否已经注册过,如果注册过则会提示用户注册失败。
  • 但这种情况下系统不可能直接去遍历磁盘当中的用户数据,判断该手机号是否被注册过,因为磁盘IO是很慢的,这会降低用户的体验。
  • 这种情况下就可以使用布隆过滤器,将所有注册过的手机号全部添加到布隆过滤器当中,当我们需要用手机号注册账号时,就可以直接去布隆过滤器当中进行查找。
  • 如果在布隆过滤器中查找后发现该手机号不存在,则说明该手机号没有被注册过,此时就可以让用户进行注册,并且避免了磁盘IO。
  • 如果在布隆过滤器中查找后发现该手机号存在,此时还需要进一步访问磁盘进行复核,确认该手机号是否真的被注册过,因为布隆过滤器在判断元素存在时可能会误判。

由于大部分情况下用户用一个手机号注册账号时,都是知道自己没有用该手机号注册过账号的,因此在布隆过滤器中查找后都是找不到的,此时就避免了进行磁盘IO。而只有布隆过滤器误判或用户忘记自己用该手机号注册过账号的情况下,才需要访问磁盘进行复核。
以让用户进行注册,并且避免了磁盘IO。

  • 如果在布隆过滤器中查找后发现该手机号存在,此时还需要进一步访问磁盘进行复核,确认该手机号是否真的被注册过,因为布隆过滤器在判断元素存在时可能会误判。

由于大部分情况下用户用一个手机号注册账号时,都是知道自己没有用该手机号注册过账号的,因此在布隆过滤器中查找后都是找不到的,此时就避免了进行磁盘IO。而只有布隆过滤器误判或用户忘记自己用该手机号注册过账号的情况下,才需要访问磁盘进行复核。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C/C++学习 -- 分组加密算法(DES算法)

数据加密标准&#xff08;Data Encryption Standard&#xff0c;DES&#xff09;是一种对称密钥加密算法&#xff0c;是信息安全领域的经典之作。本文将深入探讨DES算法的概述、特点、原理&#xff0c;以及提供C语言和C语言实现DES算法的代码案例。 一、DES算法概述 DES算法是…

【网络安全---XSS漏洞(1)】XSS漏洞原理,产生原因,以及XSS漏洞的分类。附带案例和payload让你快速学习XSS漏洞

一&#xff0c;什么是XSS漏洞&#xff1f; XSS全称&#xff08;Cross Site Scripting&#xff09;跨站脚本攻击&#xff0c;为了避免和CSS层叠样式表名称冲突&#xff0c;所以改为了XSS&#xff0c;是最常见的Web应用程序安全漏洞之一&#xff0c;位于OWASP top 10 2013/2017年…

idea配置文件属性提示消息解决方案

在项目文件路径下找到你没有属性提示消息的文件 选中&#xff0c;ok即可 如果遇到ok无法确认的情况&#xff1a; 在下图所示位置填写配置文件名称即可

lv7 嵌入式开发-网络编程开发 06 socket套接字及TCP的实现框架

目录 1 socket套接字 1.1 体系结构的两种形式 1.2 几种常见的网络编程接口 1.3 socket套接字 2 socket常用API介绍 2.1 API 2.2 地址族结构体 2.3 套接字类型 2.4 socket套接字 3 TCP通信的实现过程 4 练习 1 socket套接字 1.1 体系结构的两种形式 网络的体系结构 …

想要精通算法和SQL的成长之路 - 二叉树的判断问题(子树判断 | 对称性 | 一致性判断)

想要精通算法和SQL的成长之路 - 二叉树的判断问题 前言一. 相同的树二. 对称二叉树三. 判断子树 前言 想要精通算法和SQL的成长之路 - 系列导航 一. 相同的树 原题链接 这题目典型的递归题&#xff1a; 如果两个节点都是null&#xff0c;我们返回true。如果两个节点一个nul…

【Linux】—— 详解软硬链接

前言&#xff1a; 本期&#xff0c;我将要给大家讲解的是有关 Linux下软硬链接的相关知识&#xff01;&#xff01;&#xff01; 目录 前言 &#xff08;一&#xff09;理解硬链接 1.什么是硬链接 2.创建硬链接 3.硬链接的使用场景 &#xff08;二&#xff09;理解软链接…

麦田物语学习

设置预设 将对一个物体的操作保存下来&#xff0c;直接用于其他的物体&#xff0c;比较省力 当设置好一个物体后点击箭头所指的地方&#xff0c;保存预设&#xff0c;在其他物体的面板里点击预设使用 sprite renderer 图片渲染顺序参考点 修改游戏的渲染方式 修改为按照 y 轴…

操作系统学习笔记2

文章目录 1、进程管理逻辑图2、进程的由来 参考视频&#xff1a; 操作系统 1、进程管理逻辑图 2、进程的由来

芯驰D9评测(2)--系统环境配置连接

linux开发板的软件开发三件套&#xff1a; 建立连接-->建立交叉编译环境-->建立驱动开发环境。 如果我们不涉及镜像的深度定制&#xff0c;只是平台化应用的话 1. 建立串口连接 查看手册&#xff0c; 获取接口定义说明&#xff1a; 板载一共两个端子&#xff0c;三个…

python二次开发CATIA:文字轮廓草图

CATIA V5 版本的草图中&#xff0c;并没有文字轮廓的创建命令。通常的做法是&#xff0c;再Drawing 文件中创建所需文本-->将 Drawing 文件另存为 dwg / dxf 格式-->打开另存的文件&#xff0c;文字已转为轮廓线条-->复制线条并粘贴到草图中。 本例中&#xff0c;基于…

c#+Mysql房屋租赁管理系统

一、引言 随着房价的不断升高&#xff0c;人们对房屋的需求越来越难以满足&#xff0c;对于一些在外地工作的人来说租房子成为了一种常态&#xff0c;因此出现了越来越多的房屋中介&#xff0c;而对于房屋中介而言&#xff0c;管理日益增加租户的信息和房产的信息成了一个至关…

Hono——一个小型,简单且超快的Edges Web框架

Hono - [炎]在日语中的意思是火焰&#x1f525; - 是一个小型&#xff0c;简单且超快的Edges Web框架。它适用于任何JavaScript运行时&#xff1a;Cloudflare Workers&#xff0c;Fastly ComputeEdge&#xff0c;Deno&#xff0c;Bun&#xff0c;Vercel&#xff0c;Netlify&…

【Leetcode】滑动窗口合集

这里写目录标题 209.长度最小的子数组题目思路代码 3. 无重复字符的最长子串&#xff08;medium&#xff09;题目思路 11. 最大连续 1 的个数 III题目思路 1658. 将 x 减到 0 的最⼩操作数题目思路代码 904. 水果成篮题目思路代码 438.找到字符串中所有字母的异位词题目思路代码…

政治与科技

作者&#xff1a;Hal Finney, 1994.1.2 今天政府可以禁书吗&#xff1f;当然不&#xff0c;至少在一个人将其输入或扫描到计算机中之后不能。技术成果是永久性的。政治手段只能作为一种战术武器&#xff0c;在技术解决方案到位之前阻止它们。如果你想改变世界&#xff0c;就不要…

Pytorch目标分类深度学习自定义数据集训练

目录 一&#xff0c;Pytorch简介&#xff1b; 二&#xff0c;环境配置&#xff1b; 三&#xff0c;自定义数据集&#xff1b; 四&#xff0c;模型训练&#xff1b; 五&#xff0c;模型验证&#xff1b; 一&#xff0c;Pytorch简介&#xff1b; PyTorch是一个开源的Python机…

python二次开发CATIA:为选中元素上色

先打开一个零件文档&#xff0c;然后用鼠标选中元素&#xff0c;再运行如下python程序&#xff1a; import win32com.client import pywintypes # 导入pywintypes模块 import random # 启动CATIA应用 catia win32com.client.Dispatch(CATIA.Application) catia.visible1try:…

在OpenWrt中配置使用FTP文件服务

安装&#xff1a;opkg install vsftpd 配置&#xff1a;vim /etc/vsftpd.conf 在其中增加一行&#xff1a;local_root/tmp 重启&#xff1a;service vsftpd restart

基于B2B平台的医疗病历交互系统

目录 前言 一、技术栈 二、系统功能介绍 医院管理 医院注册 医院文章 医生信息 医院注册 医疗安排 院区注册 医院公告 医院工作人员 病人病历 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 进入21世纪&#xff0c;计算机技术迅速向着网络化的、集…

pytorch最后一步安装失败显示false怎么办?

这两天在安装pytorch&#xff0c;可谓是吃了不少苦&#xff0c;安了整整一天才安装好。 本来按照安装步骤&#xff0c;一步一步都进行的很好&#xff0c;可是最后一步却显示false。 我的解决方案是&#xff0c;先更新显卡驱动&#xff08;注意我的是英伟达显卡&#xff0c;安…

什么是向量嵌入?

一、说明 在所有关于生成式AI的讨论中&#xff0c;为生成式AI提供动力背后的概念可能有点压倒性。在这篇文章中&#xff0c;我们将重点介绍一个功能概念&#xff0c;它为人工智能的潜在认知能力提供支持&#xff0c;并为机器学习模型提供学习和成长的能力&#xff1a;向量嵌入。…