Каков самый питонический способ вытащить случайный элемент из списка?


скажем у меня есть список 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 53

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) при удалении из середины списка.

один из способов сделать это:

x.remove(random.choice(x))

не выскакивая из списка, я столкнулся с этим вопросом в 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-Себастьян предлагает. Но, не зная больше о деле, я не знаю, если это это то, что тебе нужно.