在日常生活中
![](/icons/62961dou.gif)
包括在设计计算机软件Software时
![](/icons/62961dou.gif)
我们经常要判断
![](/icons/62961yi.gif)
个元素是否在
![](/icons/62961yi.gif)
个集合中
![](/icons/62961dou2.gif)
比如在字处理软件Software中
![](/icons/62961dou.gif)
需要检查
![](/icons/62961yi.gif)
个英语单词是否拼写正确(也就是要判断它是否在已知
![](/icons/62961de.gif)
字典中);在 FBI
![](/icons/62961dou.gif)
![](/icons/62961yi.gif)
个嫌疑人
![](/icons/62961de.gif)
名字是否已经在嫌疑名单上;在网络爬虫里
![](/icons/62961dou.gif)
![](/icons/62961yi.gif)
个网址是否被访问过等等
![](/icons/62961dou2.gif)
最直接
![](/icons/62961de.gif)
思路方法就是将集合中全部
![](/icons/62961de.gif)
元素存在计算机中
![](/icons/62961dou.gif)
遇到
![](/icons/62961yi.gif)
个新元素时
![](/icons/62961dou.gif)
将它和集合中
![](/icons/62961de.gif)
元素直接比较即可
![](/icons/62961dou2.gif)
![](/icons/62961yi.gif)
般来讲
![](/icons/62961dou.gif)
计算机中
![](/icons/62961de.gif)
集合是用哈希表(hash table)来存储
![](/icons/62961de.gif)
![](/icons/62961dou2.gif)
它
![](/icons/62961de.gif)
好处是快速准确
![](/icons/62961dou.gif)
缺点是费存储空间
![](/icons/62961dou2.gif)
当集合比较小时
![](/icons/62961dou.gif)
这个问题不显著
![](/icons/62961dou.gif)
但是当集合巨大时
![](/icons/62961dou.gif)
哈希表存储效率低
![](/icons/62961de.gif)
问题就显现出来了
![](/icons/62961dou2.gif)
比如说
![](/icons/62961dou.gif)
![](/icons/62961yi.gif)
个象 Yahoo,Hotmail 和 Gmai 那样
![](/icons/62961de.gif)
公众电子邮件(email)提供商
![](/icons/62961dou.gif)
总是需要过滤来自发送垃圾邮件
![](/icons/62961de.gif)
人(spamer)
![](/icons/62961de.gif)
垃圾邮件
![](/icons/62961dou2.gif)
![](/icons/62961yi.gif)
个办法就是记录下那些发垃圾邮件
![](/icons/62961de.gif)
email 地址
![](/icons/62961dou2.gif)
由于那些发送者不停地在注册新
![](/icons/62961de.gif)
地址
![](/icons/62961dou.gif)
全世界少说也有几十亿个发垃圾邮件
![](/icons/62961de.gif)
地址
![](/icons/62961dou.gif)
将他们都存起来则需要大量
![](/icons/62961de.gif)
网络服务器
![](/icons/62961dou2.gif)
如果用哈希表
![](/icons/62961dou.gif)
每存储
![](/icons/62961yi.gif)
亿个 email 地址
![](/icons/62961dou.gif)
就需要 1.6GB
![](/icons/62961de.gif)
内存(用哈希表实现
![](/icons/62961de.gif)
具体办法是将每
![](/icons/62961yi.gif)
个 email 地址对应成
![](/icons/62961yi.gif)
个 8字节
![](/icons/62961de.gif)
信息指纹
![](/icons/62961dou.gif)
然后将这些信息指纹存入哈希表
![](/icons/62961dou.gif)
由于哈希表
![](/icons/62961de.gif)
存储效率
![](/icons/62961yi.gif)
般只有 50%
![](/icons/62961dou.gif)
因此
![](/icons/62961yi.gif)
个 email 地址需要占用十 6个字节
![](/icons/62961dou2.gif)
![](/icons/62961yi.gif)
亿个地址大约要 1.6GB
![](/icons/62961dou.gif)
即十 6亿字节
![](/icons/62961de.gif)
内存)
![](/icons/62961dou2.gif)
因此存贮几十亿个邮件地址可能需要上百 GB
![](/icons/62961de.gif)
内存
![](/icons/62961dou2.gif)
除非是超级计算机
![](/icons/62961dou.gif)
![](/icons/62961yi.gif)
般服务器是无法存储
![](/icons/62961de.gif)
![](/icons/62961dou2.gif)
今天
![](/icons/62961dou.gif)
我们介绍
![](/icons/62961yi.gif)
种称作布隆过滤器
![](/icons/62961de.gif)
数学工具
![](/icons/62961dou.gif)
它只需要哈希表 1/8 到 1/4
![](/icons/62961de.gif)
大小就能解决同样
![](/icons/62961de.gif)
问题
![](/icons/62961dou2.gif)
布隆过滤器是由巴顿.布隆于
![](/icons/62961yi.gif)
9 7零年提出
![](/icons/62961de.gif)
![](/icons/62961dou2.gif)
它实际上是
![](/icons/62961yi.gif)
个很长
![](/icons/62961de.gif)
2进制向量和
![](/icons/62961yi.gif)
系列随机映射
![](/icons/62961hanshu.gif)
![](/icons/62961dou2.gif)
我们通过上面
![](/icons/62961de.gif)
例子来介绍说明起工作原理
![](/icons/62961dou2.gif)
假定我们存储
![](/icons/62961yi.gif)
亿个电子邮件地址
![](/icons/62961dou.gif)
我们先建立
![](/icons/62961yi.gif)
个十 6亿 2进制(比特)
![](/icons/62961dou.gif)
即两亿字节
![](/icons/62961de.gif)
向量
![](/icons/62961dou.gif)
然后将这十 6亿个 2进制全部设置为零
![](/icons/62961dou2.gif)
对于每
![](/icons/62961yi.gif)
个电子邮件地址 X
![](/icons/62961dou.gif)
我们用 8个区别
![](/icons/62961de.gif)
随机数产生器(F1,F2, ...,F8) 产生 8个信息指纹(f1, f2, ..., f8)
![](/icons/62961dou2.gif)
再用
![](/icons/62961yi.gif)
个随机数产生器 G 把这 8个信息指纹映射到 1 到十 6亿中
![](/icons/62961de.gif)
8个自然数 g1, g2, ...,g8
![](/icons/62961dou2.gif)
现在我们把这 8个位置
![](/icons/62961de.gif)
2进制全部设置为
![](/icons/62961yi.gif)
![](/icons/62961dou2.gif)
当我们对这
![](/icons/62961yi.gif)
亿个 email 地址都进行这样
![](/icons/62961de.gif)
处理后
![](/icons/62961dou2.gif)
![](/icons/62961yi.gif)
个针对这些 email 地址
![](/icons/62961de.gif)
布隆过滤器就建成了
![](/icons/62961dou2.gif)
(见下图)
![](http://CrazyCoder.cn/WebFiles/20101/15bf4fb9-c5c8-48be-9463-1f969a69ef57.jpg)
现在
![](/icons/62961dou.gif)
让我们看看如何用布隆过滤器来检测
![](/icons/62961yi.gif)
个可疑
![](/icons/62961de.gif)
电子邮件地址 Y 是否在黑名单中
![](/icons/62961dou2.gif)
我们用相同
![](/icons/62961de.gif)
8个随机数产生器(F1, F2, ..., F8)对这个地址产生 8个信息指纹 s1,s2,...,s8
![](/icons/62961dou.gif)
然后将这 8个指纹对应到布隆过滤器
![](/icons/62961de.gif)
8个 2进制位
![](/icons/62961dou.gif)
分别是 t1,t2,...,t8
![](/icons/62961dou2.gif)
如果 Y 在黑名单中
![](/icons/62961dou.gif)
显然
![](/icons/62961dou.gif)
t1,t2,..,t8 对应
![](/icons/62961de.gif)
8个 2进制
![](/icons/62961yi.gif)
定是
![](/icons/62961yi.gif)
![](/icons/62961dou2.gif)
这样在遇到任何在黑名单中
![](/icons/62961de.gif)
电子邮件地址
![](/icons/62961dou.gif)
我们都能准确地发现
![](/icons/62961dou2.gif)
布隆过滤器决不会漏掉任何
![](/icons/62961yi.gif)
个在黑名单中
![](/icons/62961de.gif)
可疑地址
![](/icons/62961dou2.gif)
但是
![](/icons/62961dou.gif)
它有
![](/icons/62961yi.gif)
条不足的处
![](/icons/62961dou2.gif)
也就是它有极小
![](/icons/62961de.gif)
可能将
![](/icons/62961yi.gif)
个不在黑名单中
![](/icons/62961de.gif)
电子邮件地址判定为在黑名单中
![](/icons/62961dou.gif)
![](/icons/62961yinwei.gif)
有可能某个好
![](/icons/62961de.gif)
邮件地址正巧对应个 8个都被设置成
![](/icons/62961yi.gif)
![](/icons/62961de.gif)
2进制位
![](/icons/62961dou2.gif)
好在这种可能性很小
![](/icons/62961dou2.gif)
我们把它称为误识概率
![](/icons/62961dou2.gif)
在上面
![](/icons/62961de.gif)
例子中
![](/icons/62961dou.gif)
误识概率在万分的
![](/icons/62961yi.gif)
以下
布隆过滤器
![](/icons/62961de.gif)
好处在于快速
![](/icons/62961dou.gif)
省空间
![](/icons/62961dou2.gif)
但是有
![](/icons/62961yi.gif)
定
![](/icons/62961de.gif)
误识别率
![](/icons/62961dou2.gif)
常见
![](/icons/62961de.gif)
补救办法是在建立
![](/icons/62961yi.gif)
个小
![](/icons/62961de.gif)
白名单
![](/icons/62961dou.gif)
存储那些可能别误判
![](/icons/62961de.gif)
邮件地址