给定权重字典总和为1 - 如何使用随机选择加权密钥

时间:2012-06-03 08:14:45

标签: python

我有一本看起来像这样的字典。

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

由于

5 个答案:

答案 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 digitizeaccumulate

这是一种很好的方法
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不是一个合适的结构,因为你不能重复权重,但我们将把它作为一个起点而不管。怎么做:

  1. 首先使用accumulate创建垃圾箱(使用垃圾箱可以使用重复的重量)。
  2. 然后我们使用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),