跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

布隆过滤器 (Bloom Filter) 的底层原理与 C++ 代码实现

介绍布隆过滤器底层原理与 C++ 实现。该结构利用位图和多哈希函数实现高效概率存储,查询不存在必准,存在可误判。文章推导误判率公式并给出参数选择指南,提供基于 BKDRHash 等算法的 C++ 模板代码。同时讨论不支持删除原因、应用场景(缓存穿透、URL 去重)及性能对比。适用于海量数据判重场景。

宁静发布于 2026/3/22更新于 2026/5/2726 浏览

前言

在上一篇文章中,我们见识了位图(Bitset)在处理海量整型数据时的恐怖统治力:仅需 500MB 内存就能处理 40 亿个整数的查找与去重。

但是,现实工程中我们遇到的往往不是纯数字。比如:

爬虫系统需要对 10 亿个 URL 进行去重。垃圾邮件过滤系统需要判断一个邮箱地址是否在千万级的黑名单中。数据库防止'缓存穿透',需要快速判断一个查询条件(通常是字符串)是否存在。

URL 和邮箱都是字符串,位图只能存整型,如果用哈希把字符串转成整型存入位图,会遇到极其严重的哈希冲突(不同的字符串算出了同一个整型,导致误判)。

为了在极致压缩空间的同时,尽可能降低字符串映射带来的哈希冲突,1970 年,Burton Howard Bloom 提出了一个绝妙的数据结构——布隆过滤器(Bloom Filter)。

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

1.1 基本概念

布隆过滤器的本质是:一个极长的位图(Bitset) + 多个相互独立的哈希函数。

空间效率极高:不存储元素本身,只存储映射标记查询结果概率性:判断不存在一定正确,判断存在可能误判

1.2 核心特性

特性说明
假阳性(False Positive)可能误判存在的元素为不存在(有概率)
假阴性(False Negative)绝不误判不存在的元素为存在(100%准确)
删除操作标准实现不支持删除

1.3 工作原理图解

1.3.1 插入元素过程
初始状态:位图全 0 [0][0][0][0][0][0][0][0][0][0]
插入"find":
  哈希函数 1 → 位置 2
  哈希函数 2 → 位置 5
  哈希函数 3 → 位置 7
结果: [0][1][0][0][1][0][1][0][0][0]
↑    ↑    ↑
h1=2 h2=5 h3=7
1.3.2 查询元素过程
查询"find":
  h1=2(1)、h2=5(1)、h3=7(1) → 全部为 1 → **可能存在**
查询"hello":
  h1=2(1)、h2=5(1)、h3=8(0) → 有一个为 0 → **一定不存在**

核心结论:只要有一个 bit 位为 0,元素一定不存在;全部为 1 时,元素可能存在(可能被其他元素冲突导致)

二、误判率分析与参数选择

2.1 误判率公式

布隆过滤器的误判率与三个参数相关:

m:位图长度(bit 数) n:插入元素个数 k:哈希函数个数

最优哈希函数个数:

k = (m/n) * ln2

最小误判率:

p = (1 - e^(-kn/m))^k ≈ 0.6185^(m/n)

2.2 参数选择指南

期望误判率m/n 比率k 值(哈希函数个数)
10%4.83
1%9.64
0.1%14.45
0.01%19.26

经验公式:当使用 3 个哈希函数时,位图长度应为元素个数的 4 倍左右(m ≈ 4.2n)。

三、C++ 完整实现

3.1 哈希函数设计

布隆过滤器要求哈希函数彼此独立且均匀分布。这里选用三种经典字符串哈希算法:

#pragma once
#include <bitset>
#include <string>
#include <vector>

namespace bloom_filter {
// 1. BKDRHash - 综合评分最高的字符串哈希算法
struct BKDRHash {
    size_t operator()(const std::string& key) {
        size_t hash = 0;
        for (auto ch : key) {
            hash = hash * 131 + ch; // 131 是经验值,有效降低冲突
        }
        return hash;
    }
};

// 2. APHash - 另一种优秀哈希算法
struct APHash {
    size_t operator()(const std::string& key) {
        size_t hash = 0;
        int i = 0;
        for (auto ch : key) {
            if ((i & 1) == 0) {
                hash ^= ((hash << 7) ^ (ch) ^ (hash >> 3));
            } else {
                hash ^= (~((hash << 11) ^ (ch) ^ (hash >> 5)));
            }
            ++i;
        }
        return hash;
    }
};

// 3. DJBHash - 简洁高效的哈希算法
struct DJBHash {
    size_t operator()(const std::string& key) {
        size_t hash = 5381; // 5381 是经验初始值
        for (auto ch : key) {
            hash += (hash << 5) + ch; // hash * 33 + ch
        }
        return hash;
    }
};
}

3.2 布隆过滤器模板类

// BloomFilter 模板参数说明:
// N - 插入元素的预估个数
// X - 每个元素占用位数(m/n 比率),默认 4 表示每个元素占 4 个 bit
// K - 元素类型,默认为 string
// HashFunc1/2/3 - 三个哈希函数

template<size_t N, // 预期插入元素个数
          size_t X = 4, // 每个元素占用 bit 数(m/n 比率)
          class K = std::string,
          class HashFunc1 = bloom_filter::BKDRHash,
          class HashFunc2 = bloom_filter::APHash,
          class HashFunc3 = bloom_filter::DJBHash>
class BloomFilter {
public:
    // 构造函数:初始化位图大小 = N * X
    BloomFilter() : _bits(N * X) {}

    // 插入元素
    void set(const K& key) {
        size_t len = N * X;
        size_t hash1 = HashFunc1()(key) % len;
        size_t hash2 = HashFunc2()(key) % len;
        size_t hash3 = HashFunc3()(key) % len;
        _bits.set(hash1);
        _bits.set(hash2);
        _bits.set(hash3); // 如果需要更多哈希函数,可以继续添加
    }

    // 判断元素是否存在
    bool test(const K& key) const {
        size_t len = N * X;
        size_t hash1 = HashFunc1()(key) % len;
        // 只要有一个位为 0,一定不存在
        if (!_bits.test(hash1)) {
            return false;
        }
        size_t hash2 = HashFunc2()(key) % len;
        if (!_bits.test(hash2)) {
            return false;
        }
        size_t hash3 = HashFunc3()(key) % len;
        if (!_bits.test(hash3)) {
            return false;
        }
        // 所有位都为 1,可能存在(有误判概率)
        return true;
    }

    // 重置过滤器
    void clear() {
        _bits.reset();
    }

    // 获取位图大小(bit 数)
    size_t bit_size() const {
        return N * X;
    }

    // 获取已占用 bit 数
    size_t count() const {
        return _bits.count();
    }

private:
    std::bitset<N * X> _bits; // 底层位图
};
}

四、布隆过滤器的进阶讨论

4.1 为什么不支持删除?

布隆过滤器无法直接支持删除操作,原因如下:

假设有两个元素 A 和 B:
A 映射到位置{1, 4, 7}
B 映射到位置{4, 8, 9}
删除 A 时,如果将位置 1、4、7 置 0,会导致:
- 位置 4 被清 0,影响 B 的存在判断
- B 本来存在,现在查询 B 时位置 4 为 0,错误判断 B 不存在

解决方案:使用计数布隆过滤器,每个位置扩展为计数器,插入时+1,删除时-1。但代价是占用更多空间。

4.2 应用场景

场景说明示例
缓存穿透防护过滤不存在数据的请求,保护数据库Redis + 布隆过滤器
恶意 URL 检测快速判断 URL 是否在黑名单中浏览器安全功能
爬虫 URL 去重避免重复爬取相同网页搜索引擎爬虫
垃圾邮件过滤快速判断发件人是否在黑名单邮件系统
基因序列分析DNA 序列快速匹配生物信息学

4.3 性能分析

指标值
插入时间复杂度O(k) k 为哈希函数个数
查询时间复杂度O(k) k 为哈希函数个数
空间复杂度m bits m 为位图长度
误判率可控制在 1% 以下

五、总结与思考

5.1 核心思想回顾

哈希 + 位图:用多个哈希函数将元素映射到位图 概率性存在:用空间换时间,容忍可控误判 不可删除:bit 位被多个元素共享

5.2 设计要点

哈希函数选择:BKDRHash、APHash、DJBHash 等独立均匀的哈希函数 参数平衡:根据 n(元素个数)和 p(期望误判率)计算 m(位图大小)和 k(哈希函数个数) 空间计算:m ≈ 4.2n(k=3 时),每个元素约占用 4bit

5.3 优缺点对比

优点缺点
空间效率极高存在误判率
插入查询 O(k) 快速不支持删除
不存储元素本身(安全)不能遍历元素
适合海量数据需要预先估计数据量

布隆过滤器是哈希与位图思想的完美结合,它用一个精妙的概率模型解决了海量数据判重的难题。从理论到实践,从单机到分布式,布隆过滤器在大数据领域扮演着不可或缺的角色。

目录

  1. 前言
  2. 一、布隆过滤器的核心原理
  3. 1.1 基本概念
  4. 1.2 核心特性
  5. 1.3 工作原理图解
  6. 1.3.1 插入元素过程
  7. 1.3.2 查询元素过程
  8. 二、误判率分析与参数选择
  9. 2.1 误判率公式
  10. 2.2 参数选择指南
  11. 三、C++ 完整实现
  12. 3.1 哈希函数设计
  13. 3.2 布隆过滤器模板类
  14. 四、布隆过滤器的进阶讨论
  15. 4.1 为什么不支持删除?
  16. 4.2 应用场景
  17. 4.3 性能分析
  18. 五、总结与思考
  19. 5.1 核心思想回顾
  20. 5.2 设计要点
  21. 5.3 优缺点对比
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 18 款免费 AI 生成 3D 模型工具盘点
  • 2026 年 2 月 AIGC 行业模型发布及前沿资讯
  • Neo4j 图数据库使用入门
  • Android WebRTC 视频通话开发实战:AI 优化与避坑指南
  • AMR 机器人:如何满足现代物料搬运需求
  • 动态规划:01 背包问题详解
  • VSCode + GitHub Copilot 从安装到 Agent 实战详解
  • 笔记本 CPU 环境下 Faster-Whisper 模型模式选择指南
  • Agent Skills 与 MCP 混合架构设计模式实战
  • C++ 红黑树原理与手写实现详解
  • 大模型核心技术解析:Prompt Engineering、Function Calling、RAG 与 Fine-tuning
  • OpenClaw 跨平台安装指南:Windows、macOS 与 Linux 环境配置
  • VSCode 中 GitHub Copilot 大模型体系、订阅策略与 Agent 管理
  • RAG 应用最佳 Embedding 模型选择指南
  • Android Handler 消息传递机制深度解析
  • Whisper-Large-V3-Turbo 模型部署指南
  • Qwen2 技术报告:模型性能与多语言能力解析
  • OpenClaw 接入 QQ 机器人实现家庭 AI 助手
  • HTML5 存储技术详解:LocalStorage 与 SessionStorage
  • 灵感画廊:基于 SDXL 的极简 AI 绘画工具体验

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online