千家信息网

剖析布隆过滤器

发表于:2026-11-02 作者:千家信息网编辑
千家信息网最后更新 2026年11月02日,布隆过滤器(Bloom Filter)是由布隆(Burton Howard Bloom)在1970年提出的。它实际上是由一个很长的二进制向量和一系列随机映射函数组成,布隆过滤器可以用于检索一个元素是否
千家信息网最后更新 2026年11月02日剖析布隆过滤器

布隆过滤器(Bloom Filter)是由布隆(Burton Howard Bloom)在1970年提出的。它实际上是由一个很长的二进制向量和一系列随机映射函数组成,布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率(假正例False positives,即Bloom Filter报告某一元素存在于某集合中,但是实际上该元素并不在集合中)和删除困难,但是没有识别错误的情形(即假反例False negatives,如果某个元素确实没有在该集合中,那么Bloom Filter 是不会报告该元素存在于集合中的,所以不会漏报)。

即布隆:存在,不准确(哈希冲突) 不存在:准确

改进:映射位越多,占空间越多,误判率越低。

可使用计数达到删除功能

布隆底层:使用位图。

原理

如果想判断一个元素是不是在一个集合里,一般想到的是将集合中所有元素保存起来,然后通过比较确定。链表、树、散列表(又叫哈希表,Hash table)等等数据结构都是这种思路。但是随着集合中元素的增加,我们需要的存储空间越来越大。同时检索速度也越来越慢。

Bloom Filter 是一种空间效率很高的随机数据结构,Bloom filter 可以看做是对 bit-map 的扩展, 它的原理是:

当一个元素被加入集合时,通过 K 个 Hash 函数将这个元素映射成一个位阵列(Bit array)中的 K 个点,把它们置为 1。检索时,我们只要看看这些点是不是都是 1 就(大约)知道集合中有没有它了:

  • 如果这些点有任何一个 0,则被检索元素一定不在;

  • 如果都是 1,则被检索元素很可能在

优点

它的优点是空间效率和查询时间都远远超过一般的算法,布隆过滤器存储空间和插入 / 查询时间都是常数O(k)。另外, 散列函数相互之间没有关系,方便由硬件并行实现。布隆过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势。

缺点

但是布隆过滤器的缺点和优点一样明显。误算率是其中之一。随着存入的元素数量增加,误算率随之增加。但是如果元素数量太少,则使用散列表足矣。

(误判补救方法是:再建立一个小的白名单,存储那些可能被误判的信息。)

另外,一般情况下不能从布隆过滤器中删除元素. 我们很容易想到把位数组变成整数数组,每插入一个元素相应的计数器加 1, 这样删除元素时将计数器减掉就可以了。然而要保证安全地删除元素并非如此简单。首先我们必须保证删除的元素的确在布隆过滤器里面. 这一点单凭这个过滤器是无法保证的。另外计数器回绕也会造成问题。

模拟实现如下:

#pragma once#include#includeusing namespace std;class BitMap//将数据存储在对应的位,用位来存储数据{public:        BitMap(size_t len)        {                int size = len >> 5;                if (len % 32)                        _array.resize(size + 1);                else                        _array.resize(size);        }        BitMap(size_t minLen, size_t maxLen)//如果用这种,求下标时(num-minLen)/32        {                int size = (maxLen - minLen + 1) >> 5;                if ((maxLen - minLen + 1) % 32)                        _array.resize(size + 1);                else                        _array.resize(size);        }        void Set(size_t num)        {                size_t index = num >> 5;                size_t count = num % 32;                _array[index] |= (1 << count);//将_array[index]第count位置为1,此处存储和大小端有关系        }        void ReSet(size_t num)        {                size_t index = num >> 5;                size_t count = num % 32;                _array[index] &= (!(1 << count));//将_array[index]第count位置为1,此处存储和大小端有关系        }        bool Test(size_t num)        {                size_t index = num >> 5;                size_t count = num % 32;                return  _array[index] & (1 << count);        }private:        vector _array;//用vector不能存储相同的数,有限制,因为它只有0,1两个不同的位};class HashFunc1{        size_t BKDRHash(const char* str)        {                register size_t hash = 0;                while (size_t ch = (size_t)*str++)                {                        hash = hash * 131 + ch;                }                return hash;        }public:        size_t operator()(string key)        {                return BKDRHash(key.c_str());        }};class HashFunc2{        size_t SDBMHash(const char* str)        {                register size_t hash = 0;                while (size_t ch = (size_t)*str++)                {                        hash = 65599 * hash + ch;                }                return hash;        }public:        size_t operator()(string key)        {                return SDBMHash(key.c_str());        }};class HashFunc3{        size_t RSHash(const char* str)        {                register size_t hash = 0;                size_t magic = 63689;                while (size_t ch = (size_t)*str++)                {                        hash = hash * magic + ch;                        magic *= 378551;                }                return hash;        }public:        size_t operator()(string key)        {                return RSHash(key.c_str());        }};class HashFunc4{        size_t APHash(const char* str)        {                register size_t hash = 0;                size_t ch;                for (long i = 0; ch = (size_t)*str++; i++)                {                        if ((i & 1) == 0)                        {                                hash ^= ((hash << 7) ^ ch ^ (hash >> 3));                        }                        else                        {                                hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));                        }                }                return hash;        }public:        size_t operator()(string key)        {                return APHash(key.c_str());        }};class HashFunc5{        size_t JSHash(const char* str)        {                if (!*str)        // 这是由本人添加,以保证空字符串返回哈希值0                          return 0;                register size_t hash = 1315423911;                while (size_t ch = (size_t)*str++)                {                        hash ^= ((hash << 5) + ch + (hash >> 2));                }                return hash;        }public:        size_t operator()(string key)        {                return JSHash(key.c_str());        }};templateclass BloomFilter{public:        BloomFilter(size_t cap = 100)                :_bitmap(cap)                , _capacity(cap)        {}        void Set(const K& key)        {                size_t index1 = Func1()(key);                _bitmap.Set(index1%_capacity);                size_t index2 = Func2()(key);                _bitmap.Set(index2%_capacity);                size_t index3 = Func3()(key);                _bitmap.Set(index3%_capacity);                size_t index4 = Func4()(key);                _bitmap.Set(index4%_capacity);                size_t index5 = Func5()(key);                _bitmap.Set(index5%_capacity);                cout << index1 << " " << index2 << " " << index3                        << " " << index4 << " " << index5 << endl;        }        bool Test(const K& key)        {                if (!_bitmap.Test(Func1()(key)%_capacity))                        return false;                if (!_bitmap.Test(Func2()(key) % _capacity))                        return false;                if (!_bitmap.Test(Func3()(key) % _capacity))                        return false;                if (!_bitmap.Test(Func4()(key) % _capacity))                        return false;                if (!_bitmap.Test(Func5()(key) % _capacity))                        return false;                return true;        }protected:        BitMap _bitmap;        size_t _capacity;};void Test1(){        BloomFilter b;        b.Set("http://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html");        b.Set("http://www.cnblogs.com/-clq/archive/2012/05/31/2528154.html");        b.Set("http://www.cnblogs.com/-clq/archive/2012/05/31/2528155.html");        b.Set("http://www.cnblogs.com/-clq/archive/2012/05/31/2528156.html");        b.Set("http://www.cnblogs.com/-clq/archive/2012/05/31/2528157.html");        cout << b.Test("http://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html") << endl;        cout << b.Test("http://www.cnblogs.com/-clq/archive/2012/05/31/2528154.html") << endl;        cout << b.Test("http://www.cnblogs.com/-clq/archive/2012/05/31/2528155.html") << endl;        cout << b.Test("http://www.cnblogs.com/-clq/archive/2012/05/31/2528156.html") << endl;        cout << b.Test("http://www.cnblogs.com/-clq/archive/2012/05/31/2528157.html") << endl;        cout << b.Test("http://www.cnblogs.com/-clq/archive/2012/05/31/2528158.html") << endl;}

注

Example

可以快速且空间效率高的判断一个元素是否属于一个集合;用来实现数据字典,或者集合求交集。

如: Google chrome 浏览器使用bloom filter识别恶意链接(能够用较少的存储空间表示较大的数据集合,简单的想就是把每一个URL都可以映射成为一个bit)
得多,并且误判率在万分之一以下。
又如: 检测垃圾邮件


元素 布隆 过滤器 空间 存储 数据 检索 优点 效率 保证 函数 时间 是由 缺点 计数器 哈希 查询 原理 实际 实际上 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 博彦科技互联网金融 北京路客互联网科技有限公司 重生之互联网科技帝国 圆梦互联网科技 互联网公司 科技公司 猪兼强互联网科技 狮子座互联网科技培训 青海省互联网新科技 云创客互联网科技公司 南平创想时代互联网科技有限公司 互联网科技产业特征 畅思互联网科技 河南住房之家互联网科技 爱医互联网络科技 工业互联网展湾科技股份有限公司 西安有哪些互联网高科技企业 昆明中奇联合互联网科技 互联网科技创业计划的风险分析 宁波允成互联网科技有限公司 知域互联网科技有限公司官网 互联网核心科技是什么 汇久创客互联网科技 长亮科技 互联网金融 天扬科技互联网广告 互联网汽车科技小镇 广州百诚互联网科技有限公司官网 互联网科技股为什么大涨 互联网时代高科技布局 北京瑞云互联网科技有限公司 中国主要互联网科技公司
0