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 fromrange(start, stop, step)
. This is equivalent tochoice(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 fromrange(start, stop, step)
. This is equivalent tochoice(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建议。但是如果不了解更多的用例,我不知道这是否是您需要的。