Python:选择具有相关概率的数字

时间:2010-11-25 11:57:22

标签: python random statistics probability

  

可能重复:
  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”,则无法再次选择。

4 个答案:

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