news 2026/4/15 12:24:06

数据结构:布隆过滤器

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
数据结构:布隆过滤器

数据结构:布隆过滤器

布隆过滤器(Bloom Filter)是一种空间效率极高的概率型数据结构,由霍华德·布隆在1970年提出,用于快速判断一个元素是否存在于一个集合中。它的核心特点是存在误判的可能,但不存在漏判,即:

  • 若判断元素“不存在”,则一定不存在;
  • 若判断元素“存在”,则可能存在(存在一定的误判率);
  • 支持高效的插入查询操作,且不支持删除元素(普通布隆过滤器)。

资料:https://pan.quark.cn/s/43d906ddfa1bhttps://pan.quark.cn/s/90ad8fba8347https://pan.quark.cn/s/d9d72152d3cf

一、布隆过滤器的核心原理

布隆过滤器的底层依赖二进制位数组多个独立的哈希函数,核心流程如下:

1. 初始化

  • 申请一个长度为m的二进制位数组(初始值全为0);
  • 选择k个独立的哈希函数h₁, h₂, ..., h_k,每个哈希函数能将输入元素映射到[0, m-1]范围内的一个整数索引。

2. 插入元素

对于要插入的元素x

  1. 依次通过k个哈希函数计算,得到k个索引值h₁(x), h₂(x), ..., h_k(x)
  2. 将二进制位数组中这k个索引对应的位置置为1

3. 查询元素

对于要查询的元素y

  1. 依次通过k个哈希函数计算,得到k个索引值h₁(y), h₂(y), ..., h_k(y)
  2. 检查位数组中这k个索引对应的位置:
    • 所有位置都是1→ 判定元素y可能存在(存在误判);
    • 任意一个位置是0→ 判定元素y一定不存在

核心特性解释

  • 误判原因:不同元素通过哈希函数计算后,可能会映射到相同的索引位置,导致“哈希冲突”。当一个不存在的元素的k个哈希索引恰好都被其他元素置为1时,就会误判为“存在”。
  • 无漏判原因:只有元素确实插入过,其k个哈希索引才会全部置1;若有一个位置为0,则元素一定没插入过。

二、关键参数与误判率计算

布隆过滤器的性能由三个核心参数决定:

  1. 二进制位数组长度m:位数组越长,误判率越低,但空间占用越高;
  2. 哈希函数个数k:哈希函数越多,误判率越低,但插入和查询的时间复杂度越高;
  3. 集合中预期元素个数n:即布隆过滤器需要存储的元素总数。

误判率公式

布隆过滤器的理论误判率P计算公式为:
P=(1−e−knm)k P = \left(1 - e^{-\frac{kn}{m}}\right)^kP=(1emkn)k
其中e是自然对数的底数。

参数选择最优解

为了最小化误判率P,哈希函数个数k的最优值为:
k=mnln⁡2≈0.693⋅mn k = \frac{m}{n} \ln 2 \approx 0.693 \cdot \frac{m}{n}k=nmln20.693nm
此时的最小误判率为:
Pmin=(12)k=e−k2nm P_{min} = \left(\frac{1}{2}\right)^k = e^{-\frac{k^2 n}{m}}Pmin=(21)k=emk2n

例如:若预期存储n=100000个元素,要求误判率P≤0.01%,则可计算出m≈1917000(约 235KB),k≈13

三、布隆过滤器的实现示例(Python)

importmathimporthashlibclassBloomFilter:def__init__(self,expected_n,error_rate=0.01):""" 初始化布隆过滤器 :param expected_n: 预期存储的元素个数 :param error_rate: 允许的最大误判率 """# 计算二进制位数组长度 mself.m=self._calculate_m(expected_n,error_rate)# 计算最优哈希函数个数 kself.k=self._calculate_k(expected_n,self.m)# 初始化二进制位数组(用整数模拟,bitarray 更高效,这里用列表简化)self.bit_array=[0]*self.m# 哈希函数列表(使用 hashlib 实现多个独立哈希)self.hash_seeds=[iforiinrange(self.k)]def_calculate_m(self,n,p):"""计算位数组长度 m"""m=-(n*math.log(p))/(math.log(2)**2)returnint(math.ceil(m))def_calculate_k(self,n,m):"""计算最优哈希函数个数 k"""k=(m/n)*math.log(2)returnint(math.ceil(k))def_hash(self,value,seed):"""单个哈希函数:将 value 映射到 [0, m-1] 索引"""# 使用 MD5 哈希,结合种子生成不同的哈希值md5=hashlib.md5((str(value)+str(seed)).encode('utf-8'))# 将哈希结果转为整数,取模得到索引returnint(md5.hexdigest(),16)%self.mdefadd(self,value):"""插入元素 value"""forseedinself.hash_seeds:idx=self._hash(value,seed)self.bit_array[idx]=1defcontains(self,value):"""查询元素 value 是否存在"""forseedinself.hash_seeds:idx=self._hash(value,seed)ifself.bit_array[idx]==0:returnFalsereturnTrue# 使用示例if__name__=="__main__":# 预期存储 10000 个元素,误判率 1%bf=BloomFilter(expected_n=10000,error_rate=0.01)# 插入元素test_set=[f"element_{i}"foriinrange(10000)]forelemintest_set:bf.add(elem)# 测试存在的元素(应返回 True)print(bf.contains("element_123"))# True# 测试不存在的元素(大概率返回 False,小概率误判为 True)print(bf.contains("nonexistent_element"))# False(或 True,误判)# 输出参数print(f"位数组长度 m:{bf.m}")print(f"哈希函数个数 k:{bf.k}")

四、布隆过滤器的特性

1. 优点

  • 空间效率极高:相比哈希表、红黑树等结构,布隆过滤器仅用二进制位数组存储,空间复杂度为O(m),远低于其他结构;
  • 时间效率高:插入和查询的时间复杂度均为O(k)k为哈希函数个数,通常是常数),效率极高;
  • 支持并行操作:哈希函数之间相互独立,插入和查询可并行执行;
  • 无数据存储:布隆过滤器不存储元素本身,仅存储二进制位,适合敏感数据场景(如密码黑名单)。

2. 缺点

  • 存在误判率:无法 100% 准确判断元素存在,误判率与mkn相关;
  • 不支持删除操作:普通布隆过滤器删除元素会导致误判率升高(多个元素共享同一组二进制位);
  • 无法获取元素本身:布隆过滤器仅记录“存在性”,无法存储元素的额外信息。

3. 改进方案

  • 计数布隆过滤器:将二进制位数组改为计数器数组(每个位置存储整数),插入时计数器加 1,删除时减 1,解决删除问题,但空间占用更高;
  • 分层布隆过滤器:将多个布隆过滤器分层,用于处理动态数据集,降低误判率;
  • 布隆过滤器联盟:多个布隆过滤器协同工作,适用于分布式系统。

五、布隆过滤器的典型应用

  1. 缓存穿透防护

    • 场景:Redis 缓存中,若查询一个不存在的 key,请求会穿透到数据库,导致数据库压力过大;
    • 方案:用布隆过滤器存储所有缓存的 key,查询前先通过布隆过滤器判断 key 是否存在,不存在则直接返回,避免穿透。
  2. 黑名单过滤

    • 场景:垃圾邮件过滤、恶意 IP 拦截、违禁词检测;
    • 方案:将黑名单中的元素存入布隆过滤器,快速判断新元素是否在黑名单中。
  3. 分布式系统去重

    • 场景:分布式爬虫去重、分布式任务调度去重;
    • 方案:多个节点共享一个布隆过滤器,判断任务/URL 是否已被处理,避免重复执行。
  4. 数据库查询优化

    • 场景:判断一个值是否存在于数据库的大表中;
    • 方案:将大表的主键存入布隆过滤器,查询前先判断,不存在则直接返回,减少数据库查询次数。
  5. 网络爬虫 URL 去重

    • 场景:爬虫爬取 URL 时,避免重复爬取相同页面;
    • 方案:用布隆过滤器存储已爬取的 URL,新 URL 先查询布隆过滤器,存在则跳过。

六、布隆过滤器与其他数据结构的对比

数据结构空间复杂度插入时间复杂度查询时间复杂度支持删除误判率适用场景
布隆过滤器O(m)O(k)O(k)不支持(普通版)海量数据的存在性判断
哈希表O(n)O(1)(平均)O(1)(平均)支持需存储元素及额外信息
红黑树O(n)O(log n)O(log n)支持有序数据的增删改查
位图(BitMap)O(m)O(1)O(1)支持整数集合的存在性判断(无哈希冲突)

布隆过滤器的核心优势是极致的空间效率,适合“允许少量误判、不支持删除、仅需存在性判断”的场景;若需精确判断或存储元素信息,则需选择哈希表等结构。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/12 10:23:24

终曲:NOIP2025游记

手 ymx,ID:docxjun。退役了。以下是他在 Team:HLOI 服役期间所有的成就:CSP-J2022 1CSP-J2023 1CSP-S2023 2CSP-S2024 1CSP-S2025 1NOIP2025 ?兜兜转转,还是到这个时候了。再见OI。2022.4-2025.11.29。Day -1「自主复习」带给我的…

作者头像 李华
网站建设 2026/3/29 23:39:20

防腐涂料企业

海洋涂料:防腐涂料企业的技术创新与市场前景分析引言在当今工业领域,防腐涂料企业扮演着至关重要的角色。随着海洋经济的快速发展,海洋涂料作为防腐涂料的重要组成部分,其技术和市场正经历着深刻的变革。防腐涂料企业如何把握机遇…

作者头像 李华
网站建设 2026/4/7 3:10:55

TestDisk数据恢复实战:从分区丢失到文件找回的完整指南

TestDisk数据恢复实战:从分区丢失到文件找回的完整指南 【免费下载链接】testdisk TestDisk & PhotoRec 项目地址: https://gitcode.com/gh_mirrors/te/testdisk 当硬盘分区突然消失,重要文件不翼而飞,那种焦虑感足以让人崩溃。但…

作者头像 李华
网站建设 2026/4/15 12:48:15

磁链观测器的探索之旅:从仿真到闭环代码实现

磁链观测器(仿真+闭环代码参考文档) 1.仿真采用simulink搭建,2018b版本 2.代码采用Keil软件编译,思路参考vesc中使用的方法,自己编写的代码能够实现0速闭环启动,并且标注有大量注释,方便学习。 …

作者头像 李华
网站建设 2026/4/15 12:52:11

Java毕设项目推荐-基于JAVA/Springboot的学院校内订餐系统设计与实现基于JAVA的高校校园点餐系统基于JAVA的学院校内订餐系统的实现【附源码+文档,调试定制服务】

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

作者头像 李华