2012-04-06 59 views
49

假設我有一個列表x,其中包含未知長度,我想從中隨機彈出一個元素,以便列表之後不包含元素。什麼是最Python的方式來做到這一點?什麼是從列表中彈出隨機元素的最pythonic方法?

我可以使用poprandom.randintlen相當不方便combincation做到這一點,並希望看到更短的或更好的解決方案:

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

編輯:我所試圖實現的是連續彈出來自列表的隨機元素。 (即隨機彈出一個元素,並將其移動到字典中,隨機彈出另一個元素,並將其移動到另一個字典,...)


請注意,我使用Python 2.6,並通過沒有找到任何解決方案搜索功能。

+3

我並不是一個蟒蛇,但確實對我來說很不錯。 – 2012-04-06 19:12:38

回答

52

你彷彿是高達看起來不是很符合Python擺在首位。你不應該從列表中間刪除東西,因爲列表在我知道的所有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  

在一般情況下,你常常可以表達你的程序更優雅如果你使用更多功能風格,而不是變異狀態(就像你對列表做的那樣)。

+3

所以一個更好的(更快)的想法是使用'random.shuffle(x)'然後'x.pop()'?我不明白如何做到這一點「功能」? – Henrik 2012-04-06 19:21:30

+0

@亨利克:我不知道你在做什麼,所以我不知道。你應該在問題中添加更多的信息,或者只是在這裏評論你想達到的目標:)這似乎是[XY問題]的一個例子(http://meta.stackexchange.com/questions/66377/what-is -the-xy-problem)... – 2012-04-06 19:22:13

+0

我有我想要連續彈出隨機元素的元素列表。 – Henrik 2012-04-06 19:22:56

29

你不會得到比這更好,但這裏是一個略有改善:

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

文檔上random.randrange()

random.randrange([開始],停止[,步])
返回從range(start, stop, step)中隨機選擇的元素。這相當於choice(range(start, stop, step)),但實際上並不構建範圍對象。

3

一種方式來做到這一點是:

x.remove(random.choice(x)) 
+6

如果元素出現次數更多,這可能會出現問題。 – 2012-04-06 19:15:26

+2

當有重複時,這將刪除最左邊的元素,導致不完全隨機的結果。 – FogleBird 2012-04-06 19:15:54

+0

使用'pop'你可以在被刪除的元素上指定一個名字,這是你不能的。 – agf 2012-04-06 19:17:06

8

下面是另外一種選擇:爲什麼你不洗牌清單第一個,然後開始彈出它的元素,直到沒有更多元素保留?像這樣:

import random 

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

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

爲什麼不用'for x in x''? – 2012-04-06 19:32:52

+3

@NiklasB。因爲我們正在從列表中刪除元素。如果不是絕對需要刪除元素,是的,我同意你的意見:'[for p in x]' – 2012-04-06 19:34:51

+0

因爲它改變了列表,如果你只想選擇一半的元素而另一半以後,你將會擁有其餘的設置稍後。 – Henrik 2012-04-06 19:34:56

7

從列表中移除在隨機索引單個元件如果列表中的元素的其餘部分的次序並不重要:

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)行爲從列表中間刪除。

2

雖然沒有從列表中彈出,但我在Google上遇到了這個問題,同時嘗試從列表中獲取X個隨機項目而沒有重複項目。下面是我最終使用:

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

這可能是稍微低效的,因爲你洗牌整個名單,但只能用其中的一小部分,但我不是一個優化專家,所以我可能是錯的。

+1

'random.sample(items,items_needed)' – jfs 2013-11-22 23:55:31

1

這個答案來的@niklas-b禮貌:

你可能想使用類似pypi.python.org/pypi/blist

引述PYPI page

...名單樣型具有更好的漸進性能和類似 小列表上的性能

blist是Python列表的替代品,在修改大型列表時提供了更好的性能 。 blist軟件包還提供排序列表,排序集,弱排序列表,弱排序列表, 排序和二元類型。

人們會認爲降低了對隨機接入/隨機運行結束性能,因爲它是數據結構的「上寫副本」。這違反了Python列表中的許多用例假設,因此請謹慎使用。然而,如果你的主要用例是用一個列表來做一些奇怪和不自然的事情(如@OP給出的強制實例或者我的Python 2.6先入先出隊列傳遞問題),那麼這將會很好地適應賬單。

0

我知道這是一個老問題,但只是對文件的緣故:

如果(谷歌搜索同一個問題的人)都在做你正在做我認爲,這是從選擇項目的k個隨機一個列表(其中k < = len(yourlist)),但要確保每個項目從未被選擇超過一次(=沒有替換的採樣),您可以使用random.sample,如@ jf-sebastian所示。但是,如果不瞭解更多關於用例的信息,我不知道這是否是您需要的。

相關問題