是否有任何有效的方法(特别注意内存),确定性或概率任意错误,以确定 i -元素流中是否已经出现了元素(例如下面的模型)?
该流由以下字符串生成器建模。在此模型中,最后的M=10000
值是出现两次的值。显然,在实际应用中,未知何时会显示副本。
def binary(n):
"""Return a binary string representing the number."""
return "{0:b}".format(n)
def stream():
"""Lazily yield the ith element of the stream."""
N=10000000
M=10000
for i in range(N):
yield binary(i)
for i in range(M):
yield binary(i)
我当前的实现使用字典的形式,直接使用dict
或使用patricia-trie
的实现,该实现以这种方式定制,以利用最终的频繁子字符串。
elements = {}
already_seen = 0
for e in stream():
if e not in elements:
elements[e] = None
else:
already_seen += 1
already_seen
的值应为10000
。
当前实现的问题是它需要在主存储器中维护所有不同的项。还有其他办法吗?
答案 0 :(得分:1)
是的,它叫做Bloom Filters:
Bloom过滤器是节省空间的 概率性数据结构,用于 测试元素是否为集合的成员。
我使用pybloom-mirror
软件包来修改您的代码,并将Bloom过滤器设置为容量20000和错误率0.01(根据BloomFilter calculator过滤器的大小约为23.4kB)。但这会给您带来误报。
from pybloom import BloomFilter
def binary(n):
"""Return a binary string representing the number."""
return "{0:b}".format(n)
def stream():
"""Lazily yield the ith element of the stream."""
N=10000
M=100
for i in range(N):
yield binary(i)
for i in range(M):
yield binary(i)
def my_func():
bf = BloomFilter(capacity=20000, error_rate=0.01)
already_seen = 0
for e in stream():
if e in bf:
already_seen += 1
else:
bf.add(e)
print(already_seen)
my_func()
输出为:
100
对于容量为1000万且概率为0.01的大小,其大小约为11.43MiB(尽管有很多变量要玩,但这取决于您的用例)。
编辑:
对于链接Bloom过滤器,您可以使用以下示例:
def bloom_filter_chain(n=3):
# make bloom filters chain, each next chain is with lower error rate.
bloom_filters = []
for i in range(n):
bloom_filters.append(BloomFilter(capacity=20000, error_rate=0.1 ** (i+1)))
already_seen = 0
for e in stream():
we_have_seen = True
for bf in bloom_filters:
if e in bf: # might be false positive, continue down the chain
continue
else:
we_have_seen = False
bf.add(e)
break
if we_have_seen:
already_seen += 1
print(already_seen)