简体   繁体   English

从列表中弹出随机元素的最pythonic方法是什么?

[英]What is the most pythonic way to pop a random element from a list?

Say I have a list x with unkown length from which I want to randomly pop one element so that the list does not contain the element afterwards.假设我有一个长度未知的列表x ,我想从中随机弹出一个元素,以便该列表之后不包含该元素。 What is the most pythonic way to do this?最pythonic的方法是什么?

I can do it using a rather unhandy combincation of pop , random.randint , and len , and would like to see shorter or nicer solutions:我可以使用poprandom.randintlen的相当笨拙的组合来做到这一点,并且希望看到更短或更好的解决方案:

import random
x = [1,2,3,4,5,6]
x.pop(random.randint(0,len(x)-1))

What I am trying to achieve is consecutively pop random elements from a list.我想要实现的是从列表中连续弹出随机元素。 (ie, randomly pop one element and move it to a dictionary, randomly pop another element and move it to another dictionary, ...) (即,随机弹出一个元素并将其移动到字典,随机弹出另一个元素并将其移动到另一个字典,...)

Note that I am using Python 2.6 and did not find any solutions via the search function.请注意,我使用的是 Python 2.6,通过搜索 function 没有找到任何解决方案。

What you seem to be up to doesn't look very Pythonic in the first place.首先,您似乎在做什么看起来不太像 Pythonic。 You shouldn't remove stuff from the middle of a list, because lists are implemented as arrays in all Python implementations I know of, so this is an O(n) operation.您不应该从列表中间删除内容,因为在我所知道的所有 Python 实现中,列表都被实现为 arrays,所以这是一个O(n)操作。

If you really need this functionality as part of an algorithm, you should check out a data structure like the blist that supports efficient deletion from the middle.如果你真的需要这个功能作为算法的一部分,你应该检查像blist这样支持从中间高效删除的数据结构。

In pure Python, what you can do if you don't need access to the remaining elements is just shuffle the list first and then iterate over it:在纯 Python 中,如果您不需要访问剩余元素,您可以做的只是先打乱列表,然后遍历它:

lst = [1,2,3]
random.shuffle(lst)
for x in lst:
  # ...

If you really need the remainder (which is a bit of a code smell, IMHO), at least you can pop() from the end of the list now (which is fast:):如果你真的需要剩下的部分(这有点代码味道,恕我直言),至少你现在可以从列表的末尾pop() (这很快:):

while lst:
  x = lst.pop()
  # do something with the element      

In general, you can often express your programs more elegantly if you use a more functional style, instead of mutating state (like you do with the list).一般来说,如果您使用更函数式的风格,而不是改变 state(就像您对列表所做的那样),您通常可以更优雅地表达您的程序。

You won't get much better than that, but here is a slight improvement:你不会比这更好,但这里有一点改进:

x.pop(random.randrange(len(x)))

Documentation on random.randrange() :关于random.randrange()的文档:

random.randrange([start], stop[, step]) random.randrange([开始], 停止[, 步骤])
Return a randomly selected element from range(start, stop, step) .返回从range(start, stop, step)中随机选择的元素。 This is equivalent to choice(range(start, stop, step)) , but doesn't actually build a range object.这等效于choice(range(start, stop, step)) ,但实际上并未构建范围 object。

To remove a single element at random index from a list if the order of the rest of list elements doesn't matter:如果列表元素 rest 的顺序无关紧要,则从列表中删除随机索引处的单个元素:

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)

The swap is used to avoid O(n) behavior on deletion from a middle of a list.交换用于避免从列表中间删除时的 O(n) 行为。

despite many answers suggesting use random.shuffle(x) and x.pop() its very slow on large data.尽管许多答案建议使用random.shuffle(x)x.pop()它在大数据上非常慢。 and time required on a list of 10000 elements took about 6 seconds when shuffle is enabled.启用随机播放后, 10000个元素的列表所需的时间大约需要6 seconds when shuffle is disabled speed was 0.2s禁用随机播放时速度为0.2s

the fastest method after testing all the given methods above was turned out to be written by @jfs测试上面所有给定方法后最快的方法原来是@jfs写的

import random

L = [1,"2",[3],(4),{5:"6"},'etc'] #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)

in support of my claim here is the time complexity chart from this source为了支持我的主张,这是来自该来源的时间复杂度图表在此处输入图像描述


IF there are no duplicates in list,如果列表中没有重复项,

you can achieve your purpose using sets too.您也可以使用集合来实现您的目的。 once list made into set duplicates will be removed.一旦将列表制成集合重复项将被删除。 remove by value and remove random cost O(1) , ie very effecient. remove by value删除并remove random成本O(1) ,即非常有效。 this is the cleanest method i could come up with.这是我能想到的最干净的方法。

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.

Unlike lists which support A+B option, sets also support AB (A minus B) along with A+B (A union B) and A.intersection(B,C,D) .与支持A+B选项的lists不同, sets还支持AB (A minus B)以及A+B (A union B)A.intersection(B,C,D) super useful when you want to perform logical operations on the data.当你想对数据执行逻辑操作时超级有用。


OPTIONAL选修的

IF you want speed when operations performed on head and tail of list, use python dequeue (double ended queue) in support of my claim here is the image.如果你想在列表的头部和尾部执行操作时提高速度,请使用 python dequeue(双端队列)来支持我的声明,这里是图片。 an image is thousand words.图像是千字。

在此处输入图像描述

Here's another alternative: why don't you shuffle the list first , and then start popping elements of it until no more elements remain?这是另一种选择:为什么不打乱列表,然后开始弹出其中的元素,直到没有更多元素为止? like this:像这样:

import random

x = [1,2,3,4,5,6]
random.shuffle(x)

while x:
    p = x.pop()
    # do your stuff with p

I know this is an old question, but just for documentation's sake:我知道这是一个老问题,但只是为了记录:

If you (the person googling the same question) are doing what I think you are doing, which is selecting k number of items randomly from a list (where k<=len(yourlist)), but making sure each item is never selected more than one time (=sampling without replacement), you could use random.sample like @jf-sebastian suggests.如果您(谷歌搜索相同问题的人)正在做我认为您正在做的事情,即从列表中随机选择 k 个项目(其中 k<=len(yourlist)),但要确保每个项目都不会被选中更多超过一次(=无替换采样),你可以像@jf-sebastian 建议的那样使用random.sample But without knowing more about the use case, I don't know if this is what you need.但是在不了解更多用例的情况下,我不知道这是否是您所需要的。

One way to do it is:一种方法是:

x.remove(random.choice(x))

While not popping from the list, I encountered this question on Google while trying to get X random items from a list without duplicates.虽然没有从列表中弹出,但我在尝试从列表中获取 X 个随机项目而不重复时在 Google 上遇到了这个问题。 Here's what I eventually used:这是我最终使用的:

items = [1, 2, 3, 4, 5]
items_needed = 2
from random import shuffle
shuffle(items)
for item in items[:items_needed]:
    print(item)

This may be slightly inefficient as you're shuffling an entire list but only using a small portion of it, but I'm not an optimisation expert so I could be wrong.这可能会有点低效,因为您正在洗牌整个列表但只使用其中的一小部分,但我不是优化专家所以我可能是错的。

This answer comes courtesy of @niklas-b :这个答案来自@niklas-b的礼貌:

" You probably want to use something like pypi.python.org/pypi/blist " 你可能想使用类似pypi.python.org/pypi/blist的东西

To quote the PYPI page :引用PYPI 页面

...a list-like type with better asymptotic performance and similar performance on small lists ...一个类似列表的类型,具有更好的渐近性能和类似的小列表性能

The blist is a drop-in replacement for the Python list that provides better performance when modifying large lists. blist 是 Python 列表的直接替代品,可在修改大型列表时提供更好的性能。 The blist package also provides sortedlist, sortedset, weaksortedlist, weaksortedset, sorteddict, and btuple types. blist package 还提供了 sortedlist、sortedset、weaksortedlist、weaksortedset、sorteddict 和 btuple 类型。

One would assume lowered performance on the random access/random run end , as it is a "copy on write" data structure.人们会假设 random access/random run end 的性能降低,因为它是一种“写时复制”数据结构。 This violates many use case assumptions on Python lists, so use it with care .这违反了 Python 列表上的许多用例假设,因此请谨慎使用

HOWEVER, if your main use case is to do something weird and unnatural with a list (as in the forced example given by @OP, or my Python 2.6 FIFO queue-with-pass-over issue), then this will fit the bill nicely.但是,如果您的主要用例是用列表做一些奇怪和不自然的事情(如@OP 给出的强制示例,或我的 Python 2.6 FIFO queue-with-pass-over 问题),那么这将很好地满足要求.

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 从 Python3.6+ 中的集合中弹出随机元素的最 Pythonic 方法是什么? - What is the most Pythonic way to pop a random element from a set in Python3.6+? 在条件列表中随机选择元素的大多数Python方法 - Most pythonic way to select at random an element in a list with conditions 在列表中找到与其他元素不同的元素的最pythonic方法是什么? - what is most pythonic way to find a element in a list that is different with other elements? 从 OrderedDict 列表中查找项目的最pythonic 方法是什么? - what would be the most pythonic way of finding items from a list of OrderedDict? 从数字中筛选列表的最pythonic方法是什么? - What is the most pythonic way to filter list from number? 列表边界-最Python的方式是什么? - List boundaries - what is the most Pythonic way? 从中间对列表进行排序的最 Pythonic 方式? - Most Pythonic way to sort a list from middle? 遍历一长串字符串并从原始列表构建新列表的最pythonic 方法是什么? - What is the most pythonic way to iterate through a long list of strings and structure new lists from that original list? 从remote-ls列表中提取分支名称的最pythonic方法是什么? - What is the most pythonic way to extract branch names from a remote-ls list? 用序列中的列表值创建字典的最简洁(最Pythonic)方法是什么? - What's the cleanest (most Pythonic) way of creating a dictionary with list values from a sequence?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM