Algorithm 随机生成一组长度为n,总计为x的数字

Algorithm 随机生成一组长度为n,总计为x的数字,algorithm,math,combinatorics,Algorithm,Math,Combinatorics,我正在做一个有趣的项目,我需要一个算法,如下所示: 生成长度为n的数字列表,这些数字加起来等于x 我愿意接受整数列表,但理想情况下,我希望留下一组浮点数 如果这个问题没有被认真研究,我会非常惊讶,但我不知道该找什么 我过去也曾处理过类似的问题,但这次的性质截然不同。在我生成一系列数字的不同组合之前,这些数字加起来就是x。我确信我可以简单地解决这个问题,但这似乎不是理想的解决方案 有人知道这叫什么,或者怎么说吗?谢谢大家 编辑:为了澄清,我的意思是列表的长度应该是N,而数字本身可以是任意大小 ed

我正在做一个有趣的项目,我需要一个算法,如下所示: 生成长度为
n
的数字列表,这些数字加起来等于
x

我愿意接受整数列表,但理想情况下,我希望留下一组浮点数

如果这个问题没有被认真研究,我会非常惊讶,但我不知道该找什么

我过去也曾处理过类似的问题,但这次的性质截然不同。在我生成一系列数字的不同组合之前,这些数字加起来就是x。我确信我可以简单地解决这个问题,但这似乎不是理想的解决方案

有人知道这叫什么,或者怎么说吗?谢谢大家

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


edit2:很抱歉我不正确地使用了“set”,我将它用作列表或数组的一个全面术语。很抱歉,我知道这造成了混乱。

实际上,您需要将x划分为n个部分。这通常是通过以下方式完成的:将x划分为n个非负部分可以用以下方式表示:保留n+x自由位置,将n个边界放置到任意位置,将石头放置到其余位置。石头组加起来是x,因此可能的分区数量是(n+x\n)

所以你的算法可以是这样的:选择(n+x)-集的任意n子集,它唯一地确定x到n个部分的划分

在Knuth的TAOCP中,第3.4.2章讨论了随机抽样。请看这里的算法


算法S:(从总共n条记录中选择n条任意记录)

  • t=0,m=0
  • u=随机、均匀分布在(0,1)上
  • 如果(N-t)*u>=N-m,则跳过第t条记录并将t增加1;否则,在样本中包括第t个记录,将m和t增加1
  • 如果M

    非整数的解决方案在算法上很简单:您只需选择任意n个总和不等于0的数字,然后用它们的总和对它们进行范数。

    这就是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

    此代码的作用合理。我认为它产生了不同于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)
    
    随机导入
    def零件(总零件数和零件数):
    points=[random.random()表示范围内的i(num\u parts-1)]
    点。追加(0)
    点。附加(1)
    points.sort()
    ret=[]
    对于范围(1,len(点))内的i:
    ret.append((点[i]-点[i-1])*总计)
    回程网
    def测试(总数、数量和部分):
    ans=零件(总零件数和零件数)
    资产负债表(总和-总和)<1e-7
    打印ans
    测试(5.5,3)
    测试(10,1)
    测试(10,5)
    
    如果要在
    N-1
    -由
    x1+x2+…+定义的维度空间中均匀采样xN=x
    ,那么您将看到一个特殊情况,即从。取样程序比为
    xi
    生成均匀偏差稍微复杂一些。在Python中,有一种方法可以做到这一点:

    xs = [random.gammavariate(1,1) for a in range(N)]
    xs = [x*v/sum(xs) for v in xs]
    
    如果您不太关心结果的采样属性,那么只需生成均匀偏差,然后更正它们的和。

    在python中:

    a:创建一个(随机的0到1)倍总数的列表;将0和total追加到列表中

    对列表排序,测量每个元素之间的距离

    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
    

    据我所知,此分布是统一的

    这是上述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 sum = myarray.reduce(function(pv, cv) { return pv + cv;},0);
    

    清楚地说,“长度
    n
    ”是指整数的十进制表示形式,不带前导零,应该是
    n
    位数,对吗?是否需要整数?如果没有,只需生成
    n
    随机数,计算它们的和,并将它们缩小或放大到所需的和。@jwodder我为歧义感到抱歉,我编辑了我的问题,以澄清可能的重复,其中还讨论了一些微妙之处,这将起作用,除非所需的总数为零(将给出一个不太随机的输出),如果随机数之和恰好非常接近零(将爆炸或产生巨大的数字)。虽然这仍然是最好的解决方案,但可能需要做一些调整才能达到通用性。非常感谢,非常简单。我刚刚用javascript毫不费力地实现了它。请注意,这个分布在所有和x之和的n元组上是不一致的(即:并非所有n元组的可能性都相同)。很可能,只要输出看起来有点随机,询问者可能不会太在意。B
    var sum = myarray.reduce(function(pv, cv) { return pv + cv;},0);