每个程序员都应该掌握的数据结构 – Bloom Filter

Bloom Filter

这种数据结构的名字来源于他的发明人Burton Howard Bloom的名字,凡是用自己名字命名的东西一般都非常牛逼,思维精巧,独步武林。

Bloom Filter跟hash有着紧密的关联。首先设想我们有一个比较大的数据集合,每一条记录有一个key,现在有一个需求是问给定一个key,这个集合包含不包含这个数据?我们不太可能不假思索的把整个数据集合load进内存中,因为可能存在多个这样的数据集合。最容易想到也最直接的想法是在内存中构造一个hash的结构保存所有已经存在的key,这其实已经能够解决绝大多数的问题了,但是有没有更好的呢?乍一看对普通人来说不太容易找到突破口,这确实需要非凡的智慧来打破定式思维。Bloom Filter就设计了这样一种思路,它找到了一种折中,以一定概率的错误回答来实现比常规hash表小的多的内存使用量。直白的来说就是当我们问数据集包含不包含key的数据的时候,如果它回答不包含,那100%数据集不包含,但是如果它回答包含的话,却不是一个确定的答案,我们需要进一步的策略去确定它是不是真的包含,牛逼的地方在于这个出错的概率我们是自己可以控制的。

让我们先从一个很笨的但是朴素的方法开始,假设我们知道数据集有1000万条数据,如果我们设计一种很差的hash算法,使得这1000万条数据只有1万个hash值,常规的hash表用链表的结构解决hash冲突的问题,所以即使只有1万个hash值,如果我们用常规的hash表来保存所有key在内存中的话,内存仍然是1000万个key的大小,如果我们的数据结构不解决hash冲突呢?只load这1万个hash值在内存中,那么当有询问一个key在不在这个集合中的时候,很明显如果hash(k)不在这1万个值中,那么这个key一定不在这个数据集合中,hash(k)在的话,那么有可能是包含这个key的,因为我们没解决hash冲突,很明显的直觉告诉我们hash值越多,回答出错的概率就会越低,但是如果沿着这个思路下去的话,我们依然会陷入死胡同,因为你的hash函数越完美,就越需要更多的内存来保存hash值。

让我们再次回到一个基本认知逻辑中,现实生活中,我们经常看到一些娱乐节目玩一种你说我猜的游戏,就是给定一个物品,一个人去描述它的特征,另一个人来回答它是什么,一种食物,圆的,中秋节吃的,那么很容易猜到是月饼,我们模仿这种思路用一组hash函数hash1,hash2 …来为一个key算出一组hash值,然后用一种有效的数据结构来保存这组hash值,当再有询问一个key在不在的时候,我们同时判断hash1(key),hash2(key)…都在不在已经我们的的数据结构里来回答这个key在不在,我们依然依靠直觉这样的判断应该出错的几率会降低了,谈到有效的,内存敏感的,数相关的数据结构我们应该马上会回想到bitset,Bloom Filter正是依赖着这种数据结构来存储所有的hash值,每个hash(key)都对应着一个bit位。

现在让我们更准确的定义这种数据结构,给定n个元素的集合,k个hash函数,m大小bitset来存储所有的hash值,使得当询问一个key是否在集合中的时候以确定的概率p回答错误。以下只包含经过了严格的数学证明的结论,我们程序员可以直接拿来使用。

n和p是我们可以决定的,当我们选定这两个参数以后,下列公式可以帮我们确定m,

根据概率确定m

当m确定后,我们用下列公式确定k,


根据m和n确定k

当这些变量都确定后,我们需要去设计一组hash函数,我们可以直接拿算法导论Designing a universal class of hash functions章节去实现我们的k个hash函数。

你可能感兴趣的:(每个程序员都应该掌握的数据结构 – Bloom Filter)