2010-05-14 49 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)