Python+Redis实现布隆过滤器


Posted in Python onDecember 08, 2019

布隆过滤器是什么

布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。

布隆过滤器的基本思想

通过一种叫作散列表(又叫哈希表,Hash table)的数据结构。它可以通过一个Hash函数将一个元素映射成一个位阵列(Bit array)中的一个点。这样一来,我们只要看看这个点是不是1就可以知道集合中有没有它了

布隆过滤器的优缺点

优点:

1.布隆过滤器的存储空间和插入/查询时间都是常数

2.Hash函数相互之间没有关系,方便由硬件并行实现

3.布隆过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势

4.布隆过滤器可以表示全集,其它任何数据结构都不能

缺点:

1.存在一定的误算率,随着存入的元素数量增加,误算率随之增加

(常见的补救办法是建立一个小的白名单,存储那些可能被误判的元素。但是如果元素数量太少,则使用散列表足矣)

2.一般情况下不能从布隆过滤器中删除元素

首先我们必须保证删除的元素的确在布隆过滤器里面. 这一点单凭这个过滤器是无法保证的。另外计数器回绕也会造成问题。这就导致删除元素需要很高的成本。

正文

简单的python实现

pip install mmh3

对于安装报错,c++编译错误问题:可以安装    Microsoft Visual C++ Build Tools()

 例子转载(https://3water.com/article/175929.htm)

from bitarray import bitarray
# 3rd party
import mmh3
class BloomFilter(set):
 def __init__(self, size, hash_count):
 super(BloomFilter, self).__init__()
 self.bit_array = bitarray(size)
 self.bit_array.setall(0)
 self.size = size
 self.hash_count = hash_count
 def __len__(self):
 return self.size
 def __iter__(self):
 return iter(self.bit_array)
 def add(self, item):
 for ii in range(self.hash_count):
  index = mmh3.hash(item, ii) % self.size
  self.bit_array[index] = 1
 return self
 def __contains__(self, item):
 out = True
 for ii in range(self.hash_count):
  index = mmh3.hash(item, ii) % self.size
  if self.bit_array[index] == 0:
  out = False
 return out
def main():
 bloom = BloomFilter(10000, 10)
 animals = ['dog', 'cat', 'giraffe', 'fly', 'mosquito', 'horse', 'eagle',
  'bird', 'bison', 'boar', 'butterfly', 'ant', 'anaconda', 'bear',
  'chicken', 'dolphin', 'donkey', 'crow', 'crocodile']
 # First insertion of animals into the bloom filter
 for animal in animals:
 bloom.add(animal)
 # Membership existence for already inserted animals
 # There should not be any false negatives
 for animal in animals:
 if animal in bloom:
  print('{} is in bloom filter as expected'.format(animal))
 else:
  print('Something is terribly went wrong for {}'.format(animal))
  print('FALSE NEGATIVE!')
 # Membership existence for not inserted animals
 # There could be false positives
 other_animals = ['badger', 'cow', 'pig', 'sheep', 'bee', 'wolf', 'fox',
   'whale', 'shark', 'fish', 'turkey', 'duck', 'dove',
   'deer', 'elephant', 'frog', 'falcon', 'goat', 'gorilla',
   'hawk' ]
 for other_animal in other_animals:
 if other_animal in bloom:
  print('{} is not in the bloom, but a false positive'.format(other_animal))
 else:
  print('{} is not in the bloom filter as expected'.format(other_animal))
if __name__ == '__main__':
 main()

运行结果

dog is in bloom filter as expected
cat is in bloom filter as expected
giraffe is in bloom filter as expected
fly is in bloom filter as expected
mosquito is in bloom filter as expected
horse is in bloom filter as expected
eagle is in bloom filter as expected
bird is in bloom filter as expected
bison is in bloom filter as expected
boar is in bloom filter as expected
butterfly is in bloom filter as expected
ant is in bloom filter as expected
anaconda is in bloom filter as expected
bear is in bloom filter as expected
chicken is in bloom filter as expected
dolphin is in bloom filter as expected
donkey is in bloom filter as expected
crow is in bloom filter as expected
crocodile is in bloom filter as expected

badger is not in the bloom filter as expected
cow is not in the bloom filter as expected
pig is not in the bloom filter as expected
sheep is not in the bloom, but a false positive
bee is not in the bloom filter as expected
wolf is not in the bloom filter as expected
fox is not in the bloom filter as expected
whale is not in the bloom filter as expected
shark is not in the bloom, but a false positive
fish is not in the bloom, but a false positive
turkey is not in the bloom filter as expected
duck is not in the bloom filter as expected
dove is not in the bloom误报 filter as expected
deer is not in the bloom filter as expected
elephant is not in the bloom, but a false positive
frog is not in the bloom filter as expected
falcon is not in the bloom filter as expected
goat is not in the bloom filter as expected
gorilla is not in the bloom filter as expected
hawk is not in the bloom filter as expected

 从输出结果可以发现,存在不少误报样本,但是并不存在假阴性。

不同于这段布隆过滤器的实现代码,其它语言的多个实现版本并不提供哈希函数的参数。这是因为在实际应用中误报比例这个指标比哈希函数更重要,用户可以根据误报比例的需求来调整哈希函数的个数。通常来说,size和error_rate是布隆过滤器的真正误报比例。如果你在初始化阶段减小了error_rate,它们会调整哈希函数的数量。

误报

布隆过滤器能够拍着胸脯说某个元素“肯定不存在”,但是对于一些元素它们会说“可能存在”。针对不同的应用场景,这有可能会是一个巨大的缺陷,亦或是无关紧要的问题。如果在检索元素是否存在时不介意引入误报情况,那么你就应当考虑用布隆过滤器。

另外,如果随意地减小了误报比率,哈希函数的数量相应地就要增加,在插入和查询时的延时也会相应地增加。本节的另一个要点是,如果哈希函数是相互独立的,并且输入元素在空间中均匀的分布,那么理论上真实误报率就不会超过理论值。否则,由于哈希函数的相关性和更频繁的哈希冲突,布隆过滤器的真实误报比例会高于理论值。

在使用布隆过滤器时,需要考虑误报的潜在影响。

确定性

当你使用相同大小和数量的哈希函数时,某个元素通过布隆过滤器得到的是正反馈还是负反馈的结果是确定的。对于某个元素x,如果它现在可能存在,那五分钟之后、一小时之后、一天之后、甚至一周之后的状态都是可能存在。当我得知这一特性时有一点点惊讶。因为布隆过滤器是概率性的,那其结果显然应该存在某种随机因素,难道不是吗?确实不是。它的概率性体现在我们无法判断究竟哪些元素的状态是可能存在。

换句话说,过滤器一旦做出可能存在的结论后,结论不会发生变化。

python 基于redis实现的bloomfilter(布隆过滤器),BloomFilter_imooc

BloomFilter_imooc下载

下载地址:https://github.com/liyaopinner/BloomFilter_imooc

 py_bloomfilter.py(布隆过滤器)源码:

import mmh3
import redis
import math
import time
class PyBloomFilter():
 #内置100个随机种子
 SEEDS = [543, 460, 171, 876, 796, 607, 650, 81, 837, 545, 591, 946, 846, 521, 913, 636, 878, 735, 414, 372,
  344, 324, 223, 180, 327, 891, 798, 933, 493, 293, 836, 10, 6, 544, 924, 849, 438, 41, 862, 648, 338,
  465, 562, 693, 979, 52, 763, 103, 387, 374, 349, 94, 384, 680, 574, 480, 307, 580, 71, 535, 300, 53,
  481, 519, 644, 219, 686, 236, 424, 326, 244, 212, 909, 202, 951, 56, 812, 901, 926, 250, 507, 739, 371,
  63, 584, 154, 7, 284, 617, 332, 472, 140, 605, 262, 355, 526, 647, 923, 199, 518]
 #capacity是预先估计要去重的数量
 #error_rate表示错误率
 #conn表示redis的连接客户端
 #key表示在redis中的键的名字前缀
 def __init__(self, capacity=1000000000, error_rate=0.00000001, conn=None, key='BloomFilter'):
 self.m = math.ceil(capacity*math.log2(math.e)*math.log2(1/error_rate)) #需要的总bit位数
 self.k = math.ceil(math.log1p(2)*self.m/capacity)    #需要最少的hash次数
 self.mem = math.ceil(self.m/8/1024/1024)     #需要的多少M内存
 self.blocknum = math.ceil(self.mem/512)     #需要多少个512M的内存块,value的第一个字符必须是ascii码,所有最多有256个内存块
 self.seeds = self.SEEDS[0:self.k]
 self.key = key
 self.N = 2**31-1
 self.redis = conn
 # print(self.mem)
 # print(self.k)
 def add(self, value):
 name = self.key + "_" + str(ord(value[0])%self.blocknum)
 hashs = self.get_hashs(value)
 for hash in hashs:
  self.redis.setbit(name, hash, 1)
 def is_exist(self, value):
 name = self.key + "_" + str(ord(value[0])%self.blocknum)
 hashs = self.get_hashs(value)
 exist = True
 for hash in hashs:
  exist = exist & self.redis.getbit(name, hash)
 return exist
 def get_hashs(self, value):
 hashs = list()
 for seed in self.seeds:
  hash = mmh3.hash(value, seed)
  if hash >= 0:
  hashs.append(hash)
  else:
  hashs.append(self.N - hash)
 return hashs
pool = redis.ConnectionPool(host='127.0.0.1', port=6379, db=0)
conn = redis.StrictRedis(connection_pool=pool)
# 使用方法
# if __name__ == "__main__":
# bf = PyBloomFilter(conn=conn)  # 利用连接池连接Redis
# bf.add('www.jobbole.com')  # 向Redis默认的通道添加一个域名
# bf.add('www.luyin.org')   # 向Redis默认的通道添加一个域名
# print(bf.is_exist('www.zhihu.com')) # 打印此域名在通道里是否存在,存在返回1,不存在返回0
# print(bf.is_exist('www.luyin.org')) # 打印此域名在通道里是否存在,存在返回1,不存在返回0

总结

以上所述是小编给大家介绍的Python+Redis实现布隆过滤器,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!

Python 相关文章推荐
Python import用法以及与from...import的区别
May 28 Python
Python六大开源框架对比
Oct 19 Python
Python中绑定与未绑定的类方法用法分析
Apr 29 Python
Python中规范定义命名空间的一些建议
Jun 04 Python
python 2.6.6升级到python 2.7.x版本的方法
Oct 09 Python
对pandas中两种数据类型Series和DataFrame的区别详解
Nov 12 Python
selenium处理元素定位点击无效问题
Jun 12 Python
python实现websocket的客户端压力测试
Jun 25 Python
python开启debug模式的方法
Jun 27 Python
PyQt5中QTableWidget如何弹出菜单的示例代码
Feb 23 Python
在pycharm中创建django项目的示例代码
May 28 Python
关于Python3的import问题(pycharm可以运行命令行import错误)
Nov 18 Python
PyCharm 2019.3发布增加了新功能一览
Dec 08 #Python
使用tqdm显示Python代码执行进度功能
Dec 08 #Python
Python tkinter实现图片标注功能(完整代码)
Dec 08 #Python
Python中six模块基础用法
Dec 08 #Python
python实现布隆过滤器及原理解析
Dec 08 #Python
python实现图片二值化及灰度处理方式
Dec 07 #Python
matplotlib实现显示伪彩色图像及色度条
Dec 07 #Python
You might like
PHP高自定义性安全验证码代码
2011/11/27 PHP
PHP判断远程图片或文件是否存在的实现代码
2014/02/20 PHP
PHP采用自定义函数实现遍历目录下所有文件的方法
2014/08/19 PHP
php使用PDO获取结果集的方法
2017/02/16 PHP
php使用curl模拟浏览器表单上传文件或者图片的方法
2018/11/10 PHP
关于php unset对json_encode的影响详解
2018/11/14 PHP
php+redis实现消息队列功能示例
2019/09/19 PHP
js自带函数备忘 数组
2006/12/29 Javascript
两个DIV等高的JS的实现代码
2007/12/23 Javascript
jquery下操作HTML控件的实现代码
2010/01/12 Javascript
jQuery.extend 函数详解
2012/02/03 Javascript
javascript动态加载三
2012/08/22 Javascript
JavaScript初学者建议:不要去管浏览器兼容
2014/02/04 Javascript
使用ajax+jqtransform实现动态加载select
2014/12/01 Javascript
jQuery中slice()方法用法实例
2015/01/07 Javascript
JavaScript判断图片是否已经加载完毕的方法汇总
2016/02/05 Javascript
JS触发服务器控件的单击事件(详解)
2016/08/06 Javascript
chrome下判断点击input上标签还是其余标签的实现方法
2016/09/18 Javascript
AngularJS 过滤器(自带和自建)详解
2016/09/19 Javascript
JavaScript实现美化滑块效果
2019/05/17 Javascript
vue获取验证码倒计时组件
2019/08/26 Javascript
VUE+elementui面包屑实现动态路由详解
2019/11/04 Javascript
详细分析vue表单数据的绑定
2020/07/20 Javascript
在Python中使用Neo4j数据库的教程
2015/04/16 Python
使用Python来编写HTTP服务器的超级指南
2016/02/18 Python
Python 私有函数的实例详解
2017/09/11 Python
Python实现手写一个类似django的web框架示例
2018/07/20 Python
python实现beta分布概率密度函数的方法
2019/07/08 Python
python进程间通信Queue工作过程详解
2019/11/01 Python
python中查看.db文件中表格的名字及表格中的字段操作
2020/07/07 Python
Anaconda+spyder+pycharm的pytorch配置详解(GPU)
2020/10/18 Python
美国体育用品商店:Paragon Sports
2017/10/08 全球购物
医学生个人求职信范文
2013/09/24 职场文书
战略合作意向书
2014/07/29 职场文书
婚礼父母答谢词
2015/01/04 职场文书
2019客服个人年终工作总结范文
2019/07/08 职场文书