Python选择数字与相关概率

2024-05-16 00:25:59 发布

您现在位置:Python中文网/ 问答频道 /正文

Possible Duplicates:
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”,则不能再次选择。


Tags: 列表with数字random概率givengeneratelist
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]

我假设所有的概率加起来都是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.9999999)与之相关,这将是非常缓慢的。在这种情况下,您可以从列表中删除第一个数字,然后重新调整概率,以便在选择第二个数字之前,它们的总和为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

相关问题 更多 >