【问题标题】:How to generate a list of random numbers so their sum would be equal to a randomly chosen number如何生成随机数列表,使其总和等于随机选择的数字
【发布时间】:2015-10-26 23:24:30
【问题描述】:

我想生成一个随机分布的数字列表,以便它们的总和等于随机选择的数字。例如,如果随机选择的数字是 5,则分布将是 [1 2 2] 或 [2 3] 或 [1 1 1 2] 等等。 欢迎任何建议!

【问题讨论】:

标签: python list random distribution


【解决方案1】:

在一个循环中,您可以在 1 和剩余总和之间不断抽取一个随机数,直到达到总数为止

from random import randint
def generate_values(n):
    values = []
    while n > 0:
        value = randint(1, n)
        values.append(value)
        n -= value
    return values

此类函数的一些示例

>>> generate_values(20)
[17, 1, 1, 1]
>>> generate_values(20)
[10, 4, 4, 1, 1]
>>> generate_values(20)
[14, 4, 1, 1]
>>> generate_values(20)
[5, 2, 4, 1, 5, 1, 1, 1]
>>> generate_values(20)
[2, 13, 5]
>>> generate_values(20)
[14, 3, 2, 1]

【讨论】:

  • 由于每次采样都是在不同的均匀分布上进行的,所以样本的总分布不再均匀了吧?
  • 正确,这将偏向于较小的数字,因为它们将包含在更多的分布中。较大的数字(最接近原始值)将在前几个数字之后不久被排除。这只是我想到的。
【解决方案2】:

n 成为您希望值相加的数字。生成随机大小的随机sample(小于n),由1 到n 范围内的值组成,不包括n。现在添加端点 0 和 n,然后排序。排序值的连续差值将相加为n

import random as r

def random_sum_to(n):
    a = r.sample(range(1, n), r.randint(1, n-1)) + [0, n]
    list.sort(a)
    return [a[i+1] - a[i] for i in range(len(a) - 1)]

print(random_sum_to(20))  # yields, e.g., [4, 1, 1, 2, 4, 2, 2, 4]

如果您希望能够明确指定总和中的项数,或者如果未指定则使其随机,请添加一个可选参数:

import random as r

def random_sum_to(n, num_terms = None):
    num_terms = (num_terms or r.randint(2, n)) - 1
    a = r.sample(range(1, n), num_terms) + [0, n]
    list.sort(a)
    return [a[i+1] - a[i] for i in range(len(a) - 1)]

print(random_sum_to(20, 3))   # [9, 7, 4] for example
print(random_sum_to(5))       # [1, 1, 2, 1] for example

【讨论】:

  • 这会产生比 CoryKramer 的答案更均匀的列表长度分布。
【解决方案3】:

考虑首先连续进行。暂时我们不关心最终数字,所以让我们在区间 [0...1] 中均匀采样 X_i,使它们的总和等于 1

X_1 + X_2 + ... X_n = 1

这是众所周知的分布,称为狄利克雷分布、伽马变量或单纯形抽样。在Generating N uniform random numbers that sum to M 上查看详细信息和讨论。可以使用random.gammavariate(a,1) 或正确处理参数 1 的角伽马变量是等效的指数分布,下面是直接采样代码

def simplex_sampling(n):
    r = []
    sum = 0.0
    for k in range(0,n):
        x = random.random()
        if x == 0.0:
            return (1.0, make_corner_sample(n, k))

        t = -math.log(x)
        r.append(t)
        sum += t

    return (sum, r)

def make_corner_sample(n, k):
    r = []
    for i in range(0, n):
       if i == k:
           r.append(1.0)
       else:
           r.append(0.0)

    return r

所以从simplex_sampling 你有向量和要用作标准化的总和。

因此,将其用于例如 N=5

N = 5

sum, r = simplex_sampling(N)

norm = float(N)/sum

# normalization together with matching back to integers
result = []
for k in range(N):
    # t is now float uniformly distributed in [0.0...N], with sum equal to N
    t = r[k] * norm 
    # not sure if you could have zeros,
    # and check for boundaries might be useful, but
    # conversion to integers is trivial anyway:
    # values in [0...1) shall be converted to 0,
    # values in [1...2) shall be converted to 1, etc
    result.append( int(t) )

【讨论】:

    【解决方案4】:

    这里有一个简单的方法,一个随机的概率列表,其中总和等于一;

    a = np.random.random(2)
    a /=sum(a)
    a
    

    【讨论】:

      猜你喜欢
      • 2020-10-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多