1

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

mychoice = {0.7: 2, 0.2: 1, 0.1:3}

我将使用以下内容来选择要使用的值。在上面,值 2 将在 70% 的时间被选择,值 1 将在 20% 的时间和 3、10% 的时间被选择。

使用以下方法生成 0 到 1 之间的随机数并随机选择要使用的值的最佳方法是什么?

from random import random
ran = random()
if ran>.10 and <.30 then select value 1 with a key of .20

谢谢

4

5 回答 5

4

以您的示例为例,进行一些修改(交换字典中的键/值):

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

这可以优化,但你明白了。

于 2012-06-03T08:36:40.457 回答
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))

然后生成一个随机值,并以与之前相同的方式得到结果。

于 2012-06-03T08:48:16.690 回答
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()]
于 2012-06-03T08:28:31.383 回答
0

这是使用numpys digitizeand的好方法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指出的那样,adict不是一个合适的结构,因为你不能有重复的权重,但无论如何我们都会以此为起点。怎么做:

  1. 首先使用创建箱accumulate(使用箱允许您使用重复权重)。
  2. 然后我们使用digitize查看随机数落入哪些箱,并将此索引用于mychoice.values()(尽管mychoice是一个字典,只要没有插入或删除,键和值保持它们的顺序..)。
于 2012-06-03T09:20:06.693 回答
0

d = {2: 0.7, 1: 0.2, 3: 0.1}哪个更合乎逻辑(不同的选择及其各自的权重,可以重复),你可以使用这个函数random_weighter,它也接受不总和的权重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),

打印(例如):

3 1 2 2 2 2 2 2 3 2
于 2012-06-03T09:49:32.893 回答