写在前面,本文参考了guide哥的 bloom-filter 以及左神在牛客上的 算法入门课程。由于布隆过滤器在现在实在是太常用了,因此这个知识在面试中经常问到,最后我会查阅并且总结一下常见的面试题目以及相应的思路。
bloom提出的布隆过滤器,由两部分组成——哈希函数以及位数组,其中位数组如下所示:
位数组中的每个元素都只占用 1 bit ,并且每个元素只能是 0 或者 1。这样申请一个 100w 个元素的位数组只占用 1000000Bit / 8 = 125000 Byte = 125000/1024 kb ≈ 122kb 的空间。
比特类型的数组,不同于int long类型的数组。但是在实现的时候,可以用int类型的数组来拼凑,也就是说一个32个bit的int类型的数字,实际上是占用了32个bit。
如上所说,布隆过滤器可以判断极大容量的样本中是否存在某个值,并且对空间特别友好;相比之下,如果是哈希hashmap,那占用的内存空间将是极大的浪费,100亿个URL,每个URL64字节,需要用到640GB的内存。
注意: 布隆过滤器会多报,但是绝对不会导致漏报。
首先准备多个哈希函数,对于任何一个给定的字符串,都用哈希函数计算一遍得到一个哈希值hash,然后再准备一个长度是m的位数组 array,令 array [hash%m] = 1 (初始化的时候array等于全0)。然后对于所有要进行判定的URL重复上述这个过程,所有的URL字符串判定结束,布隆过滤器的构建阶段就完成了。
对于 k 个哈希函数以及位数组长度是等于m的布隆过滤器来说:
如果m比较小,那么失误率肯定是非常大的,因为只需要几个值就能把所有的位置给描黑。如果m特别大,能达到很好的准确率,因为构建整个布隆过滤器之后得到的位数组的1值是相当稀疏的,此时多报率就会比较客观,但是于此同时,如果m特别大,那需要花费的内存空间就上升。 因此存在一个合理的m值使得所需内存容量可以接受并且失误率也是满足系统要求的。如果 k 比较少,那么采纳到的字符串的特征就比较少,此时错误率也会上升。如果k比较大,字符串的特征是充分采集到了,但是对于长度为m 的位数组来说,就很容易达到一个饱和的 1 值填充状态,同样可能导致错误率上升。 因此存在一个合理的k ,使得k个哈希函数的情况下,布隆过滤器的过滤效果最佳。