创建一个由x和y之间的n个数字组成的列表,这些数字加起来就是z



我正在尝试创建一个由25个数字组成的随机集合,这些数字在2到25之间,在python中加起来为100。

这个问题给出了一个答案,但似乎最大数字永远不会接近25。

我试过创建一个列表,对每个数字进行除法,然后重新创建列表,但它基本上会使我的最小值和最大值无效,因为它们几乎总是被一个大于1的数字除法:

numbers = np.random.randint(low = 2, high = 25, size = 100, dtype = int)
scale = 100 / sum(numbers) #We want weights to add up to 100%
#Scale values
for value in numbers:
nums.append(value * scale)

有办法做到这一点吗?感谢

您还没有指定数字应该具有的概率分布,因此这可能是一种简单有效的方法,尽管不太可能产生接近25:的数字

import numpy as np 
numbers = np.full(25,2)
while numbers.sum() < 100:
i = np.random.randint(25)
if numbers[i] < 25: # almost guaranteed...
numbers[i] += 1

假设您想要一个由25个数字(不一定是整数)组成的随机列表,这些数字加起来为100,并且每个数字至少为2且不超过25。

首先,请注意,我们可以将其转化为一个等价的问题,其中数字只需要是非负的,通过生成0到23之间的25个数字,这些数字加起来就是100-25*2,也就是50。一旦我们有了这个列表,我们只需要在每个数字上加两个;新列表将在2到25之间,其总和将为100(因为我们在25个数字中的每个数字上加了2)。

第二件需要注意的事情是,在该列表中找到接近25的数字的概率非常小,因为这需要一个数字吸引几乎一半的可用数字。(如果你看看替代配方,这个说法会更清楚,25个介于0到23之间的数字加起来就是50。如果其中一个数字是20,那么其他24个数字加起来就是30,这意味着你的分布看起来更像是不受监管的市场中的财富分布,而不是均匀分布。)

由于我们要生成一个统一的样本,我们可以通过忽略它来处理最大值,直到我们生成随机列表,然后检查是否出现了极不可能的偏差样本;如果真的发生了,我们就把结果扔掉,再试一次。(这被称为"拒绝采样",这是一种非常常见的技术,即使一半的样本被拒绝,它也能充分发挥作用。拒绝采样的优点是它不会引入偏差。)

因此,让我们回到如何生成具有给定和的非负数的均匀分布列表的问题。只要这些数字来自一个非常大的可能值的宇宙(就像该范围内的所有双精度浮点数字一样),这就很容易了。假设我们需要N的数字,这些数字加起来就是k。我们首先随机生成N-1个数字,每个数字都在(0,k)范围内。然后我们对这组数字进行排序,将0放在排序列表的一端,将k放在另一端。最后,我们计算连续元素之间的相邻差。这给了我们一个N数字的列表,这些数字加起来就是k,结果证明,这样生成的随机列表是可能性的几乎一致的样本。(同一个随机数可能生成两次,导致最终差异列表中出现零,这一事实带来了微小的偏差。零不是问题;问题是零出现的频率有点太高。但获得精确零的概率不到一亿分之一,而且只有精确零的频率有偏差。)。)

总结:

from random import uniform
def gen_random_list(N=25, k=100, min=2, max=25):
assert(N * min <= k <= N * max)
adjusted_k = k - min * N
while True:
endpoints = sorted(uniform(0, adjusted_k) for i in range(N - 1))
values = [*(end - begin + min
for begin, end in zip([0] + endpoints,
endpoints + [adjusted_k]))]
if all(v <= max for v in values):
return values

好的,如果我们需要一个整数列表呢?在这种情况下,上述程序更有可能产生零,并且偏差会很明显。为了避免这种情况,我们做了两个更改:

  • 我们没有调整范围使最小值为0,而是调整范围使其最小值为1。(这适用于整数,因为0和1之间没有整数。)现在,调整后的和将是k'=k-N*(min-1)
  • 其次,我们不是生成N-1个独立的随机值,而是从半开范围[1,k'中随机选择N+1个不同的值(使用random.sample)除此之外,算法是相同的。对生成的列表进行排序,计算相邻差异,并验证是否未超过最大值:
from random import sample
def gen_random_list_of_ints(N=25, k=100, min=2, max=25):
assert(N * min <= k <= N * max)
adjusted_k = k - (min - 1) * N
while True:
endpoints = sorted(sample(range(1, adjusted_k), N - 1))
values = [*(end - begin + min - 1
for begin, end in zip([0] + endpoints,
endpoints + [adjusted_k]))]
if all(v <= max for v in values):
return values

对于整数来说,最简单的方法是使用多项式分布,它具有很好的特性来求和所需的数字。首先,取最小值得到范围[0…s],然后只使用多项式并拒绝超过最大值的样本。您可以使用概率数组p来获得所需的行为。

如前所述,平均值为4。

代码,Python 3.10,Windows x64

import numpy as np
N = 25
minv = 2
maxv = 25
summ = 100
def sampling(N, minv, maxv, summ):
summa = summ - N*minv # fix range to [0...]
p = np.full(N, 1.0/N) # probabilities, uniform
while True:
t = np.random.multinomial(summa, p, size=1) + minv # back to desired range
if np.any(t > maxv): # check
continue # and reject
return t
q = sampling(N, minv, maxv, summ)
print(np.sum(q))

更新

多项式的Xi的平均值为E(Xi=n pi。在你的情况下,n=(100-25⎈2)=50。pi=1/25,因此E(Xi)=50/25=2,你必须加回2,所以你认为的平均值是4。

但是!您可以更改pi,使其不再是等概率的。例如,5⎈[0.1]+20 \9096;[0.5/20]将产生前五个rv,平均值为50 \9096 0.1+2=7,最后20个rv的平均值为50\9096;0.5/20+2=1.25+2=3.25

最新更新