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

s-m*_*m-e 10 python random generator python-3.x

我正在尝试展开一些嵌套循环,以(可能)更好的性能而牺牲内存.在我的场景中,我最终得到一个大约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
Run Code Online (Sandbox Code Playgroud)

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

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

Sev*_*eux 4

这是 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))
Run Code Online (Sandbox Code Playgroud)

更新

为了速度,您可能还想内联 swap()