可能重复:
Random weighted choice
Generate random numbers with a given (numerical) distribution
我有一个列表清单,其中包含一系列数字和相关概率。
prob_list = [[1, 0.5], [2, 0.25], [3, 0.05], [4, 0.01], [5, 0.09], [6, 0.1]]
例如在prob_list[0]
中,数字1的概率为0.5,与之相关。所以你会期望1出现50%的时间。
当我选择数字时,如何为数字增加权重?
注意:列表中的数字量可以在6 - 100
之间变化修改
在列表中,我有6个数字及其相关概率。我想根据它们的概率选择两个数字。
无法选择两次号码。如果选择“2”,则无法再次选择。
答案 0 :(得分:4)
我将假设所有概率加起来为1.如果他们不这样做,那么你将不得不相应地对它们进行缩放,以便他们这样做。
首先使用random.random()
生成均匀随机变量[0,1]。然后通过列表,总结概率。总和第一次超过随机数时,返回相关的数字。这样,如果生成的均匀随机变量在您的示例中落在范围(0.5,0.75)内,则将返回2,从而为其返回所需的0.25概率。
import random
import sys
def pick_random(prob_list):
r, s = random.random(), 0
for num in prob_list:
s += num[1]
if s >= r:
return num[0]
print >> sys.stderr, "Error: shouldn't get here"
这是一个显示它有效的测试:
import collections
count = collections.defaultdict(int)
for i in xrange(10000):
count[pick_random(prob_list)] += 1
for n in count:
print n, count[n] / 10000.0
输出:
1 0.498
2 0.25
3 0.0515
4 0.0099
5 0.0899
6 0.1007
编辑:刚看到问题中的编辑。如果要选择两个不同的数字,可以重复上述操作,直到您选择的第二个数字不同为止。但如果一个数字具有与之相关的非常高(例如0.99999999)的概率,则这将非常慢。在这种情况下,您可以从列表中删除第一个数字并重新调整概率,使它们在选择第二个数字之前总和为1.
答案 1 :(得分:3)
这里的东西似乎有用,符合你的所有规格(而且主观看起来很快)。请注意,第二个数字与第一个数字不相同的约束会将概率抛出以便选择它。下面的代码实际上忽略了这个问题,它只是强制执行限制(换句话说,第二个数字的概率不会是prob_list
中每个数字的概率)。
import random
prob_list = [[1, 0.5], [2, 0.25], [3, 0.05], [4, 0.01], [5, 0.09], [6, 0.1]]
# create a list with the running total of the probabilities
acc = 0.0
acc_list = [acc]
for t in prob_list:
acc += t[1]
acc_list.append(acc)
TOLERANCE = .000001
def approx_eq(v1, v2):
return abs(v1-v2) <= TOLERANCE
def within(low, value, high):
""" Determine if low >= value <= high (approximately) """
return (value > low or approx_eq(low, value)) and \
(value < high or approx_eq(high, value))
def get_selection():
""" Find which weighted interval a random selection falls in """
interval = -1
rand = random.random()
for i in range(len(acc_list)-1):
if within(acc_list[i], rand, acc_list[i+1]):
interval = i
break
if interval == -1:
raise AssertionError('no interval for {:.6}'.format(rand))
return interval
def get_two_different_nums():
sel1 = get_selection()
sel2 = sel1
while sel2 == sel1:
sel2 = get_selection()
return prob_list[sel1][0], prob_list[sel2][0]
答案 2 :(得分:1)
这可能就是你要找的东西。扩展到Generate random numbers with a given (numerical) distribution中的解决方案。从分发中删除所选项目,更新概率并返回selected item, updated distribution
。没有证明有效,但应该给出好主意。
def random_distr(l):
assert l # don't accept empty lists
r = random.uniform(0, 1)
s = 0
for i in xrange(len(l)):
item, prob = l[i]
s += prob
if s >= r:
l.pop(i) # remove the item from the distribution
break
else: # Might occur because of floating point inaccuracies
l.pop()
# update probabilities based on new domain
d = 1 - prob
for i in xrange(len(l)):
l[i][1] /= d
return item, l
dist = [[1, 0.5], [2, 0.25], [3, 0.05], [4, 0.01], [5, 0.09], [6, 0.1]]
while dist:
val, dist = random_distr(dist)
print val
答案 3 :(得分:1)
问题可能只与数据结构有关。如果您有字典而不是列表列表会更容易:
prob_list = { 1:0.5, 2:0.25, 3:0.05, 4:0.01, 5:0.09, 6:0.1}
这样,您可以获得与数字相对应的权重:
import random
number = weight = -1
while not( number in prob_list ):
number = random.randint( 0, length( prob_list ) )
weight = prob_list[ number ]
print( number, weight )