开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 5 天,点击查看活动详情
在处理海量数据的算法问题,我们可能遇到这样的问题:
比如这里有一个包含40亿无符号整数文件,但是我们范围是0~42亿,最大使用1gb,怎么找到文件里面没出现的数?
内存限制10MB怎么找到一个没有出现的数?
如果只能申请有限几个变量,怎么找到缺失?
看到这个数据量,是不是一下子脑子蒙了?离谱了,这要怎么做?
最简单做法是使用hash直接循环遍历,但是想也知道多离谱,粗略一看,至少要300多亿B内存。这就需要用到布隆过滤器了。因为hash内存和不同数出现有关。
什么是布隆过滤器?
百度一下,感觉好像懂了,又好像没懂。看完还是没多大概念。
只需要记住:我们可以把它看作由二进制向量(或者说位数组)和一系列随机映射函数(哈希函数)两部分组成的数据结构
布隆过滤器其实就是位数组。了解布隆过滤器之前,我们要了解hash底层原理。
通过hash可以把40亿数,基本上通过hash平均分到m长度文件里面。然后把缺失的m提出,把m当作a再次hash,最终解决该问题。
为什么通过hash就平均分了?
在我看来,做hash其实和取随机数很类似,我们可以这样想,1-99里面,1出现了18次(1,11,21,31,41,51,61,71,81,91,12,13,14,15,16,17,18,19),2出现了19次(2,12,22,32,42,52,62,72,82,92,20,21,23,24,25,26,27,28,29),3出现了。。。。
也就是我们如果把1-99放入长度为10的数组链表里面,几乎每个数组对应长度相等(差的不多,当数据量足够大,理论上是相等的)。为什么?因为每个数字出现概率是相等的,大家公平竞争,哈哈。
解题思路
怎么把基本数据类型变成位图?
public static void fang() {
int a=0;
// a--->32bit(4字节,一个字节8bit)
int[] arr=new int[10]; //32bit*10-》存储320bit的信息
// arr[0] int 0-31
// arr[1] int 32-63
// ...
int i=123;//拿到第123位的信息
int numIndex=123/32;//定义到去哪个数寻找
int bitIndex=123%32;//定义位
// 拿到状态
// 找到数,右移bit位,和1作与
int s=((arr[numIndex]>>bitIndex) &1);
// 把当前位变1
arr[numIndex]=arr[numIndex]|(1<<bitIndex);
// 把当前位变0
arr[numIndex]=arr[numIndex]&(~(1<<bitIndex));
}
int[] arr=new int[10]:定义了数组,即需要模10才能既然数组。一个int类型,包含32bit,那么足够数组,就存储了320bit数据。
123/32,123%32:假设我们需要找i的信息,那么通过除32,我们可以知道是哪一个数组下标,然后通过模32,可以字段是下标里面的哪一位。
数组存储的不是值:是范围数。
做布隆过滤器,关键是数组长度怎么取
但是在平时问题里面,它会限制内存,那么我们就可以按照限制内存大小做数组。
那么10gb就很明显。再苛刻一点。只用3kb内存
3kb变成整数-》3kb/4 接近700多。和他相近的是2^9(512),所以我们数组一个下标对应范围1~42亿/512
这样第一次做完后,看到会有一个数组下标对应范围不够 42亿/512 再把他取出来。把他长度除512,再次得到不够的....如此循环,我们就可以找到缺失值了。
如果只能申请有限几个变量,怎么找到缺失?
使用二分法:(数组长度为2)
按照42亿/2 分左边右边,小的继续除2,往复循环。
布隆过滤器使用场景
解决黑名单问题,爬虫去重复。也就是:解决只有添加和查询,没有删除的集合。
当碰到有样本量和失误率的问题时候,我们可以看它:
- 是不是类似黑名单
- 可不可以有失误率,可以——》布隆过滤器模型
- 得到失误率