我有一本看起来像这样的字典。
mychoice = {0.7: 2, 0.2: 1, 0.1:3}
我将使用以下内容选择要使用的值。在上面,将在70%的时间选择值2,在20%的时间和3,10%的时间选择值1。
使用以下方法生成0到1之间的随机数并随机选择要使用的值的最佳方法是什么?
from random import random
ran = random()
if ran>.10 and <.30 then select value 1 with a key of .20
由于
答案 0 :(得分:4)
举个例子,做一些修改(在dict中交换键/值):
mychoice = {1: 0.2, 2: 0.7, 3:0.1}
current = 0
limits = {}
for key in mychoice:
limits[key] = (current,current + mychoice[key])
current = current + mychoice[key] #Next range should start at the end of current
#This should give a new dictionary: {1:(0,0.2),2:(0.2,0.9),3;(0.9,1)}
r = random.random() # float between 0 and 1
for key in limits:
range = limits[key]
if r >= range[0] and r < range[1]:
return key
return None
这可以优化,但你明白了。
答案 1 :(得分:2)
我想到的第一件事是:对它们进行排序并将它们加起来。
让我们假设你已经按照我的建议改变了你的dict结构:
mychoice = {2: 0.7, 1: 0.2, 3: 0.1}
让我们建立一个累积权重的字典:
temp = sorted(((v, w) for v, w in mychoice.items()), key = lambda x: x[1], reverse = True)
accum = [(val[0], sum(_[1] for _ in temp[:i+1])) for i, val in enumerate(temp)]
(这有点乱,有人可以优化吗?)
无论如何,现在accum
为[(2, 0.7), (1, 0.9), (3, 1)]
所以:
r = random.random()
for vw in accum:
if vw[1] > r:
print vw[0]
break
编辑:正如astynax巧妙地指出的那样,没有必要对权重进行排序,因为无论如何都会对累积概率列表进行排序。
所以我们只需要:
accum = ((k, sum(mychoice.values()[:i]))
for i, k in enumerate(mychoice.keys(), 1))
然后生成随机值并以与以前相同的方式获得结果。
答案 2 :(得分:1)
>>> d = {0.7: 2, 0.2: 1, 0.1:3}
>>> keys = [[k] * int(round(10*k)) for k in d.keys()]
>>> keys
[[0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7], [0.1], [0.2, 0.2]]
>>> import itertools
>>> keys = list(itertools.chain(*keys))
[0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.1, 0.2, 0.2]
>>> import random
>>> d[random.choice(keys)]
2
>>> d[random.choice(keys)]
2
>>> d[random.choice(keys)]
3
替代方案:将选择的概率表示为分辨率为1000:
中的1>>> keys = [[k] * int(round(1000*k)) for k in d.keys()]
答案 3 :(得分:0)
使用numpys digitize
和accumulate
:
from random import random
import numpy as np
mychoice = {0.7: 2, 0.2: 1, 0.1: 3}
bins = np.add.accumulate(mychoice.keys())
for i in xrange(100):
print mychoice.values()[np.digitize([random()], bins)[0]],
#Output:
1 2 3 2 2 2 2 2 2 1 1 3 3 2 2 2 2 2 2 2 1 3 2 2 3 2 1 2 1 2 2 2 2 2
1 2 2 2 2 3 3 2 1 1 2 2 1 1 3 2 2 2 2 2 1 2 2 2 1 1 1 2 2 2 2 2 2 2
3 2 1 2 2 2 3 1 1 1 2 2 2 2 2 2 3 2 1 2 2 2 2 1 1 2 1 2 2 2 2 1
由于@Karl Knechtel
指出dict
不是一个合适的结构,因为你不能重复权重,但我们将把它作为一个起点而不管。怎么做:
accumulate
创建垃圾箱(使用垃圾箱可以使用重复的重量)。digitize
查看随机数落入哪个区域,并将此索引用于mychoice.values()
(尽管mychoice
是一个字典,键和值会保留其顺序,前提是没有插入或删除..)。答案 4 :(得分:0)
d = {2: 0.7, 1: 0.2, 3: 0.1}
更符合逻辑(不同的选择及其各自的权重,可重复),您可以使用此random_weighter
函数,该函数也接受不与{{1}相加的权重}。
1.0
打印(例如):
import random
def random_weighted(d):
r = random.random() * sum(d.itervalues())
for k, v in d.iteritems():
r = r - v
if r <= 0.:
return k
d = {2: 0.7, 1: 0.2, 3: 0.1}
for i in xrange(10):
print random_weighted(d),