确定元素是否已出现在流中的有效方法

时间:2018-07-18 11:51:59

标签: python stream

问题

是否有任何有效的方法(特别注意内存),确定性概率任意错误,以确定 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

当前实现的问题是它需要在主存储器中维护所有不同的项。还有其他办法吗?

1 个答案:

答案 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)