布隆过滤器(Bloom Filter)原理及实现


一、什么是布隆过滤器

布隆过滤器(Bloom Filter)是一个很长的二进制向量和一系列随机映射函数。它是一种数据结构,比较巧妙的概率型数据结构(probabilistic data structure),特点是高效的插入和查询,可以用于检索一个元素是否在一个集合中。

优点:相比于传统的list、set、map等数据结构,它更高效、占用空间更少。
缺点:返回的结果是概率性(存在误差),不是确切的。


二、为什么不用HashMap?

数据存储和查找,map和hashmap应该也是很好的选择,为什么不直接使用map或hashmap,而还要用布隆过滤器呢?
在这里插入图片描述
在HashMap中,值映射到HashMap的key,然后可以在O(1)的时间复杂度内返回结果,效率也非常高。
在这里插入图片描述

不过,HashMap的也存在一定的问题,表现如下:

  • 存储容量占比高,考虑到负载因子的存在,空间通常是不能被用满。
  • HashMap需要存储key值,而当key值类似URL时占用的空间非常大。

(负载因子loadFactor表示一个散列表的空间的使用程度,假设initailCapacity为初始化数组大小,得到公式:initailCapacity*loadFactor=HashMap的容量。
负载因子越大则散列表的装填程度越高,也就是能容纳更多的元素,元素多了,链表大了,此时索引效率就降低;反之,负载因子越小则链表中的数量就越稀疏,此时会对空间造成浪费,不过此时索引效率高。)

空间、时间占用测试

unordered_map<string, bool> unordermp;
main_key = “https://blog.csdn.net/muyuyuzhong/article/details/”;
sub_key = to_string(i);
key = main_key + sub_key;

在这里插入图片描述
从上面的测试我们看到,当存储key值为53bytes的URL时,占用的内存空间和插入的耗时开销是相当大的,如果数据量达到1亿、10亿,这个时候性能就比较差。

同样的硬件配置和数据,使用布隆过滤器存储1000万条数据存储消耗的内存约为22.852562M,相比于HashMap的1599.5M,节省约67倍的内存空间。

另外,Hash一般元素超过表空间的一半就需要扩容,如果不进行扩容hash冲突就会变得严重,以致hash会退化为链表的性能。
比如,最大表1000bit,元素500,当再有一个新的数据要插入进来的时候就需要对表进行扩容。


三、布隆过滤器原理

布隆过滤器是一个bit向量或者说是bit数组:
在这里插入图片描述
布隆过滤器不存储key值,内存消耗上相对于hashmap节省了key值的内存消耗。

1、原理

当一个元素被加入集合时,通过K个hash函数将这个元素映射成一个位数组中的K个点,把它们置为1。

2、检索

通过k个点位的值进行判断。
  • k个点位中有任一为0,被检元素一定不存在。
  • k个点位都是1,被检元素可能存在。

3、实例

在这里插入图片描述
从上面两个图示例子中,我们可以看到bit4在baidu和tencent被置1,表示bit4值出现了覆盖。
(1)查询“xiazhiqi”是否存在,假设hash1、hash2、hash3哈希函数分别返回1、6、7,三个值,而bit6值为0,说明没有值映射到这个bit位上,因此我们可以很确定的说“xiazhiqi”这个值不存在。
(2)查询“baidu”是否存在,哈希函数返回1、4、7,检查发现这三个bit的值均为1,我们不能说baidu一定存在,而是只能说可能存在。因为每个bit值都有可能是被其他元素的值覆盖了。


四、布隆过滤器的设计与实现

布隆过滤器存在一定的误判,在查找元素值都返回1的时候,我们没法确定该元素就一定存在,不过我们可以通过一些参数的设置来降低误判率。
假设m为向量表的长度,k为哈希函数的个数,n为要插入的元素个数,p为误判概率。

布隆过滤器误判率表
在这里插入图片描述
为每个URL分配两个字节就可以达到千分之几的冲突。比较保守的实现是,为每个URL
分配4个字节,项目和位数比是1∶32,误判率是0.00000021167340。对于5000万数量级
的URL,布隆过滤器只占用200MB的空间。

在实际应用中,我们一般是可以给定n、p,然后计算出m、k。

1、m值的选取

在这里插入图片描述

2、k值的选取

在这里插入图片描述

3、哈希函数选择

  • 常见应用比较广的hash函数有MD5、SHA1、SHA256,一般用于信息安全方面,比如签名认证和加密等。比如我们传文件时习惯对原文件内容计算它的MD5,生成128bit的整数,通常我们说的32位MD5,是转换为HEX格式后的32个字符。
  • MutmurHash相比较MD5,不太安全。但性能是MD5的几十倍。MurmurHash有多个版本,其中MurmurHash3修复了MurmurHash2的一些缺陷同时速度还要快一些,因此很多开源项目有用,比如Nginx、Redis、memcashed、Hadoop等。

我们这里选择MurmurHash2算法实现。

4、是否可以删除集合中的元素?

答案是否。上面我们提到,在布隆过滤器算法中会存在一个bit位被多个元素值覆盖的情况,即bit位碰撞,如果我们删除元素时刚好重置该碰撞bit为0,那么其他元素在查找的时候,就会导致判断出错的问题发生。

5、部分源码实现

1) m、k值计算

void CalcBloomFilterParam(uint32_t n, double p, uint32_t *pm, uint32_t *pk)
{
    /**
     *  n - Number of items in the filter
     *  p - Probability of false positives, float between 0 and 1 or a number indicating 1-in-p
     *  m - Number of bits in the filter
     *  k - Number of hash functions
     *
     *  f = ln(2) × ln(1/2) × m / n = (0.6185) ^ (m/n)
     * m = -1*n*ln(p)/((ln(2))^2) = -1*n*ln(p)/(ln(2)*ln(2)) = -1*n*ln(p)/(0.69314718055995*0.69314718055995))
     *   = -1*n*ln(p)/0.4804530139182079271955440025
     * k = ln(2)*m/n
    **/

    uint32_t m, k, m2;

    // 计算指定假阳(误差)概率下需要的比特数
    m =(uint32_t) ceil(-1.0 * n * log(p) / 0.480453); 	//向上舍入为最近的整数
    m = (m - m % 64) + 64;                              			// 8字节对齐

    // 计算哈希函数个数
    double double_k = (0.69314 * m / n); 
    k = round(double_k);    // 返回四舍五入整数值
    
    *pm = m;
    *pk = k;
    return;
}

2) MurmurHash2算法

uint64_t MurmurHash2_x64 ( const void * key, int len, uint32_t seed )
{
    const uint64_t m = 0xc6a4a7935bd1e995;
    const int r = 47;

    uint64_t h = seed ^ (len * m);

    const uint64_t * data = (const uint64_t *)key;
    const uint64_t * end = data + (len/8);

    while(data != end)
    {
        uint64_t k = *data++;

        k *= m;
        k ^= k >> r;
        k *= m;

        h ^= k;
        h *= m;
    }

    const uint8_t * data2 = (const uint8_t*)data;

    switch(len & 7)
    {
    case 7: h ^= ((uint64_t)data2[6]) << 48;
    case 6: h ^= ((uint64_t)data2[5]) << 40;
    case 5: h ^= ((uint64_t)data2[4]) << 32;
    case 4: h ^= ((uint64_t)data2[3]) << 24;
    case 3: h ^= ((uint64_t)data2[2]) << 16;
    case 2: h ^= ((uint64_t)data2[1]) << 8;
    case 1: h ^= ((uint64_t)data2[0]);
        h *= m;
    };

    h ^= h >> r;
    h *= m;
    h ^= h >> r;

    return h;
}

五、布隆过滤器与位图的比较

1、BitMap

BitMap在处理海量数据也有着得天独厚的优势,利用内存中连续的二进制位(bit),对大量整型数据做去重和查询。由于每一个数据只占用1个bit,因此占用内存非常小,即使在处理url、邮件地址等其他数据类型时,把字符串变换为整数,也有各式各样的方法。

BitMap存储空间计算方式:
找到所有元素里最大的值(假设为N),BitMap所需空间为:
在这里插入图片描述
当N为64位整数时,根据上述公式求得S=2^64 / 8 Byte。可以说是一个很大的数字了。
BitMap优点是:空间不随集合内元素个数的增加而增加,但缺点是:空间随集合内最大元素的增大而增大。

适用:大规模但数据状态又不是很多的数据的压缩、索引、查询和去重,通常是用来判断某个数据是否存在。Java 中的 BitSet 类就是一个位图,Redis 中也提供了 BitMap 位图类。

2、布隆过滤器

优点:占用内存少,插入和查询速度快,时间复杂度都为 O(k),与集合中元素的多少无关。

缺点:存在误判的情况,只能判断一个数据是否一定不存在, 无法判断一个数据是否一定存在。而且随着数据的增加,误判率会增加;另外数据无法删除。

优化:可以通过调整hash函数的个数、向量表长度与要存储数值个数之间的比例,降低误判率。

适用:
在这里插入图片描述

  • 2
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
布隆过滤器Bloom Filter)是一种重要的数据结构,它用于快速判断一个元素是否存在于一个集合中。布隆过滤器的核心思想是通过一系列哈希函数来对元素进行多次哈希,然后将得到的哈希值映射到一个位数组中,并将对应的位置设为1。当需要判断一个元素是否存在时,同样对其进行多次哈希,检查对应位数组的值是否都为1,若都为1则可以确定元素可能存在;若存在一个0,则可以确定元素一定不存在。因此,布隆过滤器是一种基于概率的数据结构,可以高效地进行查找。 然而,布隆过滤器也存在一些问题。首先,由于多个不同的元素可能会哈希到相同的位上,因此在查询时可能出现误判,即判断一个元素存在时实际上并不存在。这种误判是由于多个元素共享了某一位的原因导致的。其次,布隆过滤器的特性决定了它无法支持元素的删除操作,因为删除一个元素可能会影响其他元素的判断结果,从而增加误判率。 要注意的是,计数布隆过滤器(Counting Bloom Filter)提供了一种实现删除操作的可能性,但并不能保证在后续查询时该值一定返回不存在。因此,不能说计数布隆过滤器支持删除,而是说计数布隆过滤器提供了实现删除的可能。 [3<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [【海量数据处理】布隆过滤器BloomFilter](https://blog.csdn.net/qq_43727529/article/details/127180864)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *3* [Java --- redis7之布隆过滤器BloomFilter](https://blog.csdn.net/qq_46093575/article/details/130613434)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值