Как использовать random.shuffle() для генератора? питон
Как использовать 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()
был спроектирован так, что он возвращает тот же результат после каждого запуска script?
Ответы
Ответ 1
Чтобы равномерно перемешать последовательность, 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]
Ответ 2
Невозможно рандомизировать выход генератора без временного сохранения всех элементов. К счастью, это довольно легко в Python:
tmp = list(yielding(x))
random.shuffle(tmp)
for i in tmp:
print i
Обратите внимание на вызов list()
, который будет читать все элементы и помещать их в список.
Если вы не хотите или не можете хранить все элементы, вам нужно будет изменить генератор для получения в произвольном порядке.
Ответ 3
В зависимости от случая, если вы знаете, сколько данных у вас есть заранее, вы можете индексировать данные и вычислять/считывать их на основе перетасованного индекса. Это означает: "не используйте генератор для этой проблемы", и без конкретных вариантов использования сложно найти общий метод.
В качестве альтернативы... Если вам нужно использовать генератор...
это зависит от того, "как тасуется" вы хотите данные. Конечно, как отмечали люди, генераторы не имеют длины, поэтому вам нужно в какой-то момент оценить генератор, который может быть дорогим. Если вам не нужна идеальная случайность, вы можете ввести буфер случайного воспроизведения:
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
, поэтому вы можете избежать проблем с памятью, если это ваш ограничивающий фактор. Конечно, это не случайный случайный случай, поэтому его не следует использовать для сортировки, но если вам просто нужно добавить некоторую случайность в ваши данные, это может быть хорошим решением.
Ответ 4
Мне нужно было найти решение этой проблемы, чтобы я мог дорого вычислять элементы в произвольном порядке, не тратя вычисления на генерацию значений. Это то, что я придумал для вашего примера. Это включает создание другой функции для индексации первого массива.
Вам понадобится 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)
Ответ 5
Вы можете выбирать из произвольно полученных результатов, генерируя не полностью случайный, но несколько перемешанный набор в диапазоне. Аналогичен приведенному выше коду @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)