Python加权随机

Python加权随机,python,algorithm,round-robin,Python,Algorithm,Round Robin,我需要根据加权循环返回不同的值,这样20分之一的人得到a,20分之一的人得到B,其余的人得到C 因此: 以下是一个基本版本,它似乎可以工作: import random x = random.randint(1, 100) if x <= 5: return 'A' elif x > 5 and x <= 10: return 'B' else: return 'C' 随机导入 x=random.randint(1100) 如果x5和x就可以了。更

我需要根据加权循环返回不同的值,这样20分之一的人得到a,20分之一的人得到B,其余的人得到C

因此:

以下是一个基本版本,它似乎可以工作:

import random

x = random.randint(1, 100)

if x <= 5:
    return 'A'
elif x > 5 and x <= 10:
    return 'B'
else:
    return 'C'
随机导入
x=random.randint(1100)

如果x5和x就可以了。更一般地说,您可以定义如下内容:

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)

这与您预期的18:1:1非常接近。

这似乎是正确的,因为您使用的是具有独立绘图的
均匀
随机变量,每个数字的概率将为
1/n
(n=100)

您可以通过运行算法(比如1000次)来轻松验证算法,并查看每个字母的频率

你可以考虑的另一个算法是生成一个数组,其中每个字母都有你想要的频率,只生成一个随机数,这是数组

中的索引。 它的内存效率会降低,但性能会更好

编辑:

为了回应@Joel Cornett的评论,一个例子将非常类似于@jurgenreza,但内存效率更高

import random
data_list = ['A'] + ['B'] + ['C'] * 18
random.choice(data_list )

你的算法是正确的,不如做些更优雅的事情:

import random
my_list = ['A'] * 5 + ['B'] * 5 + ['C'] * 90
random.choice(my_list)

如果要使用加权随机而非百分位随机,可以创建自己的随机化器类:

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)

您可以使用
random.randint(1,20)
来处理您的案例。@Akavall-怎么会这样?(1,20)只会返回仅当A或B落入5%范围时让我评估,但不是两者都是-对吗?您的随机整数可以取值1到20,如果随机整数为1,您返回A(5%概率),如果随机整数为2,您返回B(5%概率),如果随机整数为其他任何值,您返回C(90%概率)。我遗漏了什么吗?六分之一,六分之一。我想你的逻辑现在起作用了。如果你想在更一般的情况下研究这个问题,你所指的概念是“使用反向cdf方法生成随机变量”。为什么不干脆
my_list=['A']*5+['B']*5+['C']*90
?+1,但顺便说一句,不需要生成100个列表项,只要物品的数量保持比例。在这个例子中,你可以做
['A',B']+['C']*18
@JoelCornett考虑过这一点,但我认为这样更容易阅读。感谢您的更正。在这种有限的情况下,这可能无关紧要,但这种方法在时间和空间上总体上都是低效的。更好的解决方案是在[0,1]中指定与权重比例相对应的范围,例如,A为[0,5/100],B为[5/100,10/100]),以及[10/100,1)对于C,在处理诸如重复小数之类的事情时,使用适当的近似值/舍入,然后使用random.random或random.uniform生成随机数。从Python 3.6开始,您可以使用。如果您关心速度,接下来的两个答案会更快。您假设输入权重是升序的。排序如果你想安全,首先输入权重。这是不必要的-这样的算法会减少每个条目的计数器,因此结果是相同的(统计上)再加上路易斯·马苏埃利(Luis Masuelli)所说的,对于任何给定的概率,对应的值的范围是:数组中所有先前元素与该数字之和加上概率值。例如,[05、.05、.5、.4]大约对应于.00-.05 |.051-.10 |.101-.6 |.601-1的范围。在我看来,这比公认的答案要好。我明白Python代码应该努力提高可读性,但只是像疯了一样扔掉内存。特别是如果你在权重不断变化的模拟中使用它。解释器将被构建整理了这么多的名单
import random
my_list = ['A'] * 5 + ['B'] * 5 + ['C'] * 90
random.choice(my_list)
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)