2010-11-25 56 views
5

可能重复:
Random weighted choice
Generate random numbers with a given (numerical) distribution的Python:用相关的概率选号

我有列表的列表,它包含了数字的一系列有关联的概率。

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

http://stackoverflow.com/questions/4265988/generate-random-numbers-with-a-given-numerical-distribution/ – khachik 2010-11-25 12:00:16

回答

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 
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) 
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 

编辑:只是看到了问题的编辑如果您想选择两个不同的号码,可以重复上述直到你本身。 cond号码选择是不同的。但是如果一个数字非常高(例如0),这将会非常慢。99999999)与之相关的概率。在这种情况下,您可以从列表中删除第一个数字并重新调整概率,以便在选择第二个数字之前将它们总和为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]