从列表中弹出随机元素的最python化的方式是什么?

时间:2022-05-29 22:30:04

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. What is the most pythonic way to do this?

假设我有一个不知道长度的列表x,我想从中随机取出一个元素,这样列表之后就不会包含这个元素。最符合python的方法是什么?

I can do it using a rather unhandy combincation of pop, random.randint, and len and would like to see shorter or nicer solutions:

我可以用一种不方便的随机组合。randint,和len,并希望看到更短或更好的解决方案:

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

Edit: What I am trying to achieve is consecutively pop random elements from a list. (i.e., 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,没有通过搜索函数找到任何解决方案。

8 个解决方案

#1


57  

What you seem to be up to doesn't look very Pythonic in the first place. 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实现中,列表都是作为数组实现的,所以这是一个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!):

如果您真的需要剩下的(这是一种代码味道,IMHO),那么至少您可以从列表的末尾弹出(这是快速的!)

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).

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

#2


30  

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])
Return a randomly selected element from range(start, stop, step). This is equivalent to choice(range(start, stop, step)), but doesn’t actually build a range object.

随机的。randrange([start]、stop[、step])从range(start、stop、step)返回随机选择的元素。这相当于选择(range(start、stop、step)),但实际上并不构建range对象。

#3


8  

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

#4


7  

To remove a single element at random index from a list if the order of the rest of list elements doesn't matter:

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

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

#5


3  

One way to do it is:

一种方法是:

x.remove(random.choice(x))

#6


2  

While not popping from the list, I encountered this question on Google while trying to get X random items from a list without duplicates. Here's what I eventually used:

虽然我没有从列表中跳出来,但我在谷歌上遇到了这个问题,当时我试图从一个没有重复的列表中获得X个随机项。下面是我最后使用的:

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.

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

#7


1  

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. The blist package also provides sortedlist, sortedset, weaksortedlist, weaksortedset, sorteddict, and btuple types.

blist是Python列表的一种替代,它在修改大列表时提供了更好的性能。blist包还提供了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. 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- pass- pass问题),那么这将很好地符合这个要求。

#8


0  

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 @j-f-sebastian suggests. But without knowing more about the use case, I don't know if this is what you need.

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

#1


57  

What you seem to be up to doesn't look very Pythonic in the first place. 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实现中,列表都是作为数组实现的,所以这是一个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!):

如果您真的需要剩下的(这是一种代码味道,IMHO),那么至少您可以从列表的末尾弹出(这是快速的!)

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).

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

#2


30  

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])
Return a randomly selected element from range(start, stop, step). This is equivalent to choice(range(start, stop, step)), but doesn’t actually build a range object.

随机的。randrange([start]、stop[、step])从range(start、stop、step)返回随机选择的元素。这相当于选择(range(start、stop、step)),但实际上并不构建range对象。

#3


8  

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

#4


7  

To remove a single element at random index from a list if the order of the rest of list elements doesn't matter:

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

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

#5


3  

One way to do it is:

一种方法是:

x.remove(random.choice(x))

#6


2  

While not popping from the list, I encountered this question on Google while trying to get X random items from a list without duplicates. Here's what I eventually used:

虽然我没有从列表中跳出来,但我在谷歌上遇到了这个问题,当时我试图从一个没有重复的列表中获得X个随机项。下面是我最后使用的:

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.

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

#7


1  

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. The blist package also provides sortedlist, sortedset, weaksortedlist, weaksortedset, sorteddict, and btuple types.

blist是Python列表的一种替代,它在修改大列表时提供了更好的性能。blist包还提供了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. 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- pass- pass问题),那么这将很好地符合这个要求。

#8


0  

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 @j-f-sebastian suggests. But without knowing more about the use case, I don't know if this is what you need.

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