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

最初に頭に浮かぶのは、それらを並べ替えて合計することです。

あなたが私のアドバイスに従って、辞書の構造を次のように変更したとしましょう。

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

累積された重みで dict を作成しましょう。

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 digitizeこれは、 andを使用して行う良い方法です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を繰り返すことができないため、これを行うのに適した構造ではありませんが、とにかくこれを出発点として使用します。どうやってするの:

  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 に答える