Скажем, у меня есть список 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 и не нашел никаких решений через функцию поиска.
Ответы:
То, что вы, кажется, задумали, в первую очередь не выглядит очень питоническим. Вы не должны удалять что-либо из середины списка, потому что списки реализованы как массивы во всех реализациях 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
В общем, вы часто можете выразить свои программы более элегантно, если вы используете более функциональный стиль, а не изменяете состояние (как вы делаете со списком).
источник
random.shuffle(x)
а потомx.pop()
? Не понимаю, как сделать этот "функционал"?zip
получить список пар (dict, number). Вы сказали что-то о нескольких словарях, каждый из которых хотите связать со случайным числом.zip
идеально подходит для этогоВы не получите ничего лучше, но вот небольшое улучшение:
Документация по
random.randrange()
:источник
Чтобы удалить один элемент по случайному индексу из списка, если порядок остальных элементов списка не имеет значения:
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) при удалении из середины списка.
источник
Вот еще одна альтернатива: почему бы вам сначала не перетасовать список , а затем начать выталкивать его элементы, пока не останется больше элементов? как это:
import random x = [1,2,3,4,5,6] random.shuffle(x) while x: p = x.pop() # do your stuff with p
источник
[for p in x]
Один из способов сделать это:
источник
pop
вы можете указать имя на удаленный элемент, с этим вы не можете.remove
требуется линейное сканирование списка. Это ужасно неэффективно по сравнению с поиском индекса.Не появляясь из списка, я столкнулся с этим вопросом в 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)
Это может быть немного неэффективно, поскольку вы перетасовываете весь список, но используете только небольшую его часть, но я не эксперт по оптимизации, поэтому могу ошибаться.
источник
random.sample(items, items_needed)
Я знаю, что это старый вопрос, но только для документации:
Если вы (человек, который задает тот же вопрос в Google) делаете то, что, я думаю, делаете вы, то есть выбираете k элементов случайным образом из списка (где k <= len (yourlist)), но следите за тем, чтобы каждый элемент никогда не выбирался больше чем один раз (= выборка без замены), вы можете использовать random.sample, например, @ jf-sebastian. Но, не зная больше о варианте использования, я не знаю, нужно ли это вам.
источник
Этот ответ любезно предоставлен @ niklas-b :
« Возможно, вы захотите использовать что-то вроде pypi.python.org/pypi/blist »
Чтобы процитировать страницу PYPI :
Можно было бы предположить пониженную производительность в конце произвольного доступа / произвольного прогона , поскольку это структура данных «копирование при записи». Это нарушает многие предположения о вариантах использования в списках Python, поэтому используйте его с осторожностью .
ОДНАКО, если ваш основной вариант использования - сделать что-то странное и неестественное со списком (как в принудительном примере, предоставленном @OP, или моей проблеме с передачей очереди FIFO в Python 2.6), тогда это будет хорошо соответствовать требованиям .
источник
несмотря на то, что многие ответы предполагают использование
random.shuffle(x)
иx.pop()
очень медленное использование больших данных. и время, требуемое для списка10000
элементов,6 seconds
когда включено перемешивание. когда перемешивание отключено, скорость была0.2s
самый быстрый метод после тестирования всех приведенных выше методов оказался написан @jfs
import random L = ['1',2,3,'4'...1000] #you can take mixed or pure list 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)
в подтверждение моего утверждения вот диаграмма временной сложности из этого источника
ЕСЛИ в списке нет дубликатов,
Вы также можете достичь своей цели, используя наборы. после того, как список объединен в набор, дубликаты будут удалены.
remove by value
иremove random
стоитьO(1)
, т.е. очень эффективно. это самый чистый метод, который я мог придумать.L=set([1,2,3,4,5,6...]) #directly input the list to inbuilt function set() while 1: r=L.pop() #do something with r , r is random element of initial list L.
В отличие от того,
lists
какойA+B
вариант поддержки ,sets
также поддерживаетA-B (A minus B)
вместе сA+B (A union B)
иA.intersection(B,C,D)
. очень полезно, когда вы хотите выполнять логические операции с данными.ПО ЖЕЛАНИЮ
ЕСЛИ вам нужна скорость при операциях, выполняемых в начале и конце списка, используйте python dequeue (двусторонняя очередь) в поддержку моего утверждения, вот изображение. изображение - это тысяча слов.
источник