[英]How to generate three random numbers, whose sum is 1?
I need to generate 3 random numbers, the amount of which is equal to 1.我需要生成 3 个随机数,其数量等于 1。
My implementation does not support uniform distribution.我的实现不支持统一分发。 :(
:(
Just get 3 random numbers and then calculate a factor which is 1 / [sum of your numbers].只需获得 3 个随机数,然后计算一个因子,即 1 / [你的数字之和]。 Finally multiply each of the random numbers with that factor.
最后将每个随机数乘以该因子。 The sum will be 1.
总和将为 1。
This is actually a tricky question.这实际上是一个棘手的问题。 First of all:
首先:
Daren 's solution is not uniform because it does not support having two numbers > 1/3. Daren的解决方案并不统一,因为它不支持两个数字 > 1/3。
Simen 's solution is not uniform assuming the "pick a random number" draws from a uniform distribution, but this is a bit more subtle.假设“选择一个随机数”来自均匀分布, Simen的解决方案并不均匀,但这有点微妙。 It is at least symmetric between the variables (ie the probability of [a, b, c] is the same as that of any permutation of that), but it heavily favors solutions closer to (1/3, 1/3, 1/3).
它至少在变量之间是对称的(即 [a, b, c] 的概率与它的任何排列的概率相同),但它非常有利于接近 (1/3, 1/3, 1/ 3). Think about it this way by looking at extreme cases: (1/3, 1/3, 1/3) could have come from any (a, a, a), where a ranges from 0 through 1. (1, 0, 0), an equally valid triple, must come from (1, 0, 0).
通过查看极端情况以这种方式思考:(1/3, 1/3, 1/3) 可能来自任何 (a, a, a),其中 a 的范围从 0 到 1。 (1, 0, 0),一个同样有效的三元组,必须来自 (1, 0, 0)。
One solution: The set of positive numbers that add to 1 form an equilateral triangle in three-space, with coordinates (1,0,0), (0,1,0), (0,0,1).一种解法:正数与1相加构成三空间的等边三角形,坐标为(1,0,0),(0,1,0),(0,0,1)。 Extend that to a parallelogram -- eg by adding a point (1,1,-1) as the fourth point.
将其扩展为平行四边形——例如,添加一个点 (1,1,-1) 作为第四个点。 This double's the area -- map the second area to the first, so that it suffices to pick a random point in this parallelogram.
这是区域的两倍——将第二个区域映射到第一个区域,这样就足以在这个平行四边形中选择一个随机点。
The parallelogram can be sampled uniformly via (0,0,1) + A(1,0,-1) + B (0,1,-1), where A and B range uniformly from 0 to 1.平行四边形可以通过 (0,0,1) + A(1,0,-1) + B (0,1,-1) 均匀采样,其中 A 和 B 均匀地从 0 到 1。
-A -一种
Generate two random numbers between 0 and 1. Divide those each by 3. The third is the difference of 1 and the two random thirds:生成介于 0 和 1 之间的两个随机数。将它们分别除以 3。第三个是 1 与两个随机三分之一的差值:
void Main()
{
Random r = new Random();
double d1 = r.NextDouble() / 3.0;
double d2 = r.NextDouble() / 3.0;
double d3 = 1.0 - d1 - d2;
System.Console.WriteLine(d1);
System.Console.WriteLine(d2);
System.Console.WriteLine(d3);
System.Console.WriteLine(d1 + d2 + d3);
}
this outputs the following in LINQPad:这会在 LINQPad 中输出以下内容:
0.0514050276878934
0.156857372489847
0.79173759982226
1
There is an easy way to do this, but you need to be able to generate a uniform random number.有一种简单的方法可以做到这一点,但您需要能够生成一个统一的随机数。
Let X be uniform on (0,2/3).让 X 在 (0,2/3) 上均匀分布。 If X < 1/3, let Y = X + 1/3.
如果 X < 1/3,则令 Y = X + 1/3。 Otherwise let Y = X - 1/3.
否则令 Y = X - 1/3。 Let Z = 1 - X - Y.
令 Z = 1 - X - Y。
Under this setup, X, Y, and Z will sum to 1, they will all have identical uniform (0, 2/3) marginal distributions, and all three pairwise correlations will be -(1/2).在此设置下,X、Y 和 Z 的总和为 1,它们都将具有相同的均匀 (0, 2/3) 边缘分布,并且所有三个成对相关均为 -(1/2)。
UPDATE更新
Slight variation on Marnix' answer: Marnix 的回答略有不同:
a
from [0,1]a
x
from [0,a] and y
from [a,1] x
来自 [0,a] 和y
来自 [a,1]x
, yx
, 1-y
x
、 yx
、 1-y
2/2 methods: 2/2 方法:
Sorry I don't know C# this is what it looks like in python:对不起,我不知道 C# 这是它在 python 中的样子:
import random
import time
PARTS = 5
TOTAL = 10
PLACES = 3
def random_sum_split(parts, total, places):
a = [0.0, total]
for i in range(parts-1):
a.append(random.random()*total)
a.sort()
b = []
for i in range(1,(parts+1)):
b.append(a[i] - a[i-1])
if places != None:
b = [round(x, places) for x in b]
c = b[-(parts-1):]
d = total - sum(c)
if places != None:
d = round(d, places)
c.insert(0, d)
log(a)
log(b)
log(c)
log(d)
return c
def tick():
if info.tick == 1:
start = time.time()
alpha = random_sum_split(PARTS, TOTAL, PLACES)
log('********************')
log('***** RESULTS ******')
log('alpha: %s' % alpha)
log('total: %.7f' % sum(alpha))
log('parts: %s' % PARTS)
log('places: %s' % PLACES)
end = time.time()
log('elapsed: %.7f' % (end-start))
Yields:产量:
Waiting...
Saved successfully.
[2014-06-13 00:01:00] [0.0, 1.3005056784596913, 3.0412441135728474, 5.218388755020509, 7.156425483589107, 10]
[2014-06-13 00:01:00] [1.301, 1.741, 2.177, 1.938, 2.844]
[2014-06-13 00:01:00] [1.3, 1.741, 2.177, 1.938, 2.844]
[2014-06-13 00:01:00] 1.3
[2014-06-13 00:01:00] ********************
[2014-06-13 00:01:00] ***** RESULTS ******
[2014-06-13 00:01:00] alpha: [1.3, 1.741, 2.177, 1.938, 2.844]
[2014-06-13 00:01:00] total: 10.0000000
[2014-06-13 00:01:00] parts: 5
[2014-06-13 00:01:00] places: 3
[2014-06-13 00:01:00] elapsed: 0.0036860
1/2 methods: 1/2方法:
Sorry don't know C#, here's the python:抱歉不知道 C#,这里是 python:
import random
import time
PARTS = 5
TOTAL = 10
PLACES = 3
def random_sum_split(parts, total, places):
a = []
for n in range(parts):
a.append(random.random())
b = sum(a)
c = [x/b for x in a]
d = sum(c)
e = c
if places != None:
e = [round(x*total, places) for x in c]
f = e[-(parts-1):]
g = total - sum(f)
if places != None:
g = round(g, places)
f.insert(0, g)
log(a)
log(b)
log(c)
log(d)
log(e)
log(f)
log(g)
return f
def tick():
if info.tick == 1:
start = time.time()
alpha = random_sum_split(PARTS, TOTAL, PLACES)
log('********************')
log('***** RESULTS ******')
log('alpha: %s' % alpha)
log('total: %.7f' % sum(alpha))
log('parts: %s' % PARTS)
log('places: %s' % PLACES)
end = time.time()
log('elapsed: %.7f' % (end-start))
yeilds:产量:
Waiting...
Saved successfully.
[2014-06-13 00:01:00] [0.33561018369775897, 0.4904215932650632, 0.20264927800402832, 0.118862130636748, 0.03107818050878819]
[2014-06-13 00:01:00] 1.17862136611
[2014-06-13 00:01:00] [0.28474809073311597, 0.41609766067850096, 0.17193755673414868, 0.10084844382959707, 0.02636824802463724]
[2014-06-13 00:01:00] 1.0
[2014-06-13 00:01:00] [2.847, 4.161, 1.719, 1.008, 0.264]
[2014-06-13 00:01:00] [2.848, 4.161, 1.719, 1.008, 0.264]
[2014-06-13 00:01:00] 2.848
[2014-06-13 00:01:00] ********************
[2014-06-13 00:01:00] ***** RESULTS ******
[2014-06-13 00:01:00] alpha: [2.848, 4.161, 1.719, 1.008, 0.264]
[2014-06-13 00:01:00] total: 10.0000000
[2014-06-13 00:01:00] parts: 5
[2014-06-13 00:01:00] places: 3
[2014-06-13 00:01:00] elapsed: 0.0054131
Building upon @Simen and @Daren Thomas' answers, here is a service function that returns a list of doubles with uniform random values, where you can specify how many numbers you want, the total sum and the amount of digits on the numbers:在@Simen 和@Daren Thomas 的回答的基础上,这里有一个服务函数,它返回具有统一随机值的双精度列表,您可以在其中指定所需的数字数量、总和以及数字的位数:
public static List<double> GetListOfRandomDoubles(int countOfNumbers, double totalSum, int digits)
{
Random r = new Random();
List<double> randomDoubles = new List<double>();
double totalRandomSum = 0;
for (int i = 0; i < countOfNumbers; i++)
{
double nextDouble = r.NextDouble();
randomDoubles.Add(nextDouble);
totalRandomSum += nextDouble;
}
double totalFactor = 1 / totalRandomSum;
totalFactor = totalFactor * totalSum;
for (int i = 0; i < randomDoubles.Count; i++)
{
randomDoubles[i] = randomDoubles[i] * totalFactor;
randomDoubles[i] = Math.Round(randomDoubles[i], digits);
}
double currentRandomSum = 0;
randomDoubles.ForEach(x => currentRandomSum += x);
randomDoubles[0] += totalSum - currentRandomSum;
return randomDoubles;
}
Usage:用法:
// Get list of 7 random doubles that sum to 100, with up to 2 digits on each number
List<double> randomDoubles = GetListOfRandomDoubles(7, 100, 2);
Returns:退货:
12.25, 19.52, 15.49, 16.45, 1.92, 13.12, 21.25
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.