20分の1がAを取得し、20分の1がBを取得し、残りがCになるように、加重ラウンドロビンに基づいて異なる値を返す必要があります。
そう:
A => 5%
B => 5%
C => 90%
動作するように見える基本バージョンは次のとおりです。
import random
x = random.randint(1, 100)
if x <= 5:
return 'A'
Elif x > 5 and x <= 10:
return 'B'
else:
return 'C'
このアルゴリズムは正しいですか?その場合、改善できますか?
あなたのアルゴリズムは正しいです、もっとエレガントなものはどうですか:
import random
my_list = ['A'] * 5 + ['B'] * 5 + ['C'] * 90
random.choice(my_list)
それはいいです。より一般的には、次のようなものを定義できます。
from collections import Counter
from random import randint
def weighted_random(pairs):
total = sum(pair[0] for pair in pairs)
r = randint(1, total)
for (weight, value) in pairs:
r -= weight
if r <= 0: return value
results = Counter(weighted_random([(1,'a'),(1,'b'),(18,'c')])
for _ in range(20000))
print(results)
与える
Counter({'c': 17954, 'b': 1039, 'a': 1007})
予想どおり18:1:1に近い値です。
パーセンタイルランダムではなく、重み付きランダムを使用する場合は、独自のランダマイザークラスを作成できます。
import random
class WeightedRandomizer:
def __init__ (self, weights):
self.__max = .0
self.__weights = []
for value, weight in weights.items ():
self.__max += weight
self.__weights.append ( (self.__max, value) )
def random (self):
r = random.random () * self.__max
for ceil, value in self.__weights:
if ceil > r: return value
w = {'A': 1.0, 'B': 1.0, 'C': 18.0}
#or w = {'A': 5, 'B': 5, 'C': 90}
#or w = {'A': 1.0/18, 'B': 1.0/18, 'C': 1.0}
#or or or
wr = WeightedRandomizer (w)
results = {'A': 0, 'B': 0, 'C': 0}
for i in range (10000):
results [wr.random () ] += 1
print ('After 10000 rounds the distribution is:')
print (results)
独立した描画でuniform
確率変数を使用しているため、各数字の確率は1/n
(n = 100)。
アルゴリズムを1000回実行すると、各文字の頻度を確認して、アルゴリズムを簡単に確認できます。
あなたが考慮するかもしれない別のアルゴリズムは、各文字に必要な頻度を与えられた文字で配列を生成し、配列内のインデックスである単一の乱数のみを生成することです
メモリの効率は低下しますが、パフォーマンスは向上するはずです
編集:
@Joel Cornettのコメントに応答するための例は、@ jurgenrezaに非常に似ていますが、よりメモリ効率が良くなります。
import random
data_list = ['A'] + ['B'] + ['C'] * 18
random.choice(data_list )