随机生成一组n长度总和x的数字

时间:2011-08-16 19:24:18

标签: algorithm math combinatorics

我正在开展一个有趣的项目,我需要一个算法来做如下: 生成长度n的数字列表,其中加起来为x

我会选择整数列表,但理想情况下,我希望留下一组浮点数。

如果这个问题没有经过深入研究,我会非常惊讶,但我不确定该寻找什么。

我过去曾经解决过类似的问题,但这个问题本质上是截然不同的。在我生成将加起来为x的数字列表的不同组合之前。我确信我可以简单地强迫这个问题,但这似乎不是理想的解决方案。

任何人都知道这可以被称为什么,或者如何接近它?谢谢大家!

编辑:为了澄清,我的意思是列表应该是长度N,而数字本身可以是任何大小。

edit2:抱歉我不正确地使用'set',我用它作为列表或数组的所有术语。我明白它引起了混乱,我道歉。

6 个答案:

答案 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)的情况下所执行的操作:

enter image description here

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个任意记录)

  1. t = 0,m = 0;
  2. u =随机,均匀分布在(0,1)
  3. if(N-t)* u> = n - m,跳过第t个记录并将t增加1;否则包括样本中的第t个记录,将m和t增加1
  4. 如果M < n,返回2,否则,算法完成

  5. 非整数的解决方案在算法上是微不足道的:你只需选择任意总计为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

据我所知,这种分布是统一的