【问题标题】:Python: Selecting numbers with associated probabilities [duplicate]Python:选择具有相关概率的数字[重复]
【发布时间】:2011-05-15 16:13:42
【问题描述】:

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

【问题讨论】:

  • 你想生成随机数吗?计算期望值?
  • 嗨,我不明白这个问题...你想对这些数字做什么?
  • 再次,请澄清您的具体要求,我很乐意提供帮助。
  • 我想根据概率从1-6中选择这个列表中的数字。

标签: python random statistics probability


【解决方案1】:

这里有一些似乎可以工作并满足您的所有规范(主观上看起来相当快)。请注意,您对第二个数字与第一个数字不同的约束会抛出选择它的概率。下面的代码实际上忽略了这个问题,它只是强制执行限制(换句话说,第二个数字的概率不会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。如果不是,您将不得不相应地对它们进行缩放,以便它们这样做。

    首先使用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。

    【讨论】:

      【解决方案3】:

      也许问题只是与数据结构有关。如果您有字典而不是列表列表会更容易:

      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】:

        这可能是您正在寻找的。扩展到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
        

        【讨论】:

          猜你喜欢
          • 2014-05-03
          • 2011-09-19
          • 2018-11-03
          • 1970-01-01
          • 2012-01-20
          • 1970-01-01
          • 2011-05-06
          • 2012-10-06
          相关资源
          最近更新 更多