2010-05-14 51 views
1

假設我有一個元素列表,我想從列表中隨機選擇一個滿足謂詞的元素。這樣做的pythonic方式是什麼?選擇滿足某個謂詞的隨機值的pythonic方法

我現在做一個理解然後是random.choice()但這是不必要的低效:

intlist = [1,2,3,4,5,6,7,8,9] 
evenlist = [ i for i in intlist if i % 2 == 0 ] 
randomeven = random.choice(evenlist) 

謝謝!

+0

即使_random_選擇器必須知道可以選擇什麼。額外的代碼行會傷害你嗎? – mykhal 2010-05-14 16:26:27

+0

我想說這取決於你的謂詞排除了多少物品。如果它小於50%,那麼首先隨機選擇一個項目,然後測試謂詞可能更有效。另一方面,如果只有少數項目符合謂詞,事先過濾它們可能會更好。 – 2010-05-14 16:28:03

+0

@Felix_Kling即使您排除列表中少於50%的項目,也無法知道需要多長時間才能運行。 – 2010-05-14 16:59:17

回答

2

你寫它上面的方法其實是很好的習慣蟒蛇。如果我們分析算法,我們會發現它基本上是這樣做的:

  1. 製作一個滿足謂詞的元素列表。 (隨n線性增長)
  2. 從該列表中選擇一個隨機元素。 (Constant time)

唯一的解決方法是隨機選擇一個元素,決定它是否滿足謂詞,如果不滿足則選擇它。這個算法稍微複雜一點。在列表的90%滿足謂詞的情況下,這將比你的解決方案運行得更快。在只有10%的列表滿足謂詞的情況下,它實際上運行速度要慢得多,因爲很有可能它會隨機選擇一個給定的元素並檢查謂詞是否在該元素上不止一次滿足。現在你可以考慮記憶你的謂詞,但你仍然會選擇大量的隨機數據。歸根結底,除非你的解決方案特別不適合你的數據,否則堅持下去,因爲它很棒。就個人而言,我把它改寫這樣的:

intlist = range(1,10) 
randomeven = random.choice([i for i in intlist if i % 2 == 0]) 

這是一個小更簡潔,但它會運行完全一樣的現有代碼。

+0

+1有時候你想拒收樣品,有時候你*有*拒絕樣品,就像你無法列舉所有可能的選擇一樣,但是在這種情況下你不能使用'random.choice' ... – 2010-05-14 17:22:43

1

我找不到一個函數類的文檔中random.selectspecific(list, predicate),所以我會嘗試像下面的內容:

import random 
def selectspecific(l, predicate): 
    result = random.choice(l) 
    while (not predicate(result)): 
     result = random.choice(l) 
    return result 
+0

不錯。它可以被修改爲符合DRY原則,而真/假循環 – mykhal 2010-05-14 16:46:16

+3

沒有辦法知道這個解決方案將會是多麼昂貴,它可能需要「永遠」從列表中獲取有效元素。首先過濾似乎是最好的解決方案。 – 2010-05-14 16:56:23

+0

如果列表中沒有元素從'predicate()'返回True,那麼這將無限循環。 – tdedecko 2010-05-15 01:04:06

1
import random 

intlist = [1,2,3,4,5,6,7,8,9] 
randomeven = random.choice(filter(lambda x: x % 2 == 0, intlist))                                       
+0

當,你打我10秒... – computergeek6 2010-05-14 16:27:47

+0

我發佈了與itertools.ifilter相同的答案。但它不起作用,因爲random.choice需要知道數據的實際長度。當然! – joaquin 2010-05-14 16:33:55

+2

這只是他的問題中代碼的稍微可讀的版本。帶有lambda的過濾器實際上與列表理解相同。 – Benson 2010-05-14 16:53:45

0

pythonic是怎麼回事?

from itertools import ifilterfalse 
from random import choice 
print choice([ i for i in ifilterfalse(lambda x: x%2, range(10)) ]) 
0

如果你想封裝更多,你可以創建一個方法來處理選擇,這將接受一個謂詞方法。然後,您可以使用此謂詞方法filter()

import random 
def selectSpecific(intlist, predicate): 
    filteredList = filter(predicate, intlist) 
    result = random.choice(filteredList) 
    return result 

可以傳遞一個謂詞selectSpecific()作爲lambda或任何其它方法。例如:

intlist = range(1,10) 
selectSpecific(intlist, lambda x: x % 2 == 0) 

def makeEven(n): 
    if n % 2 == 0: 
    return n 

selectSpecific(intlist, makeEven)