布隆过滤器是什么?

简介

布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数

作用:布隆过滤器可以用于检索一个元素是否在一个集合中。

优点:空间效率和查询时间都比一般的算法要好的多

缺点有一定的误识别率和删除困难。

 

详细

布隆过滤器本身是一个很长的二进制向量,既然是二进制的向量,那么显而易见的,存放的不是0,就是1。

新建一个16位的布隆过滤器,如图

 

随机映射函数,比如我们使用4个hash算法:hash1,hash2,hash3,hash4

对于布隆过滤器本身来说,并没有存储任何数据,只是计算该数据的位置,然后存储向量值

有4个值a,b,c,d

 

为了便于说明,这里将二进制的向量抽象成阵列的样子(其实就是哈希表Hashtable),4个值对应的计算结果如图所示。

假设一个集合中,如果已经存储了a,b

判断c是否存在集合中?通过计算得知c不在集合中

判断d是否存在集合中?通过计算得知d“存在”集合中,但是实际上d不存在,这就是误差

为什么会存在误差呢?因为d的hash值和a,b的hash值冲突了,也叫做hash碰撞。

为了减少误差,可以增加hash函数的个数,但是相应的会降低计算的速度。

由于布隆过滤器只存储key对应的hash值,不像B树那样需要存储key,所以节约了空间,数据量巨大时效果显著

 

现在看下面的模型应该就很好理解了,假如一个集合中存储了x,y,z元素,判断w是否在集合中

 

通过计算w得知有一个hash值为0,那么说明w不在集合中

 

根据上面的理解,下面我们来做判断题:

在布隆过滤器中,如果判断一个值不在集合中,那么它可能不在集合中(False)

在布隆过滤器中,如果判断一个值不在集合中,那么它一定不在集合中(True)

在布隆过滤器中,如果判断一个值在集合中,那么它可能不在集合中(True)

在布隆过滤器中,如果判断一个值在集合中,那么它可能在集合中(True)

 

 

应用:

网页URL的去重,垃圾邮件的判别,集合重复元素的判别,查询加速(比如基于key-value的存储系统)、数据库防止查询击穿

有时候我们需要判断一个元素是否在一个集合中。比如,在字处理软件中,需要检查一个单词是否拼写正确(也就是要判断它是否在已知的字典里);在警察系统中,一个嫌疑人的名字是否出现在嫌疑名单上;在网络爬虫里,一个网址是否已经被访问过,等等。

 

实例:

1、已知某个文件内包含一些电话号码,每个号码为8位数字,统计不同号码的个数。

8位最多99 999 999,大概需要99m个bit,大概10几M字节的内存即可。

 

2、在2.5亿个整数中找出不重复的整数,内存不足以容纳这2.5亿个整数。

采用2-Bitmap(每个数分配2bit,00表示不存在,01表示出现一次,10表示多次,11无意义)进行,共需内存232*2bit=1GB内存,还可以接受。然后扫描这2.5亿个整数,查看Bitmap中相对应位,如果是00变01,01变10,10保持不变。所描完事后,查看bitmap,把对应位是01的整数输出即可。

 

 

实现

import java.util.BitSet;

/**
 * 布隆过滤器实现
 */
public class BloomFilter {

    

    //1、二进制向量长度,2的n次幂
    /**
     *  一个长度为10 亿的比特位
     */
    private static final int size = 1 << 30;

    /**
     * 初始化布隆过滤器的bitmap
     */
    private static BitSet bitSet=new BitSet(size);

    //2、hash算法的素数因子
    /**
     * 为了降低错误率,使用加法hash算法,所以定义一个8个元素的质数数组
     */
    private static final int[] seeds = {3, 5, 7, 11, 13, 31, 37, 63};

    //3、hash函数
    /**
     * 相当于构建 8 个不同的hash算法
     */
    private static HashFunction[] functions = new HashFunction[seeds.length];



    /**
     * 添加数据
     * @param str 需要加入的值
     */
    public static void add(String str){
        if (str != null) {
            for (HashFunction f : functions) {
                bitSet.set(f.hash(str),true);
            }
        }
    }

    /**
     * 判断值是否存在
     * @param str 需要判断的值
     * @return
     */
    public static boolean contains(String str){
        if (str == null) {
            return false;
        }
        boolean ret=true;
        for (HashFunction f : functions) {
            ret=bitSet.get(f.hash(str));
            //只要有一个hash函数返回false就返回
            if (!ret) {
                return ret;
            }
        }
        return ret;
    }


    /**
     * 测试
     */
    public static void main(String[] args) {

        //初始化hash函数
        for (int i = 0; i < seeds.length; i++) {
            functions[i]=new HashFunction(seeds[i],size);
        }
        //添加1个亿的数据
        for (int i = 0; i < 100000000; i++) {
            add(String.valueOf(i));
        }

        String id="123456789";
        add(id);
        System.out.println(contains(id));//true
        System.out.println(contains("234567890"));//false


    }

}

/**
 * Hash函数
 */
class HashFunction {
    //hash素数因子
    private int seed;
    //size是2的n次幂,这样size-1得到所有bit为1
    private int size;

    public HashFunction(int seed, int size) {
        this.seed = seed;
        this.size = size;
    }

    public int hash(String value) {
        int result = 0;
        if (value != null) {
            int len = value.length();
            for (int i = 0; i < len; i++) {
                result = result * seed + value.charAt(i);
            }
        }
        return result & (size - 1);
    }
}

 

参考

百度百科:

https://baike.baidu.com/item/%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8/5384697?fr=aladdin

布隆过滤器(Bloom Filter)详解:

https://www.cnblogs.com/liyulong1982/p/6013002.html

布隆过滤器(BloomFilter)的原理、实现和探究:

https://my.oschina.net/LucasZhu/blog/1813110

<think>嗯,用户想知道如何降低布隆过滤器的误判率。首先,我得回忆一下布隆过滤器的基本原理。布隆过滤器使用位数组和多个哈希函数,当添加元素时,会通过哈希函数映射到位数组的多个位置并置为1。查询时,如果所有对应的位都是1,就认为可能存在,否则肯定不存在。不过,由于哈希冲突的存在,可能会出现误判,也就是把不存在的元素误判为存在。 用户的问题是如何降低这种误判率。根据之前的知识,误判率主要和位数组的大小、哈希函数的数量以及存储的元素数量有关。我记得引用里提到过,error_rate越低,位数组的大小越大,占用的空间也越多。所以可能需要调整这两个参数。 首先,可能需要增大位数组的长度m。因为m越大,哈希冲突的概率就越低。但增大m会占用更多内存,所以需要权衡。根据引用[4],误判率的计算公式可能和m、k(哈希函数数量)以及n(元素数量)有关。具体公式可能是$(1 - e^{-k n / m})^k$。所以当m增大时,这个值应该会减小。 其次,调整哈希函数的数量k。如果k太小,冲突可能多;k太大,可能导致位数组更快被填满。存在一个最优的k值,通常是根据m和n计算出来的,比如k = (m/n) * ln2。所以可能需要合理选择k的值,不过实际中可能需要测试不同的k值来找到最佳点。 另外,控制存储的元素数量n不超过initial_size。引用[1]提到,当实际存储的元素超过initial_size时,误判率会上升。所以用户需要预估元素数量,并设置足够大的initial_size,避免超载。 还有动态调整的策略,比如当元素数量接近initial_size时,自动扩容,创建一个新的更大的布隆过滤器,并迁移数据。这样可以保持误判率在可接受范围内,但实现起来可能复杂一些,需要考虑性能开销。 使用低碰撞率的哈希函数也很重要。比如选择MurmurHash、SHA系列等,减少不同元素哈希到相同位置的概率,从而降低误判。 另外,结合其他数据结构,比如引用[2]提到可以结合其他算法优化整体方案。例如,在布隆过滤器判断存在后,再通过精确查询(如数据库)进行二次确认,虽然增加了步骤,但减少了误判的影响。 还有允许动态删除的变种,比如Counting Bloom Filter,它使用计数器而不是位数组,但会增加空间开销。这可能不是直接降低误判率,而是处理删除的问题,但可能间接帮助管理位数组的状态,避免删除导致的误判情况恶化。 需要考虑用户的实际场景,比如是否能接受一定的误判率,是否有严格的内存限制,是否需要动态扩容等。不同应用可能需要不同的策略组合,比如高并发场景可能优先考虑哈希函数优化和动态调整,而空间受限的环境可能需要仔细平衡m和k。 总结下来,最佳实践可能包括合理配置m和k、控制元素数量、使用优质哈希函数、动态调整以及结合其他结构。需要根据具体情况选择合适的方法,并在空间和准确率之间找到平衡点。</think>### 降低布隆过滤器误判率的方法与实践 #### 1. **优化位数组长度与哈希函数数量** 布隆过滤器的误判率公式为: $$P \approx \left(1 - e^{-k n / m}\right)^k$$ 其中: - $m$ 为位数组长度 - $k$ 为哈希函数数量 - $n$ 为存储的元素数量 **最佳实践**: - **增大位数组长度**:$m$ 越大,哈希冲突概率越低,但需权衡内存占用。建议根据公式 $m = -\frac{n \ln P}{(\ln 2)^2}$ 计算理论值[^4]。 - **选择最优哈希函数数量**:$k$ 的最优值为 $k = \frac{m}{n} \ln 2$,通常取整数(如3-5个)[^3]。 #### 2. **控制元素数量与动态调整** - **严格限制存储数量**:若实际元素数超过预设的 `initial_size`,误判率会显著上升[^1]。需根据业务预估合理设置初始容量。 - **动态扩容**:当元素接近容量上限时,创建更大的新布隆过滤器并迁移数据,但需额外计算和存储成本[^2]。 #### 3. **使用低碰撞率的哈希函数** 选择高质量的哈希函数(如MurmurHash、SHA系列),减少不同元素映射到相同位置的概率。例如: ```python import mmh3 def hash_functions(data, k, m): return [mmh3.hash(data, i) % m for i in range(k)] ``` #### 4. **结合其他数据结构** - **二次验证**:当布隆过滤器返回“可能存在”时,通过精确查询(如数据库)二次确认。 - **分层布隆过滤器**:对高精度需求的数据使用多层过滤器,逐层过滤以减少误判。 #### 5. **允许动态删除的变种** 使用**Counting Bloom Filter**(用计数器替代二进制位),支持删除操作,避免因元素删除导致误判率上升,但会额外增加内存开销[^4]。 --- ### 场景示例 假设需要存储 $n=10^6$ 个元素,目标误判率 $P=0.1\%$: 1. 计算位数组长度: $$m = -\frac{10^6 \cdot \ln 0.001}{(\ln 2)^2} \approx 14.9 \times 10^6 \ \text{bits} \approx 1.78\ \text{MB}$$ 2. 计算哈希函数数量: $$k = \frac{1.78 \times 10^6}{10^6} \ln 2 \approx 10$$ ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值