我正在开展一个有趣的项目,我需要一个算法来做如下:
生成长度n
的数字列表,其中加起来为x
我会选择整数列表,但理想情况下,我希望留下一组浮点数。
如果这个问题没有经过深入研究,我会非常惊讶,但我不确定该寻找什么。
我过去曾经解决过类似的问题,但这个问题本质上是截然不同的。在我生成将加起来为x的数字列表的不同组合之前。我确信我可以简单地强迫这个问题,但这似乎不是理想的解决方案。
任何人都知道这可以被称为什么,或者如何接近它?谢谢大家!
编辑:为了澄清,我的意思是列表应该是长度N,而数字本身可以是任何大小。
edit2:抱歉我不正确地使用'set',我用它作为列表或数组的所有术语。我明白它引起了混乱,我道歉。
答案 0 :(得分:6)
这是在Python中如何做到的
import random
def random_values_with_prescribed_sum(n, total):
x = [random.random() for i in range(n)]
k = total / sum(x)
return [v * k for v in x]
基本上你选择n个随机数,计算它们的总和并计算一个比例因子,这样总和将是你想要的。
请注意,这种方法不会产生“统一”切片,即如果在给定总和的所有分布中随机选取,那么您将获得的分布将更倾向于“平等”。
要查看原因,您只能描绘算法在具有规定总和的两个数字(例如1)的情况下所执行的操作:
点P
是通过拾取两个随机数得到的通用点,它在正方形[0,1]x[0,1]
内是均匀的。点Q
是通过缩放P
获得的点,因此总和必须为1.从图中可以清楚地看出,靠近中心的点具有更高的概率;例如,通过投射对角线(0,0)-(1,1)
上的任何点来找到正方形的正中心,而点(0, 1)
将仅发现从(0,0)-(0,1)
投射的点...对角线长度是sqrt(2)=1.4142...
,而方形边只有1.0
。
答案 1 :(得分:4)
实际上,您需要在 n 部分中生成 x 的分区。这通常通过以下方式完成: x 到 n 非负部分的分区可以通过以下方式表示:reserve n + x 免费场所,将 n 边框放在某些任意位置,其余部分放在石头上。石头组加起来为 x ,因此可能的分区数量为binomial coefficient( n + x \ atop 名词的)。
所以你的算法可以如下:选择 n -subset( n + x ) - set,它唯一确定 x 的分区为 n 部分。
在Knuth的TAOCP中,第3.4.2章讨论了随机抽样。见那里的Algortihm S.
算法S :(从总共N个中选择n个任意记录)
非整数的解决方案在算法上是微不足道的:你只需选择任意总计为0的 n 数字,并用它们的总和来规范它们。
答案 2 :(得分:4)
如果您想在由N-1
定义的x1 + x2 + ... + xN = x
- 维空间区域内均匀采样,那么您正在查看从Dirichlet distribution采样的特殊情况。采样程序比为xi
生成统一偏差更为复杂。这是在Python中实现它的一种方法:
xs = [random.gammavariate(1,1) for a in range(N)]
xs = [x*v/sum(xs) for v in xs]
如果您不太关心结果的采样属性,您可以生成统一的偏差并在之后更正其总和。
答案 3 :(得分:1)
以下是Javascript中上述算法的一个版本
function getRandomArbitrary(min, max) {
return Math.random() * (max - min) + min;
};
function getRandomArray(min, max, n) {
var arr = [];
for (var i = 0, l = n; i < l; i++) {
arr.push(getRandomArbitrary(min, max))
};
return arr;
};
function randomValuesPrescribedSum(min, max, n, total) {
var arr = getRandomArray(min, max, n);
var sum = arr.reduce(function(pv, cv) { return pv + cv; }, 0);
var k = total/sum;
var delays = arr.map(function(x) { return k*x; })
return delays;
};
您可以使用
进行调用var myarray = randomValuesPrescribedSum(0,1,3,3);
然后用
检查var sum = myarray.reduce(function(pv, cv) { return pv + cv;},0);
答案 4 :(得分:0)
这段代码做得很合理。我认为它产生的分布不同于6502的答案,但我不确定哪个更好或更自然。当然他的代码更清晰/更好。
import random
def parts(total_sum, num_parts):
points = [random.random() for i in range(num_parts-1)]
points.append(0)
points.append(1)
points.sort()
ret = []
for i in range(1, len(points)):
ret.append((points[i] - points[i-1]) * total_sum)
return ret
def test(total_sum, num_parts):
ans = parts(total_sum, num_parts)
assert abs(sum(ans) - total_sum) < 1e-7
print ans
test(5.5, 3)
test(10, 1)
test(10, 5)
答案 5 :(得分:0)
在python中:
a:创建一个(随机#&#39; s 0到1)次总数的列表;将0和总数追加到列表中
b:对列表进行排序,测量每个元素之间的距离
c:围绕列表元素
import random
import time
TOTAL = 15
PARTS = 4
PLACES = 3
def random_sum_split(parts, total, places):
a = [0, total] + [random.random()*total for i in range(parts-1)]
a.sort()
b = [(a[i] - a[i-1]) for i in range(1, (parts+1))]
if places == None:
return b
else:
b.pop()
c = [round(x, places) for x in b]
c.append(round(total-sum(c), places))
return c
def tick():
if info.tick == 1:
start = time.time()
alpha = random_sum_split(PARTS, TOTAL, PLACES)
end = time.time()
log('alpha: %s' % alpha)
log('total: %.7f' % sum(alpha))
log('parts: %s' % PARTS)
log('places: %s' % PLACES)
log('elapsed: %.7f' % (end-start))
的产率:
[2014-06-13 01:00:00] alpha: [0.154, 3.617, 6.075, 5.154]
[2014-06-13 01:00:00] total: 15.0000000
[2014-06-13 01:00:00] parts: 4
[2014-06-13 01:00:00] places: 3
[2014-06-13 01:00:00] elapsed: 0.0005839
据我所知,这种分布是统一的