Какой самый пифонический способ вывести случайный элемент из списка?
Скажем, у меня есть список x
с неизвестной длиной, из которого я хочу произвольно вытолкнуть один элемент, чтобы впоследствии список не содержал этот элемент. Какой самый питонный способ сделать это?
Я могу сделать это, используя довольно неудобное сочетание pop
, random.randint
и len
, и хотел бы увидеть более короткие или более хорошие решения:
import random
x = [1,2,3,4,5,6]
x.pop(random.randint(0,len(x)-1))
То, чего я пытаюсь добиться, - это последовательно извлекать случайные элементы из списка. (то есть случайным образом вытолкнуть один элемент и переместить его в словарь, случайным образом вытолкнуть другой элемент и переместить его в другой словарь,...)
Обратите внимание, что я использую Python 2.6 и не нашел каких-либо решений с помощью функции поиска.
Ответы
Ответ 1
То, что вы, похоже, не очень похоже на Pythonic. Вы не должны удалять материал из середины списка, потому что списки реализованы как массивы во всех реализациях Python, о которых я знаю, поэтому это операция O(n)
.
Если вам действительно нужна эта функциональность как часть алгоритма, вы должны проверить структуру данных, такую как blist
, которая поддерживает эффективное удаление от середины.
В чистом Python, что вы можете сделать, если вам не нужен доступ к остальным элементам, просто сначала перетасовать список, а затем перебрать его:
lst = [1,2,3]
random.shuffle(lst)
for x in lst:
# ...
Если вам действительно нужен остаток (который немного напоминает код, IMHO), по крайней мере, вы можете pop()
с конца списка (что быстро!):
while lst:
x = lst.pop()
# do something with the element
В общем, вы можете часто выражать свои программы более элегантно, если вы используете более функциональный стиль, а не изменяете состояние (как, например, со списком).
Ответ 2
Вы не будете намного лучше, но это небольшое улучшение:
x.pop(random.randrange(len(x)))
Документация на random.randrange()
:
random.randrange([start], stop [, step])
Верните случайно выбранный элемент из range(start, stop, step)
. Это эквивалентно choice(range(start, stop, step))
, но фактически не создает объект диапазона.
Ответ 3
Вот еще одна альтернатива: почему бы вам не перетасовать список первым, а затем начать выскакивать его элементы, пока не останется больше элементов? например:
import random
x = [1,2,3,4,5,6]
random.shuffle(x)
while x:
p = x.pop()
# do your stuff with p
Ответ 4
Чтобы удалить один элемент из случайного индекса из списка, если порядок остальных элементов списка не имеет значения:
import random
L = [1,2,3,4,5,6]
i = random.randrange(len(L)) # get random index
L[i], L[-1] = L[-1], L[i] # swap with the last element
x = L.pop() # pop last element O(1)
Подкачка используется для избежания поведения O (n) при удалении из середины списка.
Ответ 5
Один из способов сделать это:
x.remove(random.choice(x))
Ответ 6
Пока вы не вышли из списка, я столкнулся с этим вопросом в Google, пытаясь получить X случайных элементов из списка без дубликатов. Вот что я в конечном итоге использовал:
items = [1, 2, 3, 4, 5]
items_needed = 2
from random import shuffle
shuffle(items)
for item in items[:items_needed]:
print(item)
Это может быть немного неэффективно, поскольку вы перетасовываете весь список, но используете только небольшую часть, но я не эксперт по оптимизации, поэтому я могу ошибаться.
Ответ 7
Этот ответ представлен любезно @niklas-b:
"Возможно, вы захотите использовать что-то вроде pypi.python.org/pypi/blist"
Чтобы процитировать страницу PYPI:
... тип списка, имеющий лучшие асимптотические характеристики и аналогичные производительность в небольших списках
Blist - это замена для списка Python, который обеспечивает более высокая производительность при изменении больших списков. Пакет blist также предоставляет отсортированный список, сортировку, слабые списки, слабые стороны, sorteddict и btuple.
Можно предположить снижение производительности на случайном доступе/случайном запуске, поскольку это структура данных "копирование при записи". Это нарушает многие предположения для использования в списках Python, , поэтому используйте его с осторожностью.
ОДНАКО, если ваш основной вариант использования - сделать что-то странное и неестественное со списком (как в принудительном примере, заданном @OP, так и в случае проблемы с Python 2.6 FIFO queue-with-pass-over), тогда это будет соответствовать счет красиво.
Ответ 8
Я знаю, что это старый вопрос, но только для документации:
Если вы (человек, занимающийся поиском по одному и тому же вопросу), делаете то, что, как я думаю, вы делаете, выбираете случайное число k из списка (где k <= len (ваш список)), но убедитесь, что каждый элемент никогда не было выбрано более одного раза (= выборка без замены), вы можете использовать random.sample, как предлагает @jf-sebastian. Но, не зная больше о прецеденте, я не знаю, нужно ли это вам.