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之wxPython菜单使用详解
Sep 28 Python
Python 使用requests模块发送GET和POST请求的实现代码
Sep 21 Python
查看django执行的sql语句及消耗时间的两种方法
May 29 Python
对python同一个文件夹里面不同.py文件的交叉引用方法详解
Dec 15 Python
python执行精确的小数计算方法
Jan 21 Python
Django uwsgi Nginx 的生产环境部署详解
Feb 02 Python
python selenium登录豆瓣网过程解析
Aug 10 Python
python 实现按对象传值
Dec 26 Python
如何将PySpark导入Python的放实现(2种)
Apr 26 Python
Python devel安装失败问题解决方案
Jun 09 Python
python3 简单实现组合设计模式
Jul 02 Python
python实现图片素描效果
Sep 26 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
精致的人儿就要挑杯子喝咖啡
2021/03/03 冲泡冲煮
十天学会php(3)
2006/10/09 PHP
php设计模式 Factory(工厂模式)
2011/06/26 PHP
php使用curl存储cookie的示例
2014/03/31 PHP
浅谈PHP解析URL函数parse_url和parse_str
2014/11/11 PHP
PHP模拟QQ登录的方法
2015/07/29 PHP
javascript 选择文件夹对话框(web)
2009/07/07 Javascript
jQuery 获取和设置select下拉框的值实现代码
2013/11/08 Javascript
JS中的数组的sort方法使用示例
2014/01/22 Javascript
jQuery:delegate中select()不起作用的解决方法(实例讲解)
2014/01/26 Javascript
jquery日历控件实现方法分享
2014/03/07 Javascript
web前端设计师们常用的jQuery特效插件汇总
2014/12/07 Javascript
深入浅析JavaScript中的Function类型
2016/07/09 Javascript
jQuery简单实现列表隐藏和显示效果示例
2016/09/12 Javascript
通过jquery实现页面的动画效果(实例代码)
2016/09/18 Javascript
关于在vue-cli中使用微信自动登录和分享的实例
2017/06/22 Javascript
如何在JS文件中获取Vue组件
2020/09/16 Javascript
利用Python获取操作系统信息实例
2016/09/02 Python
Python简单过滤字母和数字的方法小结
2019/01/09 Python
python 弹窗提示警告框MessageBox的实例
2019/06/18 Python
django中forms组件的使用与注意
2019/07/08 Python
天猫超市:阿里巴巴打造的网上超市
2016/11/02 全球购物
美国折扣网站:jClub
2017/08/07 全球购物
英国日常交易网站:Wowcher
2018/09/04 全球购物
TheFork葡萄牙:欧洲领先的在线餐厅预订平台
2019/05/27 全球购物
会计电算化专业毕业生自荐信
2013/12/20 职场文书
土木工程专业推荐信
2014/02/19 职场文书
《蚂蚁和蝈蝈》教学反思
2014/02/24 职场文书
公休请假条
2014/04/11 职场文书
企业演讲稿范文大全
2014/05/20 职场文书
导师就业推荐信范文
2014/05/22 职场文书
公司离职证明范本(汇总)
2014/09/10 职场文书
乡镇党的群众路线教育实践活动个人整改方案
2014/10/31 职场文书
帝企鹅日记观后感
2015/06/10 职场文书
2015年团委副书记工作总结
2015/07/23 职场文书
Python实现简单得递归下降Parser
2022/05/02 Python