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

Hen*_*rik 69 python random list

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

我可以用一个相当不方便combincation做到这一点pop,random.randint以及len和希望看到更短的或更好的解决方案:

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

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


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

Nik*_* B. 77

你似乎要做的事情首先看起来并不像Pythonic.你不应该从列表中间删除东西,因为列表在我所知道的所有Python实现中都被实现为数组,所以这是一个O(n)操作.

如果您真的需要此功能作为算法的一部分,您应该检查一个blist支持从中间有效删除的数据结构.

在纯Python中,如果您不需要访问其余元素,您可以执行的操作只是先将列表重新排序,然后迭代它:

lst = [1,2,3]
random.shuffle(lst)
for x in lst:
  # ...
Run Code Online (Sandbox Code Playgroud)

如果你真的需要余数(这有点代码味道,恕我直言),至少你现在可以pop()从列表的末尾(这很快!):

while lst:
  x = lst.pop()
  # do something with the element      
Run Code Online (Sandbox Code Playgroud)

通常,如果使用更具功能性的样式,而不是改变状态(就像使用列表一样),通常可以更优雅地表达程序.

  • @NiklasB.OP使用随机作为一个例子(坦率地说,它应该被取消,它使问题蒙上阴影)."不要那样做"是不够的.一个更好的答案是建议一个支持这种操作的Python数据结构,同时提供足够的访问速度(显然不如arra ... er ... list).在python 2中,我找不到一个.如果我这样做,我会回答.请注意,由于浏览器不幸,我无法将其添加到我的原始评论中,我应该添加一个辅助评论.谢谢你让我诚实:) (4认同)
  • 所以更好(更快)的想法是使用`random.shuffle(x)`然后``x.pop()`?我不明白怎么做这个"功能性"? (3认同)
  • 当我投票时,我应该添加一个帖子.有时你需要从列表中间删除一个项目......我现在必须这样做.别无选择:我有一个有序列表,我必须删除中间的项目.它很糟糕,但唯一的另一个选择是为一个半罕见的操作做重型代码重构.问题是[]的实现之一,它应该对这些操作有效,但事实并非如此. (2认同)

And*_*ark 44

你不会比这更好,但这是一个小小的改进:

x.pop(random.randrange(len(x)))
Run Code Online (Sandbox Code Playgroud)

文档random.randrange():

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


Ósc*_*pez 8

这里的另一种选择:你为什么不改组列表的第一,然后启动弹出它的元素,直到没有更多的元素保持?像这样:

import random

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

while x:
    p = x.pop()
    # do your stuff with p
Run Code Online (Sandbox Code Playgroud)

  • @NiklasB.因为我们要从列表中删除元素.如果不是绝对有必要删除元素,是的我同意你的观点:`[对于p in x]` (3认同)

jfs*_*jfs 8

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

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)
Run Code Online (Sandbox Code Playgroud)

交换用于避免从列表中间删除时的O(n)行为.


Dol*_*nga 5

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

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


nik*_*ami 5

尽管许多答案都建议使用random.shuffle(x)并且x.pop()在大数据上非常慢。以及启用 shuffle 时10000元素列表所需的时间6 seconds。当 shuffle 被禁用时速度是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)
Run Code Online (Sandbox Code Playgroud)

为了支持我的主张,这里是来自这个来源的时间复杂度图表 在此处输入图片说明


如果列表中没有重复项,

您也可以使用集合来实现您的目的。一旦列表被设置为重复项将被删除。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.
Run Code Online (Sandbox Code Playgroud)

listswhich supportA+B选项不同,sets还支持A-B (A minus B)withA+B (A union B)A.intersection(B,C,D)。当您想对数据执行逻辑操作时非常有用。


可选的

如果您希望在列表的头部和尾部执行操作时提高速度,请使用 python dequeue(双端队列)来支持我的声明,这里是图像。图像是千字。

在此处输入图片说明