【C++】:STL详解 —— 布隆过滤器

目录

布隆过滤器的概念

布隆过滤器的优点 

布隆过滤器的缺点

布隆过滤器使用场景

布隆过滤器的实现


布隆过滤器的概念

布隆过滤器(Bloom Filter) 是一种空间效率极高的概率型数据结构,用于快速判断一个元素是否属于某个集合。其核心特点包括:

  • 空间高效:基于位数组(bit array)实现,占用内存远小于传统数据结构(如哈希表)。

  • 概率性判断:可能返回“可能存在”(存在误判,即假阳性),但绝不会返回“肯定不存在”(无假阴性)。

  • 不可删除:标准布隆过滤器不支持元素删除,但改进版(如计数布隆过滤器)通过替换位为计数器可实现删除功能。

工作原理

  1. 数据结构

    • 使用一个长度为 m 的位数组(bit array),初始全为0。

    • 选择 k 个独立的哈希函数,每个函数将元素映射到位数组的某个位置。

  2. 插入元素

    • 对元素进行 k 次哈希计算,得到 k 个位下标。

    • 将位数组中这 k 个位置的值设为1。

  3. 查询元素

    • 对元素进行 k 次哈希计算,检查对应的 k 个位是否均为1:

      • 若有一位为0:元素一定不存在(无假阴性)。

      • 若全为1:元素可能存在(存在假阳性,即误判)。

起源: 

  • 提出者与时间:由 Burton Howard Bloom 在1970年提出,旨在解决大规模数据下的成员查询问题。

  • 背景:在早期计算机系统中,内存资源极其有限,传统方法(如哈希表)无法高效处理海量数据。布隆过滤器通过牺牲一定准确性,换取了空间和时间的显著优化。

布隆过滤器的优点 

布隆过滤器(Bloom Filter)的核心优势在于通过概率性设计空间效率时间效率业务容忍度之间达到最佳平衡

一、空间效率极高(核心优势)

比特级存储

  • 使用位数组(bit array)存储数据,每个元素仅占用若干比特位(而非存储完整数据)。

对比示例

数据结构存储1亿元素所需内存存储方式
哈希表(链地址法)~3.2GB存储完整字符串+指针
红黑树~4.8GB字符串+平衡树节点元数据
布隆过滤器114MB (1%误判率)仅存储哈希映射的比特位

数学优化空间

  • 通过公式 ​ m= -\frac{n\ln \epsilon }{\left ( \ln 2 \right )^{2}} 可精确计算所需内存,避免资源浪费。
    • k:哈希函数个数,m:布隆过滤器长度
    • n:已插入元素的数量,\epsilon:误判率
  • 例如:1亿用户昵称判重,1%误判率仅需114MB,而哈希表需要3.2GB,内存节省28倍

二、查询与插入速度极快

  1. 时间复杂度

    • 插入和查询均为 O(k),k 为哈希函数数量(通常 k=5∼10),接近常数时间 O(1)。

    • 对比传统方案

      数据结构插入耗时查询耗时
      哈希表O(1)O(1)
      红黑树O(log n)O(log n)
      布隆过滤器O(k)O(k)

三、误判率可控且单向安全

  1. 数学可控性

    • 误判率公式 \epsilon\approx \left ( 1-e^{-kn/m} \right )^{k},通过调整 m(位数组大小)和 k(哈希函数数量)以及 n(已插入元素的数量),可精确控制误判率(如1%、0.1%)。

    • 参数调优工具化:可直接使用在线计算器(如Bloom Filter Calculator)生成最优参数。

  2. 业务安全性

    • 零假阴性(False Negative):若元素存在,布隆过滤器绝不会漏判。

    • 假阳性(False Positive):误判仅导致额外校验,不影响最终业务正确性。

灵活的变体与扩展性

  1. 支持功能扩展

    变体类型核心改进适用场景
    计数布隆过滤器用计数器替代比特位,支持删除操作动态数据集(如实时黑名单)
    可扩展布隆过滤器动态添加新位数组层,支持容量扩容数据量持续增长的系统
    压缩布隆过滤器使用Roaring Bitmap压缩稀疏位数组内存敏感场景

布隆过滤器的缺点

一、存在误判率(假阳性)

  • 核心问题
    布隆过滤器可能将不存在的元素误判为存在,误判率由公式  \epsilon\approx \left ( 1-e^{-kn/m} \right )^{k} 决定,无法完全消除。

  • 实际影响

    • 场景限制:在需要绝对准确性的领域(如金融交易、密码验证),误判可能导致严重后果。

    • 二次校验需求:需结合数据库等权威存储进行二次确认,增加系统复杂度。

    • 示例:若误判率设为1%,每100次查询可能有1次误判,需额外访问数据库。

二、不支持删除操作(标准版本)

  • 原因
    多个元素可能共享相同的位,删除一个元素可能影响其他元素的判断。

  • 解决方案与代价

    方案原理代价
    计数布隆过滤器用计数器替代位数组,记录哈希命中次数内存增加4~8倍(每个计数器占4位)
    定期重建周期性地重新构建过滤器维护成本高,可能导致服务中断
  • 示例
    计数布隆过滤器存储1亿元素需约456MB(原标准版114MB),内存占用显著上升。

三、功能局限性

  • 仅支持存在性判断
    无法获取元素值、出现次数或其他元数据,应用场景受限。

    • 示例
      无法统计昵称使用频率,也无法实现黑白名单的优先级区分。

四、需预先确定数据规模

  • 参数设计挑战
    位数组大小 m 和哈希函数数量 k 需基于预期元素数量 n 和误判率 ϵ 提前计算。若实际插入元素数 n′ ≫ n,误判率急剧上升。

  • 动态调整方案

    方案原理代价
    可扩展布隆过滤器分层叠加多个布隆过滤器内存碎片化,查询需遍历多层
    动态扩容按需分配新位数组并迁移数据迁移期间性能下降,实现复杂
  • 示例
    若预期 n=1亿,实际插入 2亿 元素,误判率可能从1%升至 13%(位数组未扩容时)。

布隆过滤器使用场景

一、缓存系统优化

1. 缓存穿透防护

  • 问题:恶意请求大量不存在的数据,绕过缓存直接冲击数据库。

  • 解决方案

    • 在缓存层(如Redis)前置布隆过滤器,存储所有合法键。

    • 请求到达时,先通过布隆过滤器判断键是否存在:

  • 效果
    • 减少99%以上的无效数据库查询(假设误判率1%)。

    • 案例:Twitter使用布隆过滤器拦截不存在的推文ID查询。

二、数据库与存储系统

1. 查询预过滤

  • 场景:减少磁盘IO(如LSM-Tree中的SSTable查询)。

  • 实现

    • 每个SSTable文件附加一个布隆过滤器。

    • 查询时先检查过滤器,仅在可能存在时访问磁盘。

    • 案例:Apache Cassandra为每个SSTable维护布隆过滤器,减少90%的磁盘读取。

2. 分布式数据同步

  • 场景:跨节点同步数据前预判差异。

  • 实现

    • 各节点维护本地数据的布隆过滤器。

    • 同步时交换过滤器,仅传输可能缺失的数据。

    • 案例:IPFS使用布隆过滤器优化DHT网络中的数据同步。

三、网络与安全

1. 恶意URL/内容过滤

  • 场景:快速拦截已知恶意请求。

  • 实现

    • 本地存储恶意URL的布隆过滤器(如浏览器安全插件)。

    • 匹配成功时触发详细检测,避免全量数据存储。

    • 案例:Chrome浏览器用布隆过滤器预筛恶意网址。

2. 密码字典防护

  • 场景:阻止用户使用弱密码。

  • 实现

    • 将常见弱密码存入布隆过滤器。

    • 用户设置密码时快速判断是否在弱密码集中(允许误判,后续人工审核)。

 

布隆过滤器的实现

#include <iostream>
#include <bitset>
#include <string>
#include <cmath>/*** @brief BKDR哈希函数* 经典字符串哈希算法,通过累乘素数种子实现* 种子通常选择31/131/1313等质数*/
struct BKDRHash {size_t operator()(const std::string& s) {size_t value = 0;for (auto ch : s) {value = value * 131 + ch; // 131为常用素数种子}return value;}
};/*** @brief AP哈希函数* Arash Partow设计的哈希算法,通过位运算混合字符* 交替使用不同的位运算策略增强散列性*/
struct APHash {size_t operator()(const std::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;}
};/*** @brief DJB哈希函数* Daniel J. Bernstein提出的哈希算法* 初始值5381为魔法数,通过左移操作混合字符*/
struct DJBHash {size_t operator()(const std::string& s) {if (s.empty()) return 0;size_t value = 5381; // 初始魔法值for (auto ch : s) {value += (value << 5) + ch; // value * 33 + ch}return value;}
};/*** @brief 布隆过滤器模板类* @tparam N 位数组大小,需根据预期数据量计算得出* @tparam K 元素类型,默认为std::string* @tparam Hash1 第一个哈希函数策略* @tparam Hash2 第二个哈希函数策略* @tparam Hash3 第三个哈希函数策略*/
template<size_t N, class K = std::string,class Hash1 = BKDRHash,class Hash2 = APHash,class Hash3 = DJBHash>
class BloomFilter {
public:/*** @brief 插入元素* @param key 要插入的元素*/void Set(const K& key) {size_t i1 = Hash1()(key) % N; // 计算哈希位置1size_t i2 = Hash2()(key) % N; // 计算哈希位置2size_t i3 = Hash3()(key) % N; // 计算哈希位置3_bs.set(i1);  // 设置位数组_bs.set(i2);_bs.set(i3);}/*** @brief 检查元素是否存在* @param key 要检查的元素* @return true 可能存在(存在误判概率)* @return false 绝对不存在*/bool Test(const K& key) const {// 三个位置中任一位置未设置即可确定不存在size_t i1 = Hash1()(key) % N;if (!_bs.test(i1)) return false;size_t i2 = Hash2()(key) % N;if (!_bs.test(i2)) return false;size_t i3 = Hash3()(key) % N;return _bs.test(i3); // 三个位置全设置返回可能存在}/*** @brief 清空过滤器(重置所有位)*/void Clear() {_bs.reset();}/*** @brief 获取当前设置的比特位数量* @return size_t 已设置的位数量*/size_t GetUsedBits() const {return _bs.count();}/*** @brief 估算当前误判率* @param element_count 假设已插入元素数量* @return double 误判概率(0.0~1.0)*/double EstimateFalsePositiveRate(size_t element_count) const {if (element_count == 0) return 0.0;const double m = N;              // 位数组总大小const double k = 3.0;            // 哈希函数数量const double n = element_count;  // 假设的元素数量// 误判率公式:(1 - e^(-k*n/m))^kconst double exponent = -k * n / m;return pow(1 - std::exp(exponent), k);}/*** @brief 估算当前存储的元素数量* @return size_t 估算的元素数量*/size_t EstimateElementCount() const {const size_t used = GetUsedBits();if (used == 0) return 0;const double m = N;    // 位数组总大小const double k = 3.0;  // 哈希函数数量const double X = used; // 已设置的位数量// 元素数量估算公式:n ≈ -m/(k) * ln(1 - X/m)return static_cast<size_t>(-m/(k) * std::log(1 - X/m));}/*** @brief 获取过滤器位数组容量* @return size_t 位数组总大小(bits)*/constexpr size_t Capacity() const noexcept {return N;}/*** @brief 获取当前空间利用率* @return double 已用位比例(0.0~1.0)*/double LoadFactor() const {return static_cast<double>(GetUsedBits()) / N;}private:std::bitset<N> _bs; // 底层位数组存储
};/* 使用示例 */
int main() {BloomFilter<1000000> bf; // 100万位的布隆过滤器// 插入测试数据bf.Set("alice");bf.Set("bob");bf.Set("charlie");// 测试存在性std::cout << "Test alice: " << bf.Test("alice") << "\n";   // 1std::cout << "Test david: " << bf.Test("david") << "\n";   // 0// 获取统计信息std::cout << "Used bits: " << bf.GetUsedBits() << "\n";std::cout << "Estimated elements: " << bf.EstimateElementCount() << "\n";std::cout << "Load factor: " << bf.LoadFactor() << "\n";std::cout << "False positive rate: " << bf.EstimateFalsePositiveRate(3) << "\n";// 清空过滤器bf.Clear();std::cout << "After clear, used bits: " << bf.GetUsedBits() << "\n";return 0;
}

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

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

相关文章

从Instagram到画廊:社交平台如何改变艺术家的展示方式

从Instagram到画廊&#xff1a;社交平台如何改变艺术家的展示方式 在数字时代&#xff0c;艺术家的展示方式正在经历一场革命。社交平台&#xff0c;尤其是Instagram&#xff0c;已经成为艺术家展示作品、与观众互动和建立品牌的重要渠道。本文将探讨社交平台如何改变艺术家的…

MySQL(事物上)

目录 示例&#xff1a; 一 引入事物 1. 概念 2. 事物的4大特性 3. 为什么要有事物&#xff1f; 二 事物操作 1. 查看存储引擎支持的事物 2. 事物的提交方式 2.1 查看事物的默认提交方式 2.2 设置事物的默认提交方式 2.3 查看事物的全局隔离级别 2.4 验证事物的回滚…

Spring Boot 实现多数据源配置

一、配置流程 在 Spring Boot 中实现多数据源配置通常用于需要连接多个数据库的场景。主要有以下几个步骤&#xff1a; 配置多个数据源的连接信息。定义多个数据源的 Bean。为每个数据源配置MyBatis的SqlSessionFactory和事务管理器。为每个数据源定义Mapper接口和Mapper XML…

p5.js:绘制各种内置的几何体,能旋转

向 豆包 提问&#xff1a;请编写 p5.js 示例&#xff0c; 绘制各种内置的几何体&#xff0c;能让这些几何体缓慢旋转。 cd p5-demo copy .\node_modules\p5\lib\p5.min.js . 此代码创建了一个包含多个内置几何体的 3D 场景&#xff0c;每个几何体都有不同的颜色和位置。运行代…

结构体定义与应用

引言 到今天为止,c语言的基础操作和基础数据类型,就都已经结束了,大家都知道,如果要实现复杂的功能,大家都可以通过函数封装调用,那么如果要实现基础数据类型的封装,该怎么办呢?答案就是结构体。 在C语言编程中,结构体(struct)是非常重要的一个概念,它为程序员提供…

MindGYM:一个用于增强视觉-语言模型推理能力的合成数据集框架,通过生成自挑战问题来提升模型的多跳推理能力。

2025-03-13&#xff0c;由中山大学和阿里巴巴集团的研究团队提出了MindGYM框架&#xff0c;通过合成自挑战问题来增强视觉-语言模型&#xff08;VLMs&#xff09;的推理能力。MindGYM框架通过生成多跳推理问题和结构化课程训练&#xff0c;显著提升了模型在推理深度和广度上的表…

R语言零基础系列教程-01-R语言初识与学习路线

代码、讲义、软件回复【R语言01】获取。 R语言初识 R是一个开放的统计编程环境&#xff0c;是一门用于统计计算和作图的语言。“一切皆是对象”&#xff0c;数据、函数、运算符、环境等等都是对象。易学&#xff0c;代码像伪代码一样简洁&#xff0c;可读性高强大的统计和可视…

PythonWeb开发框架—Flask-APScheduler超详细使用讲解

1.定时任务的两种实现方式 1.1 用scheduler.task装饰任务 安装插件&#xff1a; pip install Flask-APScheduler pip install apscheduler 脚本实现&#xff1a; ###app.py##导入依赖库 from flask import Flask import datetime import config from flask_apscheduler i…

python_巨潮年报pdf下载

目录 前置&#xff1a; 步骤&#xff1a; step one: pip安装必要包&#xff0c;获取年报url列表 step two: 将查看url列表转换为pdf url step three: 多进程下载pdf 前置&#xff1a; 1 了解一些股票的基本面需要看历年年报&#xff0c;在巨潮一个个下载比较费时间&…

从0到1构建AI深度学习视频分析系统--基于YOLO 目标检测的动作序列检查系统:(2)消息队列与消息中间件

文章大纲 原始视频队列Python 内存视频缓存优化方案(4GB 以内)一、核心参数设计二、内存管理实现三、性能优化策略四、内存占用验证五、高级优化技巧六、部署建议检测结果队列YOLO检测结果队列技术方案一、技术选型矩阵二、核心实现代码三、性能优化策略四、可视化方案对比五…

React Native 如何使用 Expo 快速开发?

React Native是当下热门的跨平台移动开发框架&#xff0c;而Expo则是它的重要开发工具之一。Expo提供了一套完整的开发环境&#xff0c;使开发者无需安装Android Studio或Xcode也能快速运行React Native项目。它包含了众多内置API&#xff0c;如相机、地理位置、推送通知等&…

中考英语之09从句

1 宾语从句 定义 在主从复合句中充当宾语&#xff0c;位于及物动词、介词或复合谓语之后的从句。 引导词 综述&#xff1a; that&#xff08;可省略&#xff09;、if/whether、连接代词&#xff08;what、which、who、whom、whose 等&#xff09;和连接副词&#xff08;when、…

平方矩阵问题

Ⅰ 回字形二维数组 #include <iostream> #include <iomanip> using namespace std; int main(){int n;while(cin>>n,n){for(int i0; i<n;i){for(int j0; j<n; j){int upi, downn-i1, leftj, rightn-j1;cout<<min(min(up,down),min(left,right)…

C++模版(复习)

1.泛型编程&#xff1a;编写与类型无关的通用代码&#xff0c;是代码复用的一种手段。模板是泛型编程的基础 2.函数模板的格式 template<typename T1,typename T2,…,typename Tn> 返回类型 函数名(参数列表) {   //函数体 } 3.template<class T1,class T2,…,class…

【sklearn 05】sklearn功能模块

sklearn功能模块 分类&#xff1a;识别某个对象属于那个类别回归&#xff1a;预测与对象相关联的连续值属性聚类&#xff1a;将相似对象自动分组降维&#xff1a;减少要考虑的随机变量的数量模型选择&#xff1a;比较、验证、选择参数和模型预处理&#xff1a;特征提取和归一化…

使用Qt创建悬浮窗口

在Qt中创建悬浮窗口&#xff08;如无边框、可拖动的浮动面板或提示框&#xff09;可以通过以下方法实现。以下是几种常见场景的解决方案&#xff1a; 方法1&#xff1a;使用无边框窗口 鼠标事件拖动 适用于自定义浮动工具窗口&#xff08;如Photoshop的工具栏&#xff09;。 …

《P4387 【深基15.习9】验证栈序列》

题目描述 给出两个序列 pushed 和 poped 两个序列&#xff0c;其取值从 1 到 n(n≤100000)。已知入栈序列是 pushed&#xff0c;如果出栈序列有可能是 poped&#xff0c;则输出 Yes&#xff0c;否则输出 No。为了防止骗分&#xff0c;每个测试点有多组数据&#xff0c;不超过 …

校园安全用电怎么保障?防触电装置来帮您

引言 随着教育设施的不断升级和校园用电需求的日益增长&#xff0c;校园电力系统的安全性和可靠性成为了学校管理的重要课题。三相智能安全配电装置作为一种电力管理设备&#xff0c;其在校园中的应用不仅能够提高电力系统的安全性&#xff0c;还能有效保障师生的用电安全&am…

【Git】--- 初识Git Git基本操作

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; Git 本篇我们来初步认识Git企业级应用是什么&#xff0c;有什么用以及Git基本操作。 &#x1f3e0; 初始Git 提出问题 在日常生活中&#xff0c;我们进行…

数据治理下半场:如何用文化变革撬动企业数字化转型?

数据治理下半场:如何用文化变革撬动企业数字化转型? 一、打破认知茧房:从"数据恐惧症"到"数据生产力"二、重构协作生态:从"部门墙"到"数据共同体"三、建立责任体系:从"无人认领"到"终身责任制"​四、点燃创新…