位图和布隆过滤器

文章介绍了位图的概念及其在判断数据存在性上的应用,包括设置、清除和检查位的方法。接着,文章转向布隆过滤器,一种结合哈希表和位图优势的数据结构,用于减少冲突并高效地判断元素是否存在,尽管存在误判可能性。文章还讨论了布隆过滤器的删除问题及应用场景,并提供了近似和精确查找交集的解决方案。
摘要由CSDN通过智能技术生成

gitee仓库:https://gitee.com/WangZihao64/data-structure-and-algorithm/tree/master/BloomFilter

位图概念

所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用来判断某个数据存不存在的。

在这里插入图片描述

代码实现

把某一位设置为1

  1. 找到x所处在的那一位: 先确定这个数应该处在第几个整数,也就是index=x/8;然后通过把x对8取模,得到x在第几号的第pos个位置

  2. 把改为设置为1: 把1左右j位,然后得到00…010…00,用这个数和整数进行按位或的操作

void set(size_t x)
        {
            int i=x/8;
            int j=x%8;
            _bits[i]|=(1<<j);
        }

把某一位设置为0

1.找到那一位: 和上面的方法一样

2.把这位设置为0: 把1左右pos位,然后得到00…010…00,把这个数按位取反(避免改变其他操作),然后和第index个整数进行按位与的操作

void reset(size_t x)
        {
            int i=x/8;
            int j=x%8;
            _bits[i]&=~(1<<j);
        }

判断某一位是否为1

1.找到那一位: 和上面的方法一样

2.把这位设置为0: 把1左右pos位,然后得到00…010…00,然后返回这个数和第index个整数进行按位与的结果

bool test(size_t x)
        {
            int i=x/8;
            int j=x%8;
            return _bits[i]&(1<<j);
        }

位图的应用

  1. 快速查找某个数据是否在一个集合中
  2. 排序+去重
  3. 操作系统中磁盘的标记等

缺点:
只能处理整形数据

应用举例

1.给定100亿个整数,设计算法找到只出现一次的整数?

思路:使用两个位图,如果一次都没出现两个位图都是0,0但是如果出现一次就把第第二个位图变为1,即0,1,如果出现多次就把第一个变为1,第二个恢复,即1,0,然后遍历位图即可

2.给两个文件,分别有100亿个整数,我们只有1G内存,如何找到文件的交集

给自放进一个位图里,(找交集记得先去重),同时在2个位图就是交集

3.一个文件有100亿个int,1G内存,设计算法,找出出现次数不超过2次的所有整数

和题目1是一样的,0次是00,1次是01,2次是10,3次及以上是11

  1. 给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址? 如何找到top K的IP?如何直接用Linux系统命令实现?

这个用位图就做不了了,我们可以使用哈希切割,先创建100个小文件A0-A99,然后计算i = hash(IP)%100,i是多少,IP就进入编号为i的文件中,也就是Ai文件中,这样相同的IP就都进入了同一个文件中。先将一个小文件加载到内存中,依次读取放入unordered_map<string, int> 中,同时用一个pair<string, int> max记录当前出现次数最多的IP,然后不断更新记录当前的max,这样就得到出现次数最多的IP地址

布隆过滤器概念

如何快速查找?

  1. 哈希表: 用哈希表存储用户的历史记录。缺点: 空间消耗比较大
  2. 位图: 用无图存储用户的历史记录。 缺点: 不能处理哈希冲突问题
  3. 布隆过滤器: 将哈希表和位图结合使用。

布隆过滤器实际上是一个很长的二进制向量和一系列随机映射函数。它通过多个哈希函数将一个数据映射到位图的结构中(也就是一个数据映射位图的多个位置,这样就可以减少冲突的概率)。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难

在这里插入图片描述

这里放置3个哈希函数,用了映射不同的概率。

在这里插入图片描述

k 为哈希函数个数
m 为布隆过滤器长度
n 为插入的元素个数
p 为可接受该容器的误报率(0-1)

其中k=3,整体算下来,m≈4.34n,所以这里我们选择m为5。

代码实现

插入

  1. 先用不同的哈希函数计算出该数据分别要映射到位图的哪几个位置
  2. 然后把位图中的这几个位置设置为1
void set(const K& key)
        {
            size_t hash1=HashFunc1()(key)%(N*5);
            size_t hash2=HashFunc2()(key)%(N*5);
            size_t hash3=HashFunc3()(key)%(N*5);
            _bs.set(hash1);
            _bs.set(hash2);
            _bs.set(hash3);
        }

查找

  1. 先用不同的哈希函数计算出该数据分别要映射到位图的哪几个位置
  2. 然后判断位图中的这几个位置是否都为1,如果有一个不为1,说明该元素一定不在容器中,否则表示在容器中

注意:
可能会误报,判断在是不准确的,判断不在是准确的。(因为一个数据判断出它是在的,可能是它映射的几个数据可能是其它数据映射导致这几个位置为1的,所以判断结果为在,该结果有误判;而判断一个数据为不在时,那这个数据是一定不在的,因为它映射的几个位置不全为1)

bool test(const K& key)
        {
            //布隆过滤器如果查到这个值出现过,不一定真正出现过,但是没出现过就一定没出现过
            size_t hash1=HashFunc1()(key)%(N*5);
            if(!_bs.test(hash1))
            {
                return false;
            }
            size_t hash2=HashFunc2()(key)%(N*5);
            if(!_bs.test(hash2))
            {
                return false;
            }
            size_t hash3=HashFunc3()(key)%(N*5);
            if(!_bs.test(hash3))
            {
                return false;
            }
            return true;
        }

删除

布隆过滤器不能直接支持删除工作,因为在删除一个元素时,可能会影响其他元素。也就是说,要删除的元素映射的位置可能会是其它元素映射的位置,所以直接删除元素会给后期查找某个元素带来极大的误判。

解决:
可以考虑给把比特位(根据需求选择用多少个比特位)扩展成为一个小的计数器,插入元素时,给计数器加1,删除元素时,给计数器减1。当然这个肯定会带来更大的空间开销,牺牲了布隆过滤器的优势。且还可能会存在计数回绕的问题,如果次数超过计数器的范围,就会溢出。

布隆过滤器的应用

通常在如图情况中我们会使用布隆过滤器,不在的情况一定是准确的,但是在的话不一定准确,我们可以再到数据库中进行查找,不在的情况会大大提高效率

在这里插入图片描述

  1. 给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法

query是一个查询语句的意思,一个query语句的大小平均约为30-60字节100亿个query大约占用300-600G,100亿个字节是10G
方案一(近似算法): 将文件1的query映射到布隆过滤器中,读取文件2中的query,判断是否在布隆过滤器中,在就是交集,是交集的一定在里面。(缺陷:交集中的数会不准确,因为有些数会误判,混到交集里面,判断在是不准确的,判断不在是准确的)
方案二(精确算法):
1.把文件1和文件2分别切分成A0、A1…A999和B0、B1…B999,两个文件分别切分成1000个小文件,因为哈希可能映射不是很均匀,然后将A0加载到内存放到unordered_set中,再依次和B0、B1…B999这10001个文件进行比较,然后加载A1到内存中,以此类推。用unordered_set的效率还是很高的,基本上是常数次。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值