如何在生成器上使用random.shuffle()?蟒蛇

问题描述 投票:12回答:4

如何在生成器上使用random.shuffle()而不从生成器初始化列表?这甚至可能吗?如果没有,我怎么能在我的名单上使用random.shuffle()

>>> import random
>>> random.seed(2)
>>> x = [1,2,3,4,5,6,7,8,9]
>>> def yielding(ls):
...     for i in ls:
...             yield i
... 
>>> for i in random.shuffle(yielding(x)):
...     print i
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/random.py", line 287, in shuffle
    for i in reversed(xrange(1, len(x))):
TypeError: object of type 'generator' has no len()

注意:random.seed()的设计是为了在每个脚本运行后返回相同的输出?

python list random generator shuffle
4个回答
29
投票

为了统一调整序列,random.shuffle()需要知道输入有多长。发电机不能提供这个;你必须将它具体化为一个列表:

lst = list(yielding(x))
random.shuffle(lst)
for i in lst:
    print i

相反,你可以使用sorted()random.random()作为关键:

for i in sorted(yielding(x), key=lambda k: random.random()):
    print i

但由于这也产生了一个清单,所以走这条路是没有意义的。

演示:

>>> import random
>>> x = [1,2,3,4,5,6,7,8,9]
>>> sorted(iter(x), key=lambda k: random.random())
[9, 7, 3, 2, 5, 4, 6, 1, 8]

3
投票

如果没有暂时保存某个元素的所有元素,就无法随机化生成器的产量。幸运的是,这在Python中非常简单:

tmp = list(yielding(x))
random.shuffle(tmp)
for i in tmp:
    print i

请注意对list()的调用,它将读取所有项目并将它们放入列表中。

如果您不想或不能存储所有元素,则需要将生成器更改为以随机顺序生成。


1
投票

根据具体情况,如果您知道提前有多少数据,则可以根据混洗索引对数据进行索引并从中进行计算/读取。这相当于:'不要使用生成器来解决这个问题',如果没有特定的用例,很难想出一般的方法。

或者......如果你需要使用发电机......

这取决于你想要数据的“洗牌方式”。当然,正如人们所指出的那样,发电机没有长度,所以你需要在某些时候评估发电机,这可能很昂贵。如果你不需要完美的随机性,你可以引入一个shuffle缓冲区:

from itertools import islice

import numpy as np


def shuffle(generator, buffer_size):
    while True:
        buffer = list(islice(generator, buffer_size))
        if len(buffer) == 0:
            break
        np.random.shuffle(buffer)
        for item in buffer:
            yield item


shuffled_generator = shuffle(my_generator, 256)

这将以buffer_size块的形式随机播放数据,因此如果这是您的限制因素,您可以避免内存问题。当然,这不是真正随机的随机播放,因此不应该用于已排序的内容,但如果您只需要为数据添加一些随机性,这可能是一个很好的解决方案。


0
投票

我需要找到这个问题的解决方案,这样我就可以通过调整顺序计算元素,而不会通过生成值来浪费计算。这就是我为你的例子提出的。它涉及制作另一个函数来索引第一个数组。

你需要安装numpy

pip install numpy

编码:

import numpy as np
x = [1, 2, 3, 4, 5, 6, 7, 8, 9]

def shuffle_generator(lst):
    return (lst[idx] for idx in np.random.permutation(len(lst)))

def yielding(ls):
    for i in ls:
        yield i

# for i in random.shuffle(yielding(x)):
#    print i

for i in yielding(shuffle_generator(x)):
    print(i)

0
投票

您可以从任意产生的结果中进行采样,在一个范围内生成一个不完全随机但有些混乱的集合。类似于上面的@sturgemeister代码,但没有分块....没有定义的随机边界。

例如:

def scramble(gen, buffer_size):
    buf = []
    i = iter(gen)
    while True:
        try:
            e = next(i)
            buf.append(e)
            if len(buf) >= buffer_size:
                choice = random.randint(0, len(buf)-1)
                buf[-1],buf[choice] = buf[choice],buf[-1]
                yield buf.pop()
        except StopIteration:
            random.shuffle(buf)
            yield from buf
            return

结果应该在buffer_size窗口中完全随机:

for e in scramble(itertools.count(start=0, step=1), 1000):
    print(e)

对于此流中的任意1000个元素......它们是随机的。但从整体趋势(超过1000)来看,它显然在增加。

要测试,断言这会返回1000个唯一元素:

for e in scramble(range(1000), 100):
    print(e)
© www.soinside.com 2019 - 2024. All rights reserved.