栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

实现位映射与布隆过滤器

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

实现位映射与布隆过滤器

1. 位映射 1.1为什么要用位映射

少量的数据可以存储在arry或者link中,需要查找的时候,把所有的数据都读到内存里,然后用for去遍历。但是有大量的数据的话,内存就放不下了。比如,在10亿个不重复的int中查找一个数。

1.2初步构思

把int映射到bit上,由于二者大小相差32倍,所以可以节省大量空间。

1.3有个问题需要解决

因为计算机很多底层,例如IO是以byte位单位传输数据,所以java以及很多编程语言并没有bit[ ]。所以需要用byte[ ]代劳一下,此处以一个byte数组存储1-8的int为例。说明byte[ ]是如何代替bit[ ]存储数组的。

1.4代码
  1. new一个byte[2^29]. int有2 ^32种可能,即需要一个bit[2 ^32]。一个byte=8个bit,所以除以2 ^3
  2. 计算int的对应的bit[ ] 上的位置,也是int的顺序位置。int的范围为[-2^31 ,2^31], 但是bit[ ]索引的范围应为(0, 2^ 32)。所以要将int + 2^31。(是+不是*)
  3. 首先求出int在哪个byte中。因为bit和byte是8倍的关系,所以将1.中的索引/8。
  4. 最后求出int在byte内部的索引。1.中的索引/8求余数。

位运算技巧:
1向左推n位,就是2的n次方
想取第n位,就向右推n-1位,再&1
想在num的第n位加入1,就将1向左推n-1位,再 | num

public class BinMap {

    byte[] bytes=new byte[1<<29];

    public void add(int num){

        
        long bitIndex=num+(1l<<31);
        int byteInd= (int) (bitIndex/8);
        int inInd=(int) (bitIndex%8);
        bytes[byteInd]= (byte) (bytes[byteInd] | (1 << (inInd-1)));
    }

    public boolean retrieve(int num){

        long bitIndex=num+(1l<<31);
        int byteInd=(int)(bitIndex/8);
        int inInd=(int)(bitIndex%8);
        if (((bytes[byteInd] >> (inInd-1))&1)==1){
            return true;
        }else {
            return false;
        }

    }
}
2. BloomFilter 2.1布隆过滤器原理

如下图所示,将一个要存储的数据,使用n个不同的hash函数计算n个hashCode,然后储存在 1.位映射 中的byte中。
为什么要用多个哈希函数呢? 虽然多个哈希函数会带来哈希冲突,会造成误差。但同时,这种误差会缩小byte[ ]所需要的空间,毕竟只是个过滤器,并不要求有多精确。所以在可容忍误差下减小所占用的空间是可行的。

2.2误差问题

上文中能很明显的看到一个问题,存入的数据越多,bloomFilter的误差越大。当然,也可以通过加长byte[ ]来减小误差。换言之,布隆过滤器说某个元素存在,小概率会误判。布隆过滤器说某个元素不在,那么这个元素一定不在。
具体的假阳性率,从网上抄了一个公式:

k: hash函数的个数;n: 已存入数据的个数;m: byte[ ]的长度;p: 误报率
再实际业务中,可以使用如下公式来确定k and m:

2.3使用场景

bloomfilter有如下特点:

  • 上文提到的误差特点。
  • 多个hash函数可以使用多线程来提高效率。
  • 增加和查询操作的复杂度都是常数,能实现以空间换时间
  • 过滤器的byte[ ]只储存了0,1,而没储存具体的值,所以安全性更好
  • 删除很难。比如"123"映射在byte[1],[2],[3] ,“234"映射在byte[2],[3],[4] 。如果删除了"123”, 那么"234" 会很尴尬~

该方法并不适合在严密的计算中使用,但是由于其假阳性率高,假阴性率低的特点,可以对请求进行初步的过滤(所以叫做过滤器),以减小真正查询操作的压力。例如:

  • 防止缓存穿透,即先过滤查询请求,如通过,再提交给数据库进行查询
  • 邮箱啥的黑名单过滤(所以邮箱黑名单屏蔽总那么不靠谱)
  • 去重,比如爬给定网址的时候对已经爬取过的 URL 去重,避免给用户推送重复内容
2.4代码
  1. 弄若干个不同的hash算法
  2. 计算hashCode
  3. 存入数组
public class BloomFilter {

    BinMap map=new BinMap();

    public void add(int num){
        int[] hash=hash(num);
        for (int i=0;i
            map.add(hash[i]);
        }
    }

    public boolean retrieve(int num){
        boolean f=true;
        int[] hash=hash(num);
        for (int i=0;i
            f=f&map.retrieve(hash[i]);
        }
        return f;
    }

    public int[] hash(int num){
        int[] ints=new int[3];
        ints[0]=Math.abs(13 * (33554432 - 1) & num);
        ints[1]=Math.abs(46 * (33554432 - 1) & num);
        ints[2]=Math.abs(71 * (33554432 - 1) & num);
        return ints;
    }
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1034740.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号