Каков самый питонический способ вытащить случайный элемент из списка?
скажем у меня есть список x
С unkown длиной, из которой я хочу случайно поп один элемент, так что список не содержит элемент впоследствии. Что такое наиболее подходящие для Python способ сделать это?
Я могу сделать это с помощью довольно громоздких combincation из pop
, random.randint
и len
и хотел бы видеть более короткие или более приятные решения:
import random
x = [1,2,3,4,5,6]
x.pop(random.randint(0,len(x)-1))
Edit: то, что я пытаюсь достичь, - это последовательно поп-случайные элементы из списка. (т. е., случайно поп один элемент и переместить его в словарь, случайно поп другой элемент и переместить его в другой словарь, ...)
обратите внимание, что я использую Python 2.6 и не нашел никаких решений с помощью функции поиска.
8 ответов
что вам казалось не очень подходящие для Python, в первую очередь. Вы не должны удалять вещи из середины списка, потому что списки реализованы как массивы во всех реализациях Python, о которых я знаю, так что это O(n)
операции.
Если вам действительно нужна эта функциональность как часть алгоритма, вы должны проверить структуру данных как blist
что обеспечивает эффективное удаление из середины.
в чистом Python, что вы можно сделать, если вам не нужен доступ к оставшимся элементам, просто перетасуйте список сначала, а затем повторите его:
lst = [1,2,3]
random.shuffle(lst)
for x in lst:
# ...
если вы очень нужно остаток (который немного запах кода, ИМХО), по крайней мере, вы можете pop()
из конца списка (это быстро!):
while lst:
x = lst.pop()
# do something with the element
В общем, вы можете часто выражать свои программы более элегантно, если используете более функциональный стиль, а не мутирующее состояние (как в списке).
вы не получите намного лучше, чем это, но вот небольшое улучшение:
x.pop(random.randrange(len(x)))
документация random.randrange()
:
случайные.randrange ([start], stop [, step])
Возврат случайно выбранного элемента изrange(start, stop, step)
. Это эквивалентноchoice(range(start, stop, step))
, но на самом деле не создает объект диапазона.
вот еще одна альтернатива: почему бы вам не перетасовать список первый, а затем начать появляться элементы его, пока не останется больше элементов? вот так:
import random
x = [1,2,3,4,5,6]
random.shuffle(x)
while x:
p = x.pop()
# do your stuff with p
удалить один элемент в случайный индекс из списка, если порядок остальных элементов списка не имеет значения:
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) при удалении из середины списка.
не выскакивая из списка, я столкнулся с этим вопросом в 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)
Это может быть немного неэффективно, поскольку вы перетасовываете весь список, но используете только небольшую его часть, но я не эксперт по оптимизации, поэтому я могу ошибаться.
этот ответ приходит учтивость @niklas-b:
"вы, вероятно, хотите использовать что-то вроде pypi.python.org/pypi/blist "
цитировать страница PYPI:
...список-подобный тип с лучшей асимптотической производительностью и аналогичный производительность по малым спискам
blist является заменой выпадающего списка Python, который предоставляет повышение производительности при изменении большой список. Пакет blist также предоставляет sortedlist, sortedset, weaksortedlist, weaksortedset, типы sorteddict и btuple.
можно было бы предположить пониженную производительность на случайном доступе / случайном конце запуска, поскольку это структура данных "копировать на запись". Это нарушает многие предположения варианта использования в списках Python,так что используйте его с осторожностью.
однако, если ваш основной вариант использования-сделать что-то странное и неестественно со списком (как в принудительном примере, приведенном @OP, или мой Python 2.6 FIFO queue-with-pass-over issue), то это будет соответствовать счету красиво.
Я знаю, что это старый вопрос, но только ради документации:
Если вы (человек, который гуглит тот же вопрос) делаете то, что я думаю, что вы делаете, который выбирает K количество элементов случайным образом из списка (где kслучайные.образец как @j-f-Себастьян предлагает. Но, не зная больше о деле, я не знаю, если это это то, что тебе нужно.