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

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