109

假设我有一个x长度未知的列表,我想从中随机弹出一个元素,以便该列表之后不包含该元素。最pythonic的方法是什么?

我可以使用, 和,的相当不方便的组合来做到这一点pop,并希望看到更短或更好的解决方案:random.randintlen

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

我想要实现的是从列表中连续弹出随机元素。(即,随机弹出一个元素并将其移至字典,随机弹出另一个元素并将其移至另一字典,...)

请注意,我使用的是 Python 2.6,并且没有通过搜索功能找到任何解决方案。

4

9 回答 9

112

首先,您似乎在做的事情看起来并不像 Pythonic。你不应该从列表中间删除东西,因为在我知道的所有 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      

一般来说,如果你使用更实用的风格而不是改变状态(就像你对列表所做的那样),你通常可以更优雅地表达你的程序。

于 2012-04-06T19:17:22.770 回答
60

你不会比这更好,但这里有一点改进:

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

文档random.randrange()

random.randrange([start], stop[, step])从 .
返回一个随机选择的元素range(start, stop, step)。这等效于choice(range(start, stop, step)),但实际上并不构建范围对象。

于 2012-04-06T19:13:22.363 回答
17

如果其余列表元素的顺序无关紧要,则从列表中删除随机索引处的单个元素:

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) 行为。

于 2012-12-30T03:45:01.903 回答
9

这是另一种选择:为什么不对列表进行洗牌,然后开始弹出其中的元素,直到没有更多元素为止?像这样:

import random

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

while x:
    p = x.pop()
    # do your stuff with p
于 2012-04-06T19:30:20.063 回答
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 valueremove 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(双端队列)来支持我的声明,这里是图像。一张图片就是千言万语。

在此处输入图像描述

于 2020-11-12T00:12:54.113 回答
4

我知道这是一个老问题,但只是为了记录:

如果您(搜索相同问题的人)正在做我认为您正在做的事情,那就是从列表中随机选择 k 个项目(其中 k<=len(yourlist)),但要确保每个项目永远不会被选中不止一次(=无替换采样),您可以像@jf-sebastian 建议的那样使用random.sample 。但是在不了解用例的情况下,我不知道这是否是您所需要的。

于 2017-06-18T02:44:18.427 回答
3

一种方法是:

x.remove(random.choice(x))
于 2012-04-06T19:14:55.610 回答
2

虽然没有从列表中弹出,但我在尝试从没有重复的列表中获取 X 个随机项目时在 Google 上遇到了这个问题。这是我最终使用的:

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

这可能会稍微低效,因为您正在改组整个列表但只使用其中的一小部分,但我不是优化专家,所以我可能是错的。

于 2013-10-26T09:19:20.540 回答
1

这个答案来自@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 问题),那么这将非常适合该法案.

于 2016-12-06T23:17:35.763 回答