Python中的现代、高性能bloom过滤器?
我正在寻找一个Python中的生产质量BloomFilter实现来处理相当多的项目(比如100到1B个项目,误报率为0.01%) 是一个选项,但它似乎显示了它的年龄,因为它定期在Python2.5上抛出DeprecationWarning错误。乔·格雷戈里奥也有 要求快速查找性能和稳定性。我也愿意为特别好的c/c++实现创建Python接口,如果有好的Java实现,甚至可以创建Jython接口 如果没有这一点,关于能够处理~16E9位的位数组/位向量表示的任何建议?请查看模块Python中的现代、高性能bloom过滤器?,python,jython,bloom-filter,Python,Jython,Bloom Filter,我正在寻找一个Python中的生产质量BloomFilter实现来处理相当多的项目(比如100到1B个项目,误报率为0.01%) 是一个选项,但它似乎显示了它的年龄,因为它定期在Python2.5上抛出DeprecationWarning错误。乔·格雷戈里奥也有 要求快速查找性能和稳定性。我也愿意为特别好的c/c++实现创建Python接口,如果有好的Java实现,甚至可以创建Jython接口 如果没有这一点,关于能够处理~16E9位的位数组/位向量表示的任何建议?请查看模块 类位(对象): 定
类位(对象):
定义初始值(自身,大小):
self.bits=array.array('B',[0表示范围内的i((大小+7)//8)])
def设置(自身,位):
b=自身。位[位//8]
self.bits[bit//8]=b | 1>(位%8))&1
FWIW,所有//8
和%8
操作都可以替换为>3
和&0x07
。这可能会导致稍好的速度,但可能会有些模糊
此外,在大多数硬件上,将
'B'
和8
更改为'L'
和32
应该更快。[在某些硬件上更改为'H'
和16可能会更快,但这是值得怀疑的。]我最近也走上了这条道路;虽然听起来我的申请有点不同。我对大量字符串上的近似集合运算感兴趣
您确实注意到需要快速位向量。根据您希望在bloom过滤器中添加的内容,您可能还需要考虑使用的哈希算法的速度。你可能会发现这很有用。您可能还想修补下面使用的随机数技术,该技术只对密钥进行一次散列
就非Java位数组实现而言:
- 助推
- Java具有内置的
from BitVector import BitVector
from random import Random
# get hashes from http://www.partow.net/programming/hashfunctions/index.html
from hashes import RSHash, JSHash, PJWHash, ELFHash, DJBHash
#
# ryan.a.cox@gmail.com / www.asciiarmor.com
#
# copyright (c) 2008, ryan cox
# all rights reserved
# BSD license: http://www.opensource.org/licenses/bsd-license.php
#
class BloomFilter(object):
def __init__(self, n=None, m=None, k=None, p=None, bits=None ):
self.m = m
if k > 4 or k < 1:
raise Exception('Must specify value of k between 1 and 4')
self.k = k
if bits:
self.bits = bits
else:
self.bits = BitVector( size=m )
self.rand = Random()
self.hashes = []
self.hashes.append(RSHash)
self.hashes.append(JSHash)
self.hashes.append(PJWHash)
self.hashes.append(DJBHash)
# switch between hashing techniques
self._indexes = self._rand_indexes
#self._indexes = self._hash_indexes
def __contains__(self, key):
for i in self._indexes(key):
if not self.bits[i]:
return False
return True
def add(self, key):
dupe = True
bits = []
for i in self._indexes(key):
if dupe and not self.bits[i]:
dupe = False
self.bits[i] = 1
bits.append(i)
return dupe
def __and__(self, filter):
if (self.k != filter.k) or (self.m != filter.m):
raise Exception('Must use bloom filters created with equal k / m paramters for bitwise AND')
return BloomFilter(m=self.m,k=self.k,bits=(self.bits & filter.bits))
def __or__(self, filter):
if (self.k != filter.k) or (self.m != filter.m):
raise Exception('Must use bloom filters created with equal k / m paramters for bitwise OR')
return BloomFilter(m=self.m,k=self.k,bits=(self.bits | filter.bits))
def _hash_indexes(self,key):
ret = []
for i in range(self.k):
ret.append(self.hashes[i](key) % self.m)
return ret
def _rand_indexes(self,key):
self.rand.seed(hash(key))
ret = []
for i in range(self.k):
ret.append(self.rand.randint(0,self.m-1))
return ret
if __name__ == '__main__':
e = BloomFilter(m=100, k=4)
e.add('one')
e.add('two')
e.add('three')
e.add('four')
e.add('five')
f = BloomFilter(m=100, k=4)
f.add('three')
f.add('four')
f.add('five')
f.add('six')
f.add('seven')
f.add('eight')
f.add('nine')
f.add("ten")
# test check for dupe on add
assert not f.add('eleven')
assert f.add('eleven')
# test membership operations
assert 'ten' in f
assert 'one' in e
assert 'ten' not in e
assert 'one' not in f
# test set based operations
union = f | e
intersection = f & e
assert 'ten' in union
assert 'one' in union
assert 'three' in intersection
assert 'ten' not in intersection
assert 'one' not in intersection
最终我找到了。我没用过,但看起来很合身 作为对巴拉德的回应,他说“通常的做法似乎是使用类似SHA1的东西并将比特分割成多个散列”,虽然这在某种意义上可能是正确的,因为这是一种普遍做法(PyBloom也使用它),但这并不意味着这是正确的做法;-) 对于Bloom过滤器,哈希函数的唯一要求是,在给定预期输入的情况下,其输出空间必须均匀分布。虽然加密散列确实满足了这一要求,但它也有点像用火箭筒射击苍蝇 相反,请尝试使用每个输入字节仅使用一个XOR和一个乘法的方法,我估计这比SHA1快几百倍:) FNV散列不是加密安全的,但您不需要它。它有轻微的错误,但您也没有使用它进行完整性检查 关于一致性,请注意,第二个链接只对32位FNV哈希进行了卡方检验。最好使用更多的位和FNV-1变体,它交换XOR和MUL步骤以获得更好的位分散性。对于Bloom过滤器,还有一些捕获,例如将输出统一映射到位数组的索引范围。如果可能的话,我会说将位数组的大小四舍五入到最接近的2次方,并相应地调整k。这样可以获得更好的精度,并且可以使用简单的异或折叠来映射范围
此外,这里还有一个参考资料,解释了为什么在需要时不需要SHA1(或任何加密哈希)。我在
它采用纯python,具有良好的哈希函数、良好的自动测试、后端选择(磁盘、数组、mmap等)以及对
\uuuuu init\uuuuuu
方法更直观的参数,因此您可以指定理想的元素数和所需的最大错误率,而不是有点空灵,特定于数据结构的可调参数。从最近的答案出现到现在已经将近十年了。时代确实在变化
看起来2019年末最流行的维护bloom filter软件包现在是这个:,在PyPi上作为pybloom_live提供:出于兴趣,您能解释一下现有实现(尤其是pybloom)的错误吗?它可能是“长在牙齿里”,但如果它能工作并且不需要修复,那听起来像是一个加号。奇怪的想法,更新了一些解释。谢谢Ryan,非常有用。关于位向量的性能,您是否找到了更快的替代方案?另外,我注意到您只使用了4个哈希,这似乎有点低。有什么想法吗?一种常见的做法似乎是使用类似SHA1的东西并将位分割成多个哈希。哈希计数取决于:#元素和可接受的误报率。我有一个以上的改进版本,我将签入。还没有找到更快的方法(尽管我认为这是一个本机实现)。您能添加一个docstring吗?这些值是用来做什么的?让我知道这个库是否对你有用!这是我能找到的最快的。测试了pybloom_live、Pybrobles和pybloof。也比布谷鸟快。顺便说一句,pyprobables速度非常慢。这里的代码非常粗糙:+1,回答很好。是的,对新用户发布链接的限制是相当愚蠢的。谢谢,伙计,我知道保留这个两年前的问题是有好处的!谢谢你
from BitVector import BitVector
from random import Random
# get hashes from http://www.partow.net/programming/hashfunctions/index.html
from hashes import RSHash, JSHash, PJWHash, ELFHash, DJBHash
#
# ryan.a.cox@gmail.com / www.asciiarmor.com
#
# copyright (c) 2008, ryan cox
# all rights reserved
# BSD license: http://www.opensource.org/licenses/bsd-license.php
#
class BloomFilter(object):
def __init__(self, n=None, m=None, k=None, p=None, bits=None ):
self.m = m
if k > 4 or k < 1:
raise Exception('Must specify value of k between 1 and 4')
self.k = k
if bits:
self.bits = bits
else:
self.bits = BitVector( size=m )
self.rand = Random()
self.hashes = []
self.hashes.append(RSHash)
self.hashes.append(JSHash)
self.hashes.append(PJWHash)
self.hashes.append(DJBHash)
# switch between hashing techniques
self._indexes = self._rand_indexes
#self._indexes = self._hash_indexes
def __contains__(self, key):
for i in self._indexes(key):
if not self.bits[i]:
return False
return True
def add(self, key):
dupe = True
bits = []
for i in self._indexes(key):
if dupe and not self.bits[i]:
dupe = False
self.bits[i] = 1
bits.append(i)
return dupe
def __and__(self, filter):
if (self.k != filter.k) or (self.m != filter.m):
raise Exception('Must use bloom filters created with equal k / m paramters for bitwise AND')
return BloomFilter(m=self.m,k=self.k,bits=(self.bits & filter.bits))
def __or__(self, filter):
if (self.k != filter.k) or (self.m != filter.m):
raise Exception('Must use bloom filters created with equal k / m paramters for bitwise OR')
return BloomFilter(m=self.m,k=self.k,bits=(self.bits | filter.bits))
def _hash_indexes(self,key):
ret = []
for i in range(self.k):
ret.append(self.hashes[i](key) % self.m)
return ret
def _rand_indexes(self,key):
self.rand.seed(hash(key))
ret = []
for i in range(self.k):
ret.append(self.rand.randint(0,self.m-1))
return ret
if __name__ == '__main__':
e = BloomFilter(m=100, k=4)
e.add('one')
e.add('two')
e.add('three')
e.add('four')
e.add('five')
f = BloomFilter(m=100, k=4)
f.add('three')
f.add('four')
f.add('five')
f.add('six')
f.add('seven')
f.add('eight')
f.add('nine')
f.add("ten")
# test check for dupe on add
assert not f.add('eleven')
assert f.add('eleven')
# test membership operations
assert 'ten' in f
assert 'one' in e
assert 'ten' not in e
assert 'one' not in f
# test set based operations
union = f | e
intersection = f & e
assert 'ten' in union
assert 'one' in union
assert 'three' in intersection
assert 'ten' not in intersection
assert 'one' not in intersection
def fast_count_bits( self, v ):
bits = (
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 )
return bits[v & 0xff] + bits[(v >> 8) & 0xff] + bits[(v >> 16) & 0xff] + bits[v >> 24]