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实现屏幕截图的两种方式
Feb 05 Python
使用python存储网页上的图片实例
May 22 Python
Python 删除连续出现的指定字符的实例
Jun 29 Python
解决python中无法自动补全代码的问题
Dec 04 Python
django的settings中设置中文支持的实现
Apr 28 Python
python3 打印输出字典中特定的某个key的方法示例
Jul 06 Python
Python使用Tkinter实现滚动抽奖器效果
Jan 06 Python
python模拟实现斗地主发牌
Jan 07 Python
python mysql 字段与关键字冲突的解决方式
Mar 02 Python
在python image 中实现安装中文字体
May 16 Python
python线程优先级队列知识点总结
Feb 28 Python
pd.drop_duplicates删除重复行的方法实现
Jun 16 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/05 新手入门
PHP反射类ReflectionClass和ReflectionObject的使用方法
2013/11/13 PHP
PHP获取文件扩展名的4种方法
2015/11/24 PHP
自定义Laravel (monolog)日志位置,并增加请求ID的实现
2019/10/17 PHP
TP5框架model常见操作示例小结【增删改查、聚合、时间戳、软删除等】
2020/04/05 PHP
多个iframe自动调整大小的问题
2006/09/18 Javascript
准确获得页面、窗口高度及宽度的JS
2006/11/26 Javascript
点图片上一页下一页翻页效果
2008/07/09 Javascript
Js 代码中,ajax请求地址后加随机数防止浏览器缓存的原因
2013/05/07 Javascript
js使用eval解析json实例与注意事项分享
2014/01/18 Javascript
jQuery选择器源码解读(四):tokenize方法的Expr.preFilter
2015/03/31 Javascript
JavaScript数组各种常见用法实例分析
2015/08/04 Javascript
基于jquery实现页面滚动到底自动加载数据的功能
2015/12/19 Javascript
json格式数据的添加,删除及排序方法
2016/01/21 Javascript
Bootstrap 组件之按钮(二)
2016/05/11 Javascript
详解jQuery停止动画——stop()方法的使用
2016/12/14 Javascript
微信小程序http连接访问解决方案的示例
2018/11/05 Javascript
Vue组件模板及组件互相引用代码实例
2020/03/11 Javascript
[06:45]DOTA2卡尔工作室 英雄介绍幻影长矛手篇
2013/07/12 DOTA
[48:12]Secret vs Optic Supermajor 胜者组 BO3 第三场 6.4
2018/06/05 DOTA
Python脚本实现网卡流量监控
2015/02/14 Python
详解python脚本自动生成需要文件实例代码
2017/02/04 Python
python实现折半查找和归并排序算法
2017/04/14 Python
python中日志logging模块的性能及多进程详解
2017/07/18 Python
python利用正则表达式排除集合中字符的功能示例
2017/10/10 Python
Django使用 Bootstrap 样式修改书籍列表过程解析
2019/08/09 Python
python3实现带多张图片、附件的邮件发送
2019/08/10 Python
解决 jupyter notebook 回车换两行问题
2020/04/15 Python
python文件读取失败怎么处理
2020/06/23 Python
锐步香港官方网上商店:Reebok香港
2020/11/05 全球购物
迟到检讨书大全
2014/01/25 职场文书
全国法院系统开展党的群众路线教育实践活动综述(全文)
2014/10/25 职场文书
诚信承诺书
2015/01/19 职场文书
让生命充满爱观后感
2015/06/08 职场文书
如何用JavaScript检测当前浏览器是无头浏览器
2021/04/27 Javascript
html中两种获取标签内的值的方法
2022/06/16 jQuery