从列表中随机选择一个元素是否有更好的方法?

4
choices = [a,a,a,a,b,b,c]
random.choice(choices)

从结果可以看出a最有可能被选中,但是否有更好/更短的方法来完成这个任务?


你的意思是从 [a,b,c] 中选择,但不是均匀地选择吗? - chepner
3
这种方式有什么问题?你所说的“更好”是什么意思? - juanpa.arrivillaga
我想打印一个随机选择,但是得到a而不是b或c的概率更大。 - Glitchd
@juanpa.arrivillaga 如果选项很多,代码会变得很长。所谓“更好”,指的是实现同样功能所需的代码更少。 - Glitchd
2个回答

5
如果您使用的是choices而不是choice,则可以为每个元素指定权重。
random.choices([a,b,c], [4,2,1])

第二个参数是第一个参数中每个元素的相对权重。例如,在下面的例子中,您可以看到选择了a大约两倍于b,大约四倍于c
>>> import collections, random
>>> collections.Counter(random.choices('abc', [4,2,1], k=100))
Counter({'a': 58, 'b': 25, 'c': 17})

如果你定义了权重,那么这样做更随机吗? - def_init_
1
random.choice('abc') 有1/3的概率返回 abcrandom.choices('abc', [4,2,1]) 将在7次选择中选择 a 4 次,b 2 次,c 1 次。它仍然是随机的,但不是均匀分布。 - chepner
@darshvader 因为一个更有可能被选择。 - Glitchd
@chepner 啊啊啊,好的,非常好的解释。由于某种原因,我忘记了即使是随机的,它们仍然会均匀对齐。 - def_init_
@chepner 有和我最初的方法相同的功能吗? - Glitchd

0

如果您没有使用Python 3.6+(支持random.choices中权重),您可以构建一个种群:

import random
a, b, c = 'abc'

weighted = [(a, 4), (b, 2), (c, 1)]

population = [x for x, weight in weighted for _ in range(weight)]
random.choice(population)

如果您担心大权重值的性能问题,可以使用 itertools.accumulatebisect

import bisect
import itertools
import random

choices = ['a', 'b', 'c']
weights = [4, 2, 1]
cumulative_weights = list(itertools.accumulate(weights))

print(choices[bisect.bisect(cumulative_weights, random.random() * cumulative_weights[-1])])

这与原帖作者使用的完全相同。 - Austin
1
@Austin 的意思是避免为大量值键入 a,a,a,a,a,a,a,a,a,a,a... - iz_
1
@Tomothy32 没错,谢谢。我觉得那很明显。 - Glitchd
如果权重非常大,这将非常低效。 - ekhumoro
@ekhumoro,我添加了一种更通用的方法,尽管它会使事情变得非常复杂。 - iz_

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接