【leetcode学习笔记】布隆过滤器

本文介绍了布隆过滤器在解决大规模数据集合查询中的应用,特别是在浏览器黑名单url过滤场景下。布隆过滤器通过牺牲一定的准确率来降低空间复杂度,避免第一类错误并能控制第二类错误概率。文章详细阐述了其工作原理,包括二进制数组和哈希函数的使用,以及性能分析中的两类错误率与参数k、m的关系。通过实例计算表明,相比于传统的HashSet,布隆过滤器能显著减少存储需求,且误判率可控制在较低水平。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、问题背景

假设一家浏览器公司收集了若干个包含违法内容的网站,构成一个url的黑名单。每次用户访问一个网址时,都要先判断是不是命中了黑名单,如果是则拦截。这里,黑名单库不需要执行删除操作,这是讨论布隆过滤器的关键。

1.1 问题本质

设计一个集合的数据结构,这个结构支持添加、查询的功能,不需要支持删除功能。

1.2 传统做法

显而易见,hashset是最直接的做法。添加和查询的时间代价都很低,但是问题在于空间复杂度。假设黑名单中有100亿个url,每个url大小是64B。那么所需要的内存空间就是大约640GB。这需要平台提供至少10个拥有64GB内存的服务器,代价比较大。

布隆过滤器通过付出牺牲一定准确率的代价,大幅度降低了空间复杂度。

二、布隆过滤器

2.1 第一类错误和第二类错误

上文提到,布隆过滤器不能做到查询的准确率100%。那么我们首先需要区分一下查询错误的类型。

第一类错误:用户访问了黑名单里的url,但是系统没有识别到,允许用户访问。

第二类错误:用户访问了合法的url,但是系统误杀了,不允许用户访问。

布隆过滤器可以避免第一类错误,第二类错误无法避免。但是,布隆过滤器可以控制第二类错误发生的概率小于任意\epsilon>0

从业务的视角理解,第一类错误是绝对不能容忍的,因为它可能会导致浏览器不合规、公司被举报等问题。但是第二类错误,只要能够有效控制错误率,比如在万分之一以下,那么损失相对是比较小的。比如用户登不了某个网站,可以换一个功能类似的网站。这种损失既是可控的,同时还能节省大量的空间成本,对于业务方也是可以接受的。

2.2 二进制数组

为了介绍布隆过滤器,我们首先需要介绍bit array的概念。我们平常看到的数组多数是基础或引用类型的,比如int[10],本质上存储在计算机中的是10*32个0-1值;long[10],本质上存储在计算机中的是10*64个0-1值。

现在我们希望引入一种二进制数组的概念,即bit[100],共有100个元素,每一个元素都是0-1值。虽然java没有这种结构,但我们可以通过基础类型int,充分的利用位运算来实现。

class BitArray{
    private int[] intArray;
    private int numIndex;
    private int bitIndex;
        
    // 初始化一个长度为length的位数组
    BitArray(int length){
        intArray = new int[length / 32 + 1];
    }
    

    // 得到第i位的状态
    private int get(int index){
        numIndex = index / 32;
        bitIndex = index & 32;
        return (intArray[numIndex] >> bitIndex) & 1;
    }
    
    // 把位数组的第i位变成1
    private void changeTo1(int index){
        numIndex = index / 32;
        bitIndex = index & 32;
        intArray[numIndex] = intArray[numIndex] | (1 << bitIndex);
    }

    // 把位数组的第i位变成0
    private void changeTo0(int index){
        numIndex = index / 32;
        bitiNDEX = index % 32;
        intArray[numIndex] = intArray[numIndex] & (~(1 << bitIndex));
    }
}

这里我们如何理解这个intArray呢?比如说一个数组int[10],数组中的每个数都能唯一对应一个32位的二进制数,那么这个数组就可以表征一个二进制数组bit[320]。通过位运算,我们可以实现数组的修改和查询操作。

2.3 布隆过滤器的机制

布隆过滤器,采用二进制数组来表征黑名单库,具体操作如下:

初始化:新建一个长度为m的二进制数组,再设计k个独立的哈希函数f_1,f_2,..., f_k 。

添加操作:对于一个黑名单url,分别经过k个哈希函数+模m的操作映射成0到m-1之间的值。于是,我们把二进制数组的这k个位置的值变成1。

(注:哈希函数的随机性保证了,100亿个url经过映射后,会等概率的分布在0到m-1)

查询操作:对于一个陌生的url访问,同样经过k个哈希函数+取模的操作,拿到0到m-1之间的k个值。如果二进制数组的这k个位置均为1,视为命中黑名单;否则视为未命中。

3 布隆过滤器的性能分析

3.1 两类错误

第一类错误可以完全避免。因为如果一个url在黑名单里,那么它一定曾经执行过添加操作,二进制数组中它对应的k个位置一定都是1,不可能会被漏掉。

第二类错误是有可能发生的。比如说,黑名单中只有2个url,共有3个哈希函数,一个映射成第1、2、3位,另一个映射成第3、6、9位。新访问的url和这两个都不一样,但是它经过哈希映射后对应的是第2、3、6位,因为这3位都已经变成1,所以会被误识别为命中。

3.2 第二类错误率与k、m的关系

设黑名单的网址数量为n,哈希函数的数量为k,二进制数组的长度为m,第二类错误的概率为P。

固定n和k,那么m越大,不同的黑名单url经过哈希映射后,发生碰撞的概率就越低,因此P也就越低。P关于m是递减的趋势。

固定n和m,那么随着k的增大,查询匹配的难度会逐渐上升。在执行查询操作时,如果只有1个哈希函数,那么一个正常的url,只需要命中一个1的位置,就会被误判。而如果有多个哈希函数,则需要同时满足多个位置同时凑巧都为1才会误判,误判率会变低。因此误判率P在k较小时会呈现递减趋势;但是如果k变得非常大,那么经过一系列添加操作之后,很可能二进制数组中的m位都变成1了,P会变得很高。因此P关于k是先递减后递增的趋势。

3.3 性能分析

固定样本量n和要求的错误率P,那么所需要的m和k可通过如下公式计算:

m=-\frac{n\ln P}{(\ln 2)^2}

k=\frac{m\ln2}{n}

假设n为100亿,P为万分之一,经过公式计算,我们可以得到m,再计算出对应的存储空间为26G,相较于传统方法的640G,有了极大的改善,而我们只付出了万分之一的错误率的代价。

值得注意的是,布隆过滤器的所需空间是不取决于样本本身的数据量的,不管一条url的大小是64B还是32B都没有关系,我们只需要哈希函数能够接受64B大小的输入即可,经过映射加取模之后,一律对应m维的二进制数组。这也是性能提升的重要因素之一。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值