布隆过滤器(超详细!!!含图解)

前言

通过对于位图的认识,位图对于整形的适用比较好,当我们想要进行字符串的映射时,需要将字符串进行转化成整形,但是字符串的来说,当字符串的长度较长时就会显得苍白无力,表示字符的ASCII值有256种,当字符串的长度为10时,字符串的个数就有256的十次方,这个个数是远大于位图的最大空间的,直接地址法直接就失效了。当字符串进行向位图中映射时,由于字符串的数量远大于位图中能够映射的位置,那么必然就会出现一个位置进行有好几个字符串进行映射的结果。

狼多肉少就会出现冲突问题(hash冲突2.0版本)

内容摘要

本文内容包括布隆过滤器的概念、布隆过滤器优化的问题及其底层原理、解释布隆过滤器误判的原因以及优化布隆过滤器的实现、布隆过滤器的解决问题的场景、布隆过滤器的实现思路以及代码展现、探究布隆过滤器的误判率的测试逻辑以及代码展现、布隆过滤器和哈希切分进行配合使用处理海量数据问题等等。

布隆过滤器的概念

布隆过滤器就是为了缓解这种情况进行设计出来的,布隆过滤器并不能解决这种hash冲突2.0版本,只是缓解。本质上布隆过滤器是一种数据结构,比较巧妙的概率型数据结构(probabilistic data structure),特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”

布隆过滤器的原理

布隆过滤器解决哈希冲突2.0版本的逻辑就是,既然一个字符串进行一个映射一个位置容易与别的字符串进行冲突,那我将字符串通过不同哈希函数进行不同的映射是不是就可以明显的降低哈希冲突2.0版本的概率呢??答案确实是这样

通过不同的哈希映射进行映射到相同的位置,只有每个字符串通过不同的哈希函数进行映射到与其他字符串(必须是同一个字符串)同时映射到某几个位置时才发生哈希冲突2.0,当少于哈希函数个数的位置与别的字符串(也是同一个)进行映射到同一个位置不算冲突,例如美团通过某一个哈希函数进行映射后和美团通过某一个哈希函数进行映射后的位置相同,但是他们的另一个映射的位置不相同,这就不是冲突。

当我们进行查找"字节"这个字符串,哈希函数返回了1,5这三个值,然后去1,5这两个位置去查看bit位上存储的值是否为1,如果这两个位置的值都是1,则说明"字节"这个字符串存在。当我们进行查询"百度"这个字符串是否存在时,通过哈希函数返回假如是6,7这两个位置,发现6这个位置bit位存储的值是0,直接说明"百度"这个字符串不存在。

布隆过滤器的缺陷及优化

布隆过滤器的缺陷

布隆过滤器只是能减少字符串的冲突,但是还是会出现误判的情况,如上图所示,当我们要查询"网易"这个字符串是否存在时,经过两个哈希函数的返回,假如是1,2,这时通过查询1,2这两个比特位存储的值全是1,这时就说明"网易"这个字符串存在,这显然是不合理的,所以说布隆过滤器是存在一定的误判的。继续以上图为例,并且当任意一个不存在的字符串讲过哈希函数进行计算,只要哈希函数进行计算的结果不存在6,就一定会将非存在的字符串误判为存在,也就是说当布隆过滤器的长度过短时,进行映射几个值后,所有bit未中的值几乎全部为1,此时在进行查询为未存在的字符串时,几乎都会误判。当布隆过滤器的长度进行变化时,会引起布隆过滤器的误判率。

布隆过滤器是不支持删除操作的,还是如上图所示,当我们进行删除"腾讯"这个字符串时,会将5,7这两个bit位的值变成0,当我们进行查询"字节","美团"这两个字符串时,出现查询不到的结果

说明:布隆过滤器智慧将不存在误判成存在,不会将存在的误判成不存在的。

影响布隆过滤器误判率的因素

通过上面的一些情况可以看出,影响布隆过滤器进行检测字符串是否存在的误判率和布隆过滤器的长度以及哈希函数的个数是密切相关的

  • 在其他因素保持不变的情况下,当布隆过滤器的长度越长时,误判率越低
  • 在其他因素保持不变的情况下,当哈希函数的个数越多时,误判率越低

但是也不是说布隆过滤器的长度和哈希函数的个数就是越多越好,当布隆过滤器的长度太长时,会造成内存空间的浪费,当哈希函数的个数越多时,返回的值的个数越多,需要进行查询的bit位越多,效率就会变低。

如何选择布隆过滤器的长度和哈希函数的个数呢??

布隆过滤器的误判率公式:

其中:

  • k 是哈希函数的数量。
  • n 是要插入的数据个数。
  • m 是位数组的大小(即布隆过滤器的长度)。
  • e 是自然对数的底数(约为2.71828)。

如何解决布隆过滤器不支持删除的问题???

既然删除字符串会影响其他字符串的查询,如何进行优化解决这个问题呢?通过双位图就可以完美解决。利用另一个位图用于记录在第一个位图中对应位置被映射的次数,当进行某字符串的删除时,利用另一个位图将对应位置的存储数据进行-1处理。

适用场景

能够允许低概率的误判场景

能够起到大量数据的过滤效果,对效率有比较大的提升

举个栗子

解释一下什么叫做允许低概率进行误判的场景,像注册账户,当用户进行填写完想要注册的用户名后,通过布隆过滤器显示用户名已经存在,需要用户重新拟定用户名,这时候通过布隆过滤器进行返回的用户名已经存在,存在小概率的误判可能(用户名没有人注册过,但是布隆过滤器进行误判了),这时候用户自己是不知道这有可能是误判的结果。

但是像电话号码这种就是属于不能允许有误判的可能,当用户输入自己的电话号码,明明这个电话号码没有被注册过,这里却显示被注册了,那么可能这款产品将面临被投诉的可能。这种情况下就是属于不能够允许低概率进行误判的情况,但是通过布隆过滤器在次场景下能够过滤掉电话号码本来就没有注册过的情况,避免了电话号码没有注册过的情况也需要在数据库中进行查找,当手机号判断存在时,这里的存在有可能是因为布隆过滤器误判的情况,所以说还需要在向数据库中进行查询。

布隆过滤器的实现

布隆过滤器也是对于set的复用,布隆过滤器的开辟空间的大小就是布隆过滤器的长度,通过静态常量_x和数据个数N共同控制布隆过滤器的长度,由于字符转整形的通过哈希函数进行返回的值一般都比较大,所以要将他们进行映射到布隆过滤器长度这个空间中,需要进行%一下。

布隆过滤器的测试

布隆过滤器的误判率将分为两种情况进行验证,即相似字符串的误判率完全不同的字符串的误判率

  • 相似字符串的误判率的测试思路

将原字符串进行加一个字符进行处理,并且字符串不进行重复,使得生成了一个包含原字符串但是和原字符串并不相同的字符串,将大量字符串的数据进行放到vector容器中,然后将vector容器中的数据依次进行映射到布隆过滤器中,在利用上面的思路进行生成一个和上面数据个数相同的字符串,并且和上面生成的大量字符串并不重复,然后将其放到另一个vector容器中,依次取数据和布隆过滤器中的数据进行比对是否存在,如果存在则是误判,进行计数,最后计算误判率即可。

  • 不相似字符串的误判率

重新定义一个字符串,使得和原字符串有较大的差异,然后进行包含重新定义的字符串的字符串,进行和布隆过滤器中字符串进行验证。

过程中出现的BUG

哈希切分

给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出 精确算法和近似算法   注:这里的query是查询字符串的意思,这里当成长字符串进行看待即可。

普通切分

假设平均一个query是50字节,那么100亿个query就是500亿个字节换算成内存空间大小有500G,我们只有1G的内存空间,将两个文件中的数据进行放入布隆过滤器不能满足要求,需要将文件进行切分成小文件后再将小文件中的数据放到布隆过滤器中,然后将另一个文件中也进行文件切割,然后将小文件和放入布隆过滤器中的数据进行比对,第二个文件中所有小文件和放入布隆过滤器中的小文件数据进行比对后,再将第一个文件中其余的小文件其中一个继续放到布隆过滤器中,依次进行。

像以上这种比对逻辑,效率十分低,能不能改善上面的比对逻辑将效率进行提高呢??

哈希切分

通过哈希切分将文件进行切分时通过哈希函数计算出对应的hashi,然后将将数据进入对应小文件中,另一个文件也是,然后将相对应位置的小文件进行比对即可,极大提高了比对效率。

哈希切割存在的问题

哈希切割是属于不均等的切割,可能存在某个小文件中的数据过多,小文件过大不能满足内存需求

哈希切割的优化

小文件过大分为两种情况,一种是小文件中数据存在某一个query的大量数据;另一种是小文件中存在大量不同的query。

解决办法:声明一个set/unordered set 进行遍历文件,如果出现抛异常,则说明冲突太多,小文件过大,是属于情况二,换一个哈希函数继续进行切割即可。如果没有抛异常,则说明是情况一,set/unordered set起到了去重的作用。

<think>好的,我现在需要回答用户关于位图的问题,包括概念、用法、示例和图解。首先,我得确保自己对位图的理解正确,然后按照用户的要求组织内容。用户特别提到要使用中文回答,注意数学表达式的格式,生成相关问题,并且在引用部分添加标识。 首先,位图(Bitmap)的基本概念。位图是一种使用位(bit)来存储数据的高效数据结构,通常用于表示布尔值集合,例如存在或不存在。每个元素对应一个位,0表示不存在,1表示存在。这种结构节省空间,特别是处理大量数据时。比如,如果有100万个元素,用位图只需要大约125KB,而用布尔数组则需要1MB,节省了八倍空间[^1][^3]。 接下来是用法部分。位图常用于快速查找、去重和排序。例如在数据库索引中,快速判断某行是否存在;网络爬虫中记录已访问的URL;还有布隆过滤器的基础结构。需要说明这些应用场景,并引用提供的参考资料中的相关内容,比如引用[1]提到的经典算法图解和引用[2]中的实际项目应用。 然后是示例部分。用户需要代码示例和图解。我可以用Python或类似语言展示位图的基本操作,比如初始化、设置位、检查位。同时,考虑如何用ASCII或文字描述图解。例如,展示一个8位的位图,说明每个位的索引和值。例如,位图[0,0,1,1,0,1,0,0]表示第2、3、5位被设置为1。这部分需要确保代码正确,符合LaTeX格式要求,比如变量使用$...$包裹。 图解部分可能需要用文字描述,因为实际图片无法插入,但可以用字符画或表格形式表示。例如: 索引: 0 1 2 3 4 5 6 7 值:  0 0 1 1 0 1 0 0 这样用户能直观看到位的排列。 接下来,生成相关问题。根据用户之前的示例,可能的问题包括位图的优缺点、应用场景、与其他数据结构的比较,以及具体实现中的技术细节。确保问题与内容相关,帮助用户深入理解。 最后,检查引用是否正确。用户提供的引用[1]和[2]涉及数据结构算法的实际应用,引用[3]关于数据表示方式,可以联系到位图如何高效表示数据。在适当的地方添加引用标记,比如在提到空间效率时引用[3],在应用场景提到开源项目时引用[2]。 需要确保所有数学表达式如位运算使用$...$格式,例如$bitmap[5] = 1$。独立公式如计算空间占用的公式用$$...$$,例如存储n个元素的空间是$\lceil n/8 \rceil$字节。同时,代码块需要用正确的语法高亮,比如Python代码用```python包裹。 现在整理结构:先介绍概念,再讲用法,示例代码和图解,相关问题。确保各部分符合要求,引用正确,格式无误。可能还需要验证代码的正确性,比如位操作的实现是否正确,是否有错误处理,比如处理超出范围的索引。 例如,Python中的位图可以用字节数组实现,每个字节8位。设置位时,计算所在的字节位置和位偏移。检查位时,使用位与操作。这些细节需要在示例代码中体现,同时添加注释帮助理解。 最后,总结位图的优势和适用场景,确保用户理解其适用性和限制,比如位图适合稠密数据且元素范围不大的情况,稀疏数据可能不太适合,这时候其他结构如哈希表更优。 确认所有要点都已覆盖,没有遗漏用户的需求,特别是示例和图解部分是否清晰,相关问题是否相关且有助于进一步学习。</think>### 位图(Bitmap)的概念与用法 **概念** 位图(Bitmap)是一种通过二进制位(bit)存储数据的高效数据结构,常用于表示布尔值集合(存在/不存在)。每个元素对应一个位,$0$ 表示“不存在”,$1$ 表示“存在”。位图特别适合处理大规模数据的去重、查询和统计操作,空间复杂度为 $O(n)$,但实际存储时每个元素仅占用 1 bit,远优于传统数组或哈希表。 **核心特性** - **空间高效**:存储 $n$ 个元素仅需 $\lceil n/8 \rceil$ 字节。 - **快速操作**:支持 $O(1)$ 时间复杂度的插入、删除和查询。 - **局限性**:元素范围较大时可能占用较多连续内存,稀疏数据场景效率较低[^2]。 --- ### 位图的典型应用 1. **去重与存在性检查** 如数据库索引中快速判断某行是否存在[^1]。 2. **布隆过滤器基础** 结合哈希函数实现概率型数据结构,用于缓存穿透防护。 3. **数据压缩与排序** 位图排序(Bitmap Sort)可对无重复整数高效排序[^3]。 --- ### 位图实现示例(Python) ```python class Bitmap: def __init__(self, size): self.size = size self.bitmap = bytearray((size + 7) // 8) # 按字节分配空间 def set_bit(self, pos): if pos >= self.size: raise IndexError("位置超出范围") byte_idx = pos // 8 bit_idx = pos % 8 self.bitmap[byte_idx] |= (1 << bit_idx) def get_bit(self, pos): if pos >= self.size: return False byte_idx = pos // 8 bit_idx = pos % 8 return (self.bitmap[byte_idx] & (1 << bit_idx)) != 0 # 示例用法 bm = Bitmap(10) bm.set_bit(2) bm.set_bit(5) print(bm.get_bit(2)) # 输出: True print(bm.get_bit(3)) # 输出: False ``` --- ### 图解说明 假设位图大小为 8 位,初始状态为全 0: $$ \text{索引: } 0\ 1\ 2\ 3\ 4\ 5\ 6\ 7 \\ \text{值: }\ 0\ 0\ 0\ 0\ 0\ 0\ 0\ 0 $$ 设置第 2 位和第 5 位后: $$ \text{索引: } 0\ 1\ 2\ 3\ 4\ 5\ 6\ 7 \\ \text{值: }\ 0\ 0\ 1\ 0\ 0\ 1\ 0\ 0 $$ ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值