Python加权随机
我需要根据加权循环返回不同的值,这样20分之一的人得到a,20分之一的人得到B,其余的人得到C 因此: 以下是一个基本版本,它似乎可以工作: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就可以了。更
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)