以下方法满足您的要求。它使用拒绝采样方法:随机均匀地生成一个整数,并以与其权重成正比的概率接受它。如果该号码不被接受,我们拒绝它并重试(另请参阅this answer of mine)。
import random
def weighted_random(mn, mx, mnweight, mxweight):
while True:
# Get the highest weight.
highestweight=max(mnweight,mxweight)
# Generate a uniform random integer in the interval [mn, mx].
r=random.randint(mn,mx)
# Calculate the weight for this integer. This ensures the min's
# weight is mnweight and the max's weight is mxweight
weight=mnweight+(mxweight-mnweight)*((i-mn)/(mx-mn))
# Generate a random value between 0 and the highest weight
v=random.random()*highestweight
# Is it less than this weight?
if v<weight:
# Yes, so return it
return r
# No, so try again
(诚然,由于浮点除法以及输出浮点数的random.random(),实现并不完全“优雅”,但下面的示例是,一旦我们编写了它。实现可以通过在fractions 模块中使用Fractions 来改进。)
这个方法也可以使用Python中已有的random.choices方法实现如下。首先我们计算random.choices 所需的权重,然后我们传递这些权重。但是,如果最小值和最大值之间的范围非常大,这种方法的效率并不高。
import random
# Calculate weights for `random.choices`
def make_weights(mn, mx, mnweight, mxweight):
r=(mx-mn)
return [mnweight+(mxweight-mnweight)*(i/r) for i in range(mn, mx+1)]
def weighted_random(mn, mx, mnweight, mxweight):
weights=make_weights(mn, mx, mnweight, mxweight)
return random.choices(range(mn, mx+1), weights=weights)[0]
使用 NumPy 库,这甚至可以按如下方式实现:
import random
import numpy
def weighted_random(mn, mx, mnweight, mxweight):
return random.choices(range(mn, mx+1), \
weights=numpy.linspace(mnweight,mxweight,(mx-mn)+1))[0]
weighted_random 函数的示例如下:
# Generate 100 random integers in the interval [0, 10],
# where 0 is assigned the weight 30 and 10 is assigned the
# weight 10 and numbers in between are assigned
# decreasing weights.
print([weighted_random(0,10,30,10) for i in range(100)])