Каков самый питонический способ вытащить случайный элемент из списка?
скажем у меня есть список x
с неизвестной длиной, из которой я хочу случайно поп один элемент, так что список не содержит элемент впоследствии. Что такое наиболее подходящие для Python способ сделать это?
Я могу сделать это, используя довольно неудобное сочетание 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 page:
...список-подобный тип с лучшей асимптотической производительностью и тому подобное производительность на небольших списках
blist-это выпадающая замена для списка Python, который предоставляет лучшая производительность при изменении большой список. Блист пакет также предоставляет sortedlist, sortedset, weaksortedlist, weaksortedset, sorteddict и btuple типов.
можно было бы предположить пониженную производительность на случайном доступе / случайном конце запуска, так как это" копия при записи " структуры данных. Это нарушает многие предположения о прецедентах в списках Python,так что используйте его с осторожностью.
однако, если ваш основной вариант использования-сделать что-то странное и неестественно со списком (как в принудительном примере, приведенном @OP, или мой Python 2.6 FIFO queue-with-pass-over issue), то это будет соответствовать счету красиво.
Я знаю, что это старый вопрос, но только для документации:
Если вы (человек, который ищет тот же вопрос) делаете то, что я думаю, что вы делаете, который выбирает K количество элементов случайным образом из списка(где kслучайные.образец как @ j-f-Себастьян предлагает. Но, не зная больше о деле, я не знаю, если это это то, что тебе нужно.