• 为了保证你在浏览本网站时有着更好的体验,建议使用类似Chrome、Firefox之类的浏览器~~
    • 如果你喜欢本站的内容何不Ctrl+D收藏一下呢,与大家一起分享各种编程知识~
    • 本网站研究机器学习、计算机视觉、模式识别~当然不局限于此,生命在于折腾,何不年轻时多折腾一下

布隆过滤器-转载

Alg admin 3年前 (2017-04-26) 1159次浏览 0个评论 扫描二维码

哈希 hash

原理

Hash (哈希,或者散列)函数在计算机领域,尤其是数据快速查找领域,加密领域用的极广。

其作用是将一个大的数据集映射到一个小的数据集上面(这些小的数据集叫做哈希值,或者散列值)

一个应用是 Hash table(散列表,也叫哈希表),是根据哈希值 (Key value) 而直接进行访问的数据结构。也就是说,它通过把哈希值映射到表中一个位置来访问记录,以加快查找的速度。下面是一个典型的 hash 函数 / 表示意图:

哈希函数有以下两个特点:

  • 如果两个散列值是不相同的(根据同一函数),那么这两个散列值的原始输入也是不相同的。
  • 散列函数的输入和输出不是唯一对应关系的,如果两个散列值相同,两个输入值很可能是相同的。但也可能不同,这种情况称为 “散列碰撞”(或者 “散列冲突”)。

缺点: 引用吴军博士的《数学之美》中所言,哈希表的空间效率还是不够高。如果用哈希表存储一亿个垃圾邮件地址,每个 email 地址 对应 8bytes, 而哈希表的存储效率一般只有 50%,因此一个 email 地址需要占用 16bytes. 因此一亿个 email 地址占用 1.6GB,如果存储几十亿个 email address 则需要上百 GB 的内存。除非是超级计算机,一般的服务器是无法存储的。

所以要引入下面的 Bloom Filter。

布隆过滤器 Bloom Filter

原理

如果想判断一个元素是不是在一个集合里,一般想到的是将集合中所有元素保存起来,然后通过比较确定。链表、树、散列表(又叫哈希表,Hash table)等等数据结构都是这种思路。但是随着集合中元素的增加,我们需要的存储空间越来越大。同时检索速度也越来越慢。

Bloom Filter 是一种空间效率很高的随机数据结构,Bloom filter 可以看做是对 bit-map 的扩展, 它的原理是:

当一个元素被加入集合时,通过 KHash 函数将这个元素映射成一个位阵列(Bit array)中的 K 个点,把它们置为 1。检索时,我们只要看看这些点是不是都是 1 就(大约)知道集合中有没有它了:

  • 如果这些点有任何一个 0,则被检索元素一定不在
  • 如果都是 1,则被检索元素很可能在。

优点

It tells us that the element either definitely is not in the set or may be in the set.

它的优点是空间效率查询时间都远远超过一般的算法,布隆过滤器存储空间和插入 / 查询时间都是常数O(k)。另外, 散列函数相互之间没有关系,方便由硬件并行实现。布隆过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势。

缺点

但是布隆过滤器的缺点和优点一样明显。误算率是其中之一。随着存入的元素数量增加,误算率随之增加。但是如果元素数量太少,则使用散列表足矣。

(误判补救方法是:再建立一个小的白名单,存储那些可能被误判的信息。)

另外,一般情况下不能从布隆过滤器中删除元素. 我们很容易想到把位数组变成整数数组,每插入一个元素相应的计数器加 1, 这样删除元素时将计数器减掉就可以了。然而要保证安全地删除元素并非如此简单。首先我们必须保证删除的元素的确在布隆过滤器里面. 这一点单凭这个过滤器是无法保证的。另外计数器回绕也会造成问题。

Example

可以快速且空间效率高的判断一个元素是否属于一个集合;用来实现数据字典,或者集合求交集。

如: Google chrome 浏览器使用 bloom filter 识别恶意链接(能够用较少的存储空间表示较大的数据集合,简单的想就是把每一个 URL 都可以映射成为一个 bit)
得多,并且误判率在万分之一以下。
又如: 检测垃圾邮件

假定我们存储一亿个电子邮件地址,我们先建立一个十六亿二进制(比特),即两亿字节的向量,然后将这十六亿个二进制全部设置为零。对于每一个电子邮件地址 X,我们用八个不同的随机数产生器(F1,F2, ...,F8) 产生八个信息指纹(f1, f2, ..., f8)。再用一个随机数产生器 G 把这八个信息指纹映射到 1 到十六亿中的八个自然数 g1, g2, ...,g8。现在我们把这八个位置的二进制全部设置为一。当我们对这一亿个 email 地址都进行这样的处理后。一个针对这些 email 地址的布隆过滤器就建成了。

再如此题:

A,B 两个文件,各存放 50 亿条 URL,每条 URL 占用 64 字节,内存限制是 4G,让你找出 A,B 文件共同的 URL。如果是三个乃至 n 个文件呢?

分析 :如果允许有一定的错误率,可以使用 Bloom filter,4G 内存大概可以表示 340 亿 bit。将其中一个文件中的 url 使用 Bloom filter 映射为这 340 亿 bit,然后挨个读取另外一个文件的 url,检查是否与 Bloom filter,如果是,那么该 url 应该是共同的 url(注意会有一定的错误率)。”

Network applications of Bloom Filter: a survey

python 代码实践

 

#_*_coding:utf_8_  
import BitVector  
import os  
import sys  
  
class SimpleHash():    
      
    def __init__(self, capability, seed):  
        self.capability = capability  
        self.seed = seed  
  
    #传入的 value 即为 url 值,ord(value[i])表示第 i 位字符的 ascii 码值  
    def hash(self, value):  
        ret = 0  
        for i in range(len(value)):  
            ret += self.seed*ret + ord(value[i])  
        #最终产生的随机数是二进制向量最大下标与随机数的按位与结果  
        return (self.capability-1) & ret      
  
class BloomFilter():  
      
    def __init__(self, BIT_SIZE=1<<25):  
        self.BIT_SIZE = 1 << 25  
        self.seeds = [5, 7, 11, 13, 31, 37, 61]  
        #建立一个大小为 1<<25=33554432 位的二进制向量,分配内存  
        self.bitset = BitVector.BitVector(size=self.BIT_SIZE)  
        self.hashFunc = []  
        #利用 8 个素数初始化 8 个随机数生成器  
        for i in range(len(self.seeds)):  
            self.hashFunc.append(SimpleHash(self.BIT_SIZE, self.seeds[i]))  
          
    def insert(self, value):  
        for f in self.hashFunc:  
            loc = f.hash(value)  
            self.bitset[loc] = 1  
    def isContaions(self, value):  
        if value == None:  
            return False  
        ret = True  
        for f in self.hashFunc:  
            loc = f.hash(value)  
            #用同样的随机数产生方法对比相应位的二进制值,只要发现有一个不同即返回结果为假  
            ret = ret & self.bitset[loc]  
            if ret==False:  
                return ret  
        #只有当 8 个二进制位都相等时才返回真  
        return ret  
  
def main():  
    fd = open("urls.txt")  
    bloomfilter = BloomFilter()  
    while True:  
        #url = raw_input()  
        url = fd.readline()  
        if cmp(url, 'exit') == 0: #if 'exit' then break  
            break  
        if bloomfilter.isContaions(url) == False:  
            bloomfilter.insert(url)  
        else:  
            print 'url :%s has exist' % url   
              
main()  

Deeplearn, 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明布隆过滤器-转载
喜欢 (0)
admin
关于作者:
互联网行业码农一枚/业余铲屎官/数码影音爱好者/二次元

您必须 登录 才能发表评论!