下面的是网络上找到的python的布隆过滤器的实现.
#!/usr/local/bin/python2.7#coding=gbk
'''
Created on 2012-11-7
@author: palydawn
'''
import cmath
from BitVector import BitVector
class BloomFilter(object):
def __init__(self, error_rate, elementNum):
#计算所需要的bit数
self.bit_num = -1 * elementNum * cmath.log(error_rate) / (cmath.log(2.0) * cmath.log(2.0))
#四字节对齐
self.bit_num = self.align_4byte(self.bit_num.real)
#分配内存
self.bit_array = BitVector(size=self.bit_num)
#计算hash函数个数
self.hash_num = cmath.log(2) * self.bit_num / elementNum
self.hash_num = self.hash_num.real
#向上取整
self.hash_num = int(self.hash_num) + 1
#产生hash函数种子
self.hash_seeds = self.generate_hashseeds(self.hash_num)
def insert_element(self, element):
for seed in self.hash_seeds:
hash_val = self.hash_element(element, seed)
#取绝对值
hash_val = abs(hash_val)
#取模,防越界
hash_val = hash_val % self.bit_num
#设置相应的比特位
self.bit_array[hash_val] = 1
#检查元素是否存在,存在返回true,否则返回false
def is_element_exist(self, element):
for seed in self.hash_seeds:
hash_val = self.hash_element(element, seed)
#取绝对值
hash_val = abs(hash_val)
#取模,防越界
hash_val = hash_val % self.bit_num
#查看值
if self.bit_array[hash_val] == 0:
return False
return True
#内存对齐
def align_4byte(self, bit_num):
num = int(bit_num / 32)
num = 32 * (num + 1)
return num
#产生hash函数种子,hash_num个素数
def generate_hashseeds(self, hash_num):
count = 0
#连续两个种子的最小差值
gap = 50
#初始化hash种子为0
hash_seeds = []
for index in xrange(hash_num):
hash_seeds.append(0)
for index in xrange(10, 10000):
max_num = int(cmath.sqrt(1.0 * index).real)
flag = 1
for num in xrange(2, max_num):
if index % num == 0:
flag = 0
break
if flag == 1:
#连续两个hash种子的差值要大才行
if count > 0 and (index - hash_seeds[count - 1]) < gap:
continue
hash_seeds[count] = index
count = count + 1
if count == hash_num:
break
return hash_seeds
def hash_element(self, element, seed):
hash_val = 1
for ch in str(element):
chval = ord(ch)
hash_val = hash_val * seed + chval
return hash_val
'''
#测试代码
bf = BloomFilter(0.001, 1000000)
element = 'palydawn'
bf.insert_element(element)
print bf.is_element_exist('palydawn')'''
#其中使用了BitVector库,python本身的二进制操作看起来很麻烦,这个就简单多了
如果解决了您的问题请采纳!
如果未解决请继续追问
如果使用哈希表,内存空间需要100亿*64字节=640G(10亿字节(byte)是1G),空间就爆掉了。此时就轮到布隆过滤器登场了。
布隆过滤器应用场景:黑名单 爬虫去重
布隆过滤器优势:节省内存(30G以内),查询速度快
布隆过滤器劣势:存在一定失误率
但布隆过滤器的失误率是可以容忍的,一是可以通过设计人为的把失误率控制的很低;二是就算失误了不会误报已经在黑名单里的URL。布隆过滤器只会把正常的URL当成黑名单系统里的,但不会误报已经在黑名单里的URL。形象点说就是“宁可错杀三千不会放过一个”
在讲解布隆过滤器原理之前先讲位图。
位图是bit类型的数组。 int类型4字节即32bit,所以长度100的int类型数组可以看出长度3200的bit数组。假如要找1782位比特,那么在int数组里下标是1782/32,在下标里存的int数字里第几个比特位?即1782%32的计算结果,从而把整型数组操作转换成比特类型数组操作。
布隆过滤器即大位图,假设是长度为m的bit数组,那么占用m/8位字节(Byte),
URL加黑名单过程:开始时m位比特都是0(白)的状态,该URL通过哈希函数f1得到一个哈希值,然后%m,得到0~m-1上某个位置,将该位置描黑(变成1),再用哈希函数f2得到一个哈希值,再描黑,再用哈希函数f3同样处理(f1、f2、f3是独立的哈希函数),假设一共用了k个哈希函数,那么描黑了k个位置(某个位置重复了就重复了,但一旦描黑就没有描白的时候)完成后可以说该URL加入了位图里。对下一个URL同样处理,用k个哈希函数描黑k个位置……一直做100亿个,位图中有相当多位置被描黑了。
如何查某个URL在不在黑名单里?把待查的URL用K个哈希函数算出对应的哈希值,再把该哈希值%m,把K个位置的状态都拿出来,如果全黑,就说这个URL属于黑名单系统,如果有一个是白,就不属于黑名单系统。如果m很小,100亿个URL之后所有位图都是黑的,那么不论查谁都在黑名单里;如果m取的大一些,失误率就会降低。
但布隆过滤器需要准备多少个bit位和单样本的大小无关。一个URL经过K个哈希函数得到K个哈希值,对m取模后去相应的大比特map中描黑,只要保证哈希函数能接收单样本这种类型的参数就可以了,与样本是64字节还是128字节无关。所以影响失误率的重要参数就是样本量N和位图比特数组长度m。
布隆过滤器三公式: 出处
1.确定m:对于输入的数据量n(这里是100亿)和失误率p(这里是万分之一),布隆过滤器的大小m:m = - (n lnp) / (ln2 ln2)
2.确定K:K假如很少,例如只有一个哈希函数,相当于每个数据只采集了一个特征点,只把一个位置描黑,查的时候也只用一个哈希函数,特征点不够,失误率虽然不至于很高但有一定的量,如果K很大,例如用10万个哈希函数去把10万个位置描黑,m的空间会接近耗尽,查什么URL都是黑的,失误率非常高。需要的哈希函数的个数k:k = ln2 * m/n = 0.7 * m/n
3.因为前两步中公式1公式2都会进行向上取整,所以公式3算出的实际的失误率与比预期失误率要低
布隆过滤器在Hadoop中的应用:Hadoop中的分布式文件系统,是由许多小文件组成的,如何查询一个数据在哪个文件里?首先不可能记录每个小文件的索引,这样做占用空间太大了。所以每个小文件里都搞一个布隆过滤器,当Hadoop想知道某个key在哪个文件里,就查每一个布隆过滤器,文件a的布隆过滤器会说明你这个key在我这个文件里,但可能会有误报;文件c的布隆过滤器会说明你这个key在我这个文件里,但可能会有误报……如果失误率很低,哪怕有几万个分布式文件,最终可能算出来只有3个文件里可能含有这个key。那么就只用把这3个小文件遍历一遍,就知道key在哪个小文件里了。通俗点讲,如果一个文件真的含有key,那么它的布隆过滤器会说这个key属于我;但因为有失误率,可能会发生一个文件不含有这个key,它还是会说这个key属于我;可是这也没关系,多查一遍就可以,反正失误率很低,需要遍历的文件很少。