Python各种模块互联网科技爬虫专题

布隆过滤器 Bloom Filter

2017-06-26  本文已影响152人  未不明不知不觉

基本概念

如果想要判断一个元素是不是在一个集合里,一般想到的是将所有元素保存起来,然后通过比较确定。链表,树等等数据结构都是这种思路. 但是随着集合中元素的增加,我们需要的存储空间越来越大,检索速度也越来越慢(O(n),O(logn))。不过世界上还有一种叫作散列表(又叫哈希表,Hash table)的数据结构。它可以通过一个Hash函数将一个元素映射成一个位阵列(Bit array)中的一个点。这样一来,我们只要看看这个点是不是1就可以知道集合中有没有它了。这就是布隆过滤器的基本思想。
Hash面临的问题就是冲突。假设Hash函数是良好的,如果我们的位阵列长度为m个点,那么如果我们想将冲突率降低到例如 1%, 这个散列表就只能容纳m / 100个元素。显然这就不叫空间效率了(Space-efficient)了。解决方法也简单,就是使用多个Hash,如果它们有一个说元素不在集合中,那肯定就不在。如果它们都说在,虽然也有一定可能性它们在说谎,不过直觉上判断这种事情的概率是比较低的

优点

相比于其它的数据结构,布隆过滤器在空间和时间方面都有巨大的优势。布隆过滤器存储空间和插入/查询时间都是常数。另外, Hash函数相互之间没有关系,方便由硬件并行实现。布隆过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势。

缺点

布隆过滤器的缺点和优点一样明显。误算率是其中之一。随着存入的元素数量增加,误算率随之增加。但是如果元素数量太少,则使用散列表足矣。
另外,一般情况下不能从布隆过滤器中删除元素。我们很容易想到把位列阵变成整数数组,每插入一个元素相应的计数器加1, 这样删除元素时将计数器减掉就可以了。然而要保证安全的删除元素并非如此简单。首先我们必须保证删除的元素的确在布隆过滤器里面. 这一点单凭这个过滤器是无法保证的。另外计数器回绕也会造成问题

误判

构建布隆过滤器时我们可以自定义两个参数,和。m和 k

如果我们有一个具有m比特和k个哈希函数的布隆过滤器,插入一次后某一位仍然为零的概率是


然后,插入n次后,仍为零的概率为


那么这就意味着误判的概率是


在这些方程的每一个中,提高k值(散列函数的数量)将使误判的概率降低。然而 k值很大并不能线性的降低误判率。在这里我们假设程序员已经确定了他们要使用的空间大小m,他们大概知道数据的规模是多大 n 。那么最小化这个方程的值是


应用

网页URL的去重,垃圾邮件的判别,集合重复元素的判别,查询加速(比如基于key-value的存储系统)等

相关理论

程序示例

import hashlib


def h1(data):
    h = hashlib.sha256(data)
    return hash(h.digest().encode('base64'))


def h2(data):
    h = hashlib.sha1(data)
    return hash(h.digest().encode('base64'))


class BloomFilter(object):
    def __init__(self, capacity, num_of_hash):
        self.capacity = capacity
        self.data = [0] * capacity
        self.num = num_of_hash
        self.n = 0

    def insert(self, element):
        if self.num == 1:
            has1 = h1(element)%self.capacity
            self.data[has1] = 1
        elif self.num ==2:
            has1 = h1(element)%self.capacity
            has2 = h2(element)%self.capacity
            self.data[has1] =1 
            self.data[has2] = 1
        self.n+=1
    def search(self,element):
        if self.num ==1:
            has1 = h1(element)%self.capacity
            if self.data[has1] ==0:
                return "Not in Filter"
        elif self.num ==2:
            has1 = h1(element)%self.capacity
            has2 = h2(element)%self.capacity
            if self.data[has2]==1 and self.data[has1]==0:
                return "Not in Filter"
        prob = (1.0 - ((1.0 - 1.0/self.capacity)**(self.num*self.n))) ** self.num
        return 'Mabye be in Filter with false probability{}'.format(prob)

def main():
    bloom = BloomFilter(10,2)
    bloom.insert('Hello')
    print(bloom.data)
    print(bloom.search('Hello'))
    print(bloom.search('World'))

if __name__ == '__main__':
    main()

输出

[1, 0, 0, 0, 0, 0, 0, 0, 0, 1]
Mabye be in with false 0.0361
Not in Filter
上一篇 下一篇

猜你喜欢

热点阅读