得到N个总和为M的随机数
Posted
技术标签:
【中文标题】得到N个总和为M的随机数【英文标题】:Getting N random numbers whose sum is M 【发布时间】:2011-02-08 01:31:18 【问题描述】:我想得到 N 个随机数,总和是一个值。
例如,假设我想要 5 个总和为 1 的随机数。
那么,一个有效的可能性是:
0.2 0.2 0.2 0.2 0.2
另一种可能是:
0.8 0.1 0.03 0.03 0.04
等等。我需要这个来为模糊 C 均值创建一个归属矩阵。
【问题讨论】:
Random numbers that add to 100: Matlab的可能重复 均匀分布?非负数?在 [0,1] 范围内? 【参考方案1】:随机生成 N 个正数,总和为正数 M,其中每个可能的组合都具有相同的可能性:
生成 N 个指数分布的随机变量。生成这样一个数字的一种方法可以写成——
number = -ln(1.0 - RNDU())
其中ln(x)
是x
的自然对数,RNDU()
是一种返回0 或更大且小于1 的统一随机变量的方法(例如,javascript 的Math.random()
)。请注意,生成具有均匀分布的 N 个数字并不理想,因为会导致随机变量组合的有偏分布。但是,上面给出的实现有几个问题,例如being ill-conditioned at large values,因为分布的右尾,特别是当实现涉及浮点运算时。 another answer 中给出了另一种实现。
用这种方式生成的数字除以它们的总和。
将每个数字乘以 M。
结果是 N 个数字,其总和大约等于 M(由于舍入误差,我说“大约”)。另见***文章Dirichlet distribution。
这个问题也等价于generating random variates uniformly from an N-dimensional unit simplex的问题。
但是,为了获得更高的准确性(与使用浮点数的替代方案相比,这在实践中经常发生),您应该考虑生成n
random integers that sum to an integer m * x
,并将这些整数视为n
有理数的分子和分母x
(假设m
是一个整数,则总和为m
)。您可以将x
选择为一个较大的数字,例如 232 或 264 或具有所需精度的其他数字。如果x
为0 且m
为整数,这解决了生成随机整数 的问题,其总和为m
。
下面的伪代码展示了如何生成n
均匀随机整数大于 0 与给定的正和,以随机顺序。 (此算法在 Smith 和 Tromble,“Sampling Uniformly from the Unit Simplex”,2004 中提出。)在下面的伪代码中——
PositiveIntegersWithSum
方法以随机顺序返回大于 0 的整数 m
,
IntegersWithSum
方法以随机顺序返回 n
整数 0 或大于 m
,并且
Sort(list)
按升序对list
中的项目进行排序(请注意,排序算法超出了此答案的范围)。
METHOD PositiveIntegersWithSum(n, m)
if n <= 0 or total <=0: return error
ls = [0]
ret = NewList()
while size(ls) < n
c = RNDINTEXCRANGE(1, m)
found = false
for j in 1...size(ls)
if ls[j] == c
found = true
break
end
end
if found == false: AddItem(ls, c)
end
Sort(ls)
AddItem(ls, total)
for i in 1...size(ls): AddItem(ret,
ls[i] - ls[i - 1])
return ret
END METHOD
METHOD IntegersWithSum(n, total)
if n <= 0 or total <=0: return error
ret = PositiveIntegersWithSum(n, total + n)
for i in 0...size(ret): ret[i] = ret[i] - 1
return ret
END METHOD
这里,RNDINTEXCRANGE(a, b)
在区间 [a, b) 中返回一个统一的随机整数。
【讨论】:
【参考方案2】:简答:
只需生成 N 个随机数,计算它们的总和,然后将每个数除以 和乘以 M。
更长的答案:
上述解决方案不会产生均匀分布,这可能是一个问题,具体取决于这些随机数的用途。 Matti Virkkunen 提出的另一种方法:
在0和1之间生成N-1个随机数,将数字0和1相加 将它们自己添加到列表中,对它们进行排序,然后将它们的差异 相邻的数字。
这会产生均匀分布,如 here 所述
【讨论】:
然后乘以 M(除非 M 是 1,如示例中)。 这不是一个好的随机化,因为增加 N 会产生趋于零的方差 我想跳上“这个解决方案确实提供了分布良好的答案”的潮流 这是一个糟糕的答案。请参阅此答案,该答案使用漂亮的图表证明此解决方案不正确:***.com/a/8068956/88821 请看下面更准确的取差答案【参考方案3】:很遗憾,如果您想要统一随机数,这里的一些答案是不正确的。保证均匀随机数的最简单(在许多语言中也是最快的)解决方案只是
# This is Python, but most languages support the Dirichlet.
import numpy as np
np.random.dirichlet(np.ones(n))*m
其中n
是您要生成的随机数的数量,m
是结果数组的总和。这种方法产生正值,对于生成总和为 1(令 m = 1)的有效概率特别有用。
【讨论】:
我发现总和并不总是完美地加起来M,【参考方案4】:在 Java 中:
private static double[] randSum(int n, double m)
Random rand = new Random();
double randNums[] = new double[n], sum = 0;
for (int i = 0; i < randNums.length; i++)
randNums[i] = rand.nextDouble();
sum += randNums[i];
for (int i = 0; i < randNums.length; i++)
randNums[i] /= sum * m;
return randNums;
【讨论】:
> 然后乘以 M(除非 M 像示例中那样为 1)。 – ILMTitan 4 月 14 日 18:49randNums[i] /= sum * m;
等价于randNums[i] = randNums[i] / (sum * m);
。这需要是randNums[i] = randNums[i] / sum * m;
,以便操作顺序正确。【参考方案5】:
只需生成 N 个随机数,计算它们的总和,然后将每个数除以 总和。
Expanding on Guillaume's accepted answer,这里有一个 Java 函数可以做到这一点。
public static double[] getRandDistArray(int n, double m)
double randArray[] = new double[n];
double sum = 0;
// Generate n random numbers
for (int i = 0; i < randArray.length; i++)
randArray[i] = Math.random();
sum += randArray[i];
// Normalize sum to m
for (int i = 0; i < randArray.length; i++)
randArray[i] /= sum;
randArray[i] *= m;
return randArray;
在测试运行中,getRandDistArray(5, 1.0)
返回以下内容:
[0.38106150346121903, 0.18099632814238079, 0.17275044310377025, 0.01732932296660358, 0.24786240232602647]
【讨论】:
【参考方案6】:我认为值得注意的是the currently accepted answer并没有给出均匀分布:
"只需生成N个随机数, 计算它们的总和,将每一个除以 总和”
要了解这一点,让我们看一下 N=2 和 M=1 的情况。这是一个简单的情况,因为我们可以通过在 (0,1) 范围内均匀选择 x 来生成列表 [x,1-x]。 建议的解决方案生成一对 [x/(x+y), y/(x+y)],其中 x 和 y 在 (0,1) 中是一致的。为了分析这一点,我们选择一些 z 使得 0
概率(x/(x+y)
我做了一些快速计算,似乎到目前为止唯一能导致均匀分布的解决方案是proposed by Matti Virkkunen:
“生成0到1之间的N-1个随机数,将数字0和1本身加入列表,排序,取相邻数字的差。”
【讨论】:
在你的例子中,x+y = 1 所以 P(\fracxx+y @Apprentice Queue:请注意,我只是在分析上面文本中 0 我认为您的分析不正确,因为正常/均匀是指值的分布,当将范围除以常数时不会改变。如果原始分布是均匀的,则除以总和会产生一个均匀分布,该分布会加到总和上。正常情况下也是如此。 是的,提供的解决方案不提供均匀分布。因为您将约束应用于改变分布的均匀分布。因此,虽然 .1 .1 .1 .1 .1 对于原始分布来说是一个很好的生成,但在这个约束下,它不是。所以分布会改变。 我错过了什么吗?我知道接受的答案不提供 normal 分布,但它不提供 uniform 分布吗?统一不是意味着每个数字都是同样随机的,并且不会或多或少地更高或更低吗? 0.2 0.2 0.2 0.2 0.2 加起来等于 1。这是一个均匀分布。如果您的目标数字是 57 而不是 1,则取 0.2s,除以 1,乘以 57...您得到 11.4 11.4 11.4 11.4 11.4,如果我错了,请纠正我,这也是均匀分布。人们一直在说“明显的例子”,但没有一个例子对我来说是显而易见的。【参考方案7】:-
生成 N-1 个随机数。
计算所述数字的总和。
将计算的总和与所需总和之间的差添加到集合中。
你现在有 N 个随机数,它们的总和就是期望的总和。
【讨论】:
除非你得到最后一个数字是负数。【参考方案8】:你在约束方面有点苗条。很多很多的程序都会起作用。
例如,数字是正态分布的吗?制服? 我假设所有数字都必须是正数并且均匀分布在平均值 M/N 周围。
试试这个。
-
平均值 = M/N。
生成介于 0 和 2*mean 之间的 N-1 个值。这可以是一个介于 0 和 1 之间的标准数字,u,随机值是 (2*u-1)*mean 以创建适当范围内的值。
计算 N-1 个值的总和。
剩余值为 N-sum。
如果剩余值不符合约束条件(0 到 2*均值),请重复该过程。
【讨论】:
“剩余值”不是统一选择的,因为 (n-1) 个统一随机数之和不统一。【参考方案9】:在0和1之间生成N-1个随机数,将数字0和1本身加入列表,排序,取相邻数字的差。
【讨论】:
好吧,这太复杂了。如果有人想将其限制为整数,也许有用(显然使用大于 0 到 1 的范围) 我不保证我不完全理解的数学。 看起来这是迄今为止唯一能实现均匀分布的解决方案(除非我在验证时犯了错误,这总是可能的)。 @chovy:要获得“8 之间的 0”,请在算法中使用 8 而不是 1,并为 N 使用 3。它起作用的原因是,它就像取一段具有设定长度的字符串,在随机的地方标记它,然后在标记的地方切割它。你最终会得到 N 段字符串,它们必须加起来等于原始长度。 如果我的数字有下限,有没有办法做到这一点?数字必须大于 A。以上是关于得到N个总和为M的随机数的主要内容,如果未能解决你的问题,请参考以下文章