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

скажем у меня есть список 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) при удалении из середины списка.


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

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:

...список-подобный тип с лучшей асимптотической производительностью и аналогичный производительность по малым спискам

blist является заменой выпадающего списка Python, который предоставляет повышение производительности при изменении большой список. Пакет blist также предоставляет sortedlist, sortedset, weaksortedlist, weaksortedset, типы sorteddict и btuple.

можно было бы предположить пониженную производительность на случайном доступе / случайном конце запуска, поскольку это структура данных "копировать на запись". Это нарушает многие предположения варианта использования в списках Python,так что используйте его с осторожностью.

однако, если ваш основной вариант использования-сделать что-то странное и неестественно со списком (как в принудительном примере, приведенном @OP, или мой Python 2.6 FIFO queue-with-pass-over issue), то это будет соответствовать счету красиво.


Я знаю, что это старый вопрос, но только ради документации:

Если вы (человек, который гуглит тот же вопрос) делаете то, что я думаю, что вы делаете, который выбирает K количество элементов случайным образом из списка (где kслучайные.образец как @j-f-Себастьян предлагает. Но, не зная больше о деле, я не знаю, если это это то, что тебе нужно.