生成多个随机数以等于python中的值

时间:2010-08-28 02:37:01

标签: python random

所以这是交易:我想(例如)生成4个伪随机数,当加在一起时等于40.这怎么可能在python中圆顶?我可以生成一个随机数1-40,然后在1和剩余部分之间生成另一​​个数字等,但是第一个数字将更有可能“抓住”更多。

8 个答案:

答案 0 :(得分:78)

这是标准解决方案。这与Laurence Gonsalves的答案相似,但与答案相比有两个优势。

  1. 它是统一的:每个组合的4个正整数加起来达到40,同样有可能提出这个方案。
    1. 很容易适应其他总数(7个数字加起来等等)
    2. import random
      
      def constrained_sum_sample_pos(n, total):
          """Return a randomly chosen list of n positive integers summing to total.
          Each such list is equally likely to occur."""
      
          dividers = sorted(random.sample(range(1, total), n - 1))
          return [a - b for a, b in zip(dividers + [total], [0] + dividers)]
      

      示例输出:

      >>> constrained_sum_sample_pos(4, 40)
      [4, 4, 25, 7]
      >>> constrained_sum_sample_pos(4, 40)
      [9, 6, 5, 20]
      >>> constrained_sum_sample_pos(4, 40)
      [11, 2, 15, 12]
      >>> constrained_sum_sample_pos(4, 40)
      [24, 8, 3, 5]
      

      说明:(1)正整数的4元组(a, b, c, d)之间存在一对一的对应关系,a + b + c + d == 40和(2)整数三倍(e, f, g)与{ {1}},使用0 < e < f < g < 40生成后者很容易。对应关系由random.sample在一个方向上给出,(e, f, g) = (a, a + b, a + b + c)在相反方向上给出。

      如果你想要非负整数(即允许(a, b, c, d) = (e, f - e, g - f, 40 - g))而不是正整数,那么就有一个简单的转换:如果0是非负整数,则总和为{{1然后(a, b, c, d)是求积分为40的正整数,反之亦然。使用这个想法,我们有:

      (a+1, b+1, c+1, d+1)

      44的图解说明,感谢@FM。 (稍微编辑。)

      def constrained_sum_sample_nonneg(n, total):
          """Return a randomly chosen list of n nonnegative integers summing to total.
          Each such list is equally likely to occur."""
      
          return [x - 1 for x in constrained_sum_sample_pos(n, total + n)]
      

答案 1 :(得分:11)

b = random.randint(2, 38)
a = random.randint(1, b - 1)
c = random.randint(b + 1, 39)
return [a, b - a, c - b, 40 - c]

(我假设你想要整数,因为你说“1-40”,但这可以很容易地为浮点数推广。)

以下是它的工作原理:

  • 随机减少两个总范围,即b。奇数范围是因为中点以下至少有2个,而且至少有2个以上。 (这来自每个值的最小值1。)
  • 随机地将每个范围切成两个。同样,界限是最小的1。
  • 返回每个切片的大小。他们最多可以增加40个。

答案 2 :(得分:8)

生成4个随机数,计算它们的总和,将每个数除以总和并乘以40。

如果你想要整数,那么这需要一点非随机性。

答案 3 :(得分:4)

from numpy.random import multinomial
multinomial(40, [1/4.] * 4)

答案 4 :(得分:3)

在[1,37]范围内只有37 ^ 4 = 1,874,161个四个整数的排列(允许重复)。枚举它们,保存并计算最多可增加40的排列。 (这将是一个小得多的数字,N)。

在区间[0,N-1]中绘制均匀分布的随机整数K并返回第K个置换。可以很容易地看出这可以保证在可能结果的空间上的均匀分布,每个序列位置相同地分布。 (我看到的许多答案都有最终选择偏低于前三个!)

答案 5 :(得分:1)

假设您希望它们均匀分布,并假设您不想重复

addends = []
picks = range(1, 34)
while sum(addends) != 40:
    addends = random.sample(picks, 3)
    if sum(addends) > 39:
        continue
    addends.append(40 - sum(addends))

答案 6 :(得分:0)

通过对除数之间的分布进行控制,以@markdickonson为基础。我介绍了方差/摆动作为它们之间均匀距离的百分比。

 def constrained_sum_sample(n, total, variance=50):
    """Return a random-ish list of n positive integers summing to total.

    variance: int; percentage of the gap between the uniform spacing to vary the result.
    """
    divisor = total/n
    jiggle = divisor * variance / 100 / 2
    dividers = [int((x+1)*divisor + random.random()*jiggle) for x in range(n-1)]
    result = [a - b for a, b in zip(dividers + [total], [0] + dividers)]
    return result

样本输出:

[12, 8, 10, 10]
[10, 11, 10, 9]
[11, 9, 11, 9]
[11, 9, 12, 8]

想法仍然是将人口平均分配,然后在给定范围内将其随机向左或向右移动。由于每个值仍然绑定到统一点,因此我们不必担心它会漂移。

对于我的目的来说足够好,但是还不完美。例如:第一个数字将始终较高,而最后一个数字将始终较低。

答案 7 :(得分:0)

如果您想要真正的随机性,请使用:

import numpy as np
def randofsum_unbalanced(s, n):
    # Where s = sum (e.g. 40 in your case) and n is the output array length (e.g. 4 in your case)
    r = np.random.rand(n)
    a = np.array(np.round((r/np.sum(r))*s,0),dtype=int)
    while np.sum(a) > s:
        a[np.random.choice(n)] -= 1
    while np.sum(a) < s:
        a[np.random.choice(n)] += 1
    return a

如果您想要更高的均匀度,请利用多项式分布:

def randofsum_balanced(s, n):
    return np.random.multinomial(s,np.ones(n)/n,size=1)[0]