以(伪)随机顺序从大列表中有效地产生元素

时间:2018-03-09 06:54:55

标签: python python-3.x random generator

我正在尝试展开一些嵌套循环,以(可能)更好的性能而牺牲内存。在我的场景中,我最终得到一个大约300M元素(元组)的列表,我必须以(或多或少)随机顺序产生。

按照这个数量级,random.shuffle(some_list)真的不再适合了。

以下示例说明了该问题。请注意,在x86_64 Linux和CPython 3.6.4上,它将占用大约11 GB的内存。

def get_random_element():
    some_long_list = list(range(0, 300000000))
    for random_item in some_long_list:
        yield random_item

到目前为止,我的想法是简单地为每次迭代生成一个随机索引,并从列表中无限地生成随机选取的元素(无限期)。它可能会多次产生某些元素并完全跳过其他元素,这将是一个值得考虑的权衡。

我在内存和CPU时间的合理范围内还有哪些其他选项可能只产生一次列表中的每个元素?

1 个答案:

答案 0 :(得分:3)

这是Fisher-Yates-Knuth就地采样(https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle

内存稳定~4Gb(是的,我使用的是100000000)

# Fisher-Yates-Knuth sampling, in-place Durstenfeld version

import numpy as np

def swap(data, posA, posB):
    if posA != posB:
        data[posB], data[posA] = data[posA], data[posB]

def get_random_element(data, datalen):
    pos = datalen

    while pos > 0:
        idx = np.random.randint(low=0, high=pos) # sample in the [0...pos) range

        pos -= 1
        swap(data, idx, pos)

        yield data[pos]


length = 100000000
some_long_list = list(range(0, length))

gen = get_random_element(some_long_list, length)

for k in range(0, length):
    print(next(gen))

更新

对于速度,您可能还想内联swap()