位图和布隆过滤器

位图概念

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

例:给40个亿不重复的无符号整数,没排过序,给一个无符号整数,如何快速判断是否在这40亿个数中。

1G=2^30=10亿Byte,40亿整数占用空间=40x4Byte=160亿Byte=16G

但我们用char类型把每个bit位标识一个整数值,1Byte可以标识8个数,那么只需要16/4/8=0.5G=512MB

位图的应用

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

缺点:  只能处理整形数据

size_t i = x / 8计算x映射的bit在第i个char数组位置 
size_t j = x % 8计算x映射的bit在这个char的第j个比特位

代码实现设置bit位,以及重置bit位

set

把1左右j位,然后得到00…010…00,用这个数和整数进行按位或的操作

reset

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

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

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

判断是否存在这个数

这个数的bit位置和第i个整数进行按位与的结果

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

布隆过滤器概念

如何快速查找?

  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的效率还是很高的,基本上是常数次。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值