【重学算法】位图与布隆过滤器

242 阅读2分钟

2021.05.12

学习于极客时间[time.geekbang.org/column/arti…]

位图 位图是通过将数组下标与应用中的一些值关联映射,数组中该下标所指定的位置上的元素可以用来标识值的情况。

位图代码

`public class BitMap { // Java中char类型占2个字节,即16bit private char[] bytes; private int nbits;

public BitMap(int nbits) { this.nbits = nbits; this.bytes = new char[nbits/16+1]; }

public void set(int k) { if (k > nbits) return; int byteIndex = k / 16; int bitIndex = k % 16; bytes[byteIndex] |= (1 << bitIndex); }

public boolean get(int k) { if (k > nbits) return false; int byteIndex = k / 16; int bitIndex = k % 16; return (bytes[byteIndex] & (1 << bitIndex)) != 0; } }` (2) 布隆过滤器

布隆过滤器的原理是,当一个元素被加入集合时,通过K个哈希函数将这个元素映射成一个位数组中的K个点,把它们置为1。检索时,我们只要看看这些点是不是都是1就大约知道集合中有没有它了:如果这些点有任何一个0,则被检元素一定不在;如果都是1,则被检元素很可能在。

布隆过滤器为了降低哈希冲突的概率,使用 K 个哈希函数,对同一个数字进行求哈希值,那会得到 K 个不同的哈希值,我们分别记作 X1​,X2​,X3​,…,XK​。我们把这 K 个数字作为位图中的下标,将对应的 BitMap[X1​],BitMap[X2​],BitMap[X3​],…,BitMap[XK​]都设置成 true,也就是说,我们用 K 个二进制位,来表示一个数字的存在。当我们要查询某个数字是否存在的时候,我们用同样的 K 个哈希函数,对这个数字求哈希值,分别得到 Y1​,Y2​,Y3​,…,YK​。我们看这 K 个哈希值,对应位图中的数值是否都为 true,如果都是 true,则说明,这个数字存在,如果有其中任意一个不为 true,那就说明这个数字不存在。布隆过滤器非常适合不需要 100% 准确的、允许存在小概率误判的大规模判重场景。

网页爬虫是搜索引擎中的非常重要的系统,同一个网页链接有可能被包含在多个页面中,这就会导致爬虫在爬取的过程中重复爬取相同的网页,那么该如何避免这些重复的爬取呢?

我们用布隆过滤器 (CPU 密集型)来记录已经爬取过的网页链接,假设需要判重的网页有 10 亿,那我们可以用一个 10 倍大小的位图(用以减少误判)来存储,也就是 100 亿个二进制位,换算成字节,那就是大约 1.2GB。如果用散列表判重,需要至少 100GB 的空间。相比来讲,布隆过滤器在存储空间的消耗上,降低了非常多。