繁体   English   中英

总计为A的N个整数的不同组的数量

[英]number of different groups of N integers that sum up to A

我想计算N个整数的不同有序组的数量,以便每个组的元素总和为A.

例如:如果N = 3且A = 3,结果应为10:
1 = [3,0,0]
2 = [2,1,0]
3 = [1,2,0]
4 = [0,3,0]
5 = [2,0,1]
6 = [1,1,1]
7 = [0,2,1]
8 = [1,0,2]
9 = [0,1,2]
10 = [0,0,3]

我这样做的方式是蛮力:

public static int calc(int a, int n){
    if (n <= 1 || a == 0) return 1;

    int sum = 0;
    for (int i=0; i<=n; i++)
        sum += calc(a - i, n - 1);

    return sum;
}

我怀疑可能有更好的方法(我错过了一些数学计算......)吗?

编辑在原始问题中,我忘了考虑订单

这是A到N部分(包括零部分)的组合成分 对(A,N)的组成数等于C(A + N-1,A),其中C()是组合数,即二项式系数。 这里这里看到相同的公式

想象一下长度为A大段。 并想象N-1有序分隔符,将分段分成几部分。 因此,每个部分是一个加数,而整个部分是一个总和。

因此,您只需提供算法来枚举分隔符位置。

可以放入N+1位置的第一个分隔符P_0 = {0,1,... N}

第二个分隔符可以进入P_1 = {P_0,... N}中的任何一个

等等。

您可以使用递归来实现此功能。

我确信有一个数学计算来回答这个问题,但由于这是一个编程问答,我会告诉你如何让你的程序更快地给出答案:你可以使用memoization

目前,您的程序每次都会重新计算calc(a, n)的答案。 但是,答案可以计算一次,因为它在后续调用中不会更改。 calc(a,n)的结果添加一个2D数组,用-1初始化,并在计算结果之前使用它来查找结果,以节省大量时间反复重复计算相同的数字:

private static int[][] memo = new int[30][30];
static {
    for(int i = 0 ; i != 30 ; i++)
        for(int j = 0 ; j != 30 ; j++)
            memo[i][j] = -1;
}
public static int calc(int a, int n){
    if (n <= 1 || a == 0) return 1;
    if (memo[a][n] > 0) return memo[a][n];
    int sum = 0;
    for (int i=0; i<=n; i++)
        sum += calc(a - i, n - 1);
    return (memo[a][n] = sum);
}

对于枚举:使用上面其他解决方案中给出的公式,更加高效。 除非需要,否则您永远不想实际生成一整套n整数合成。 它们具有难以处理的属性,特别是如果您只想要总计它们,而不是生成它们。 生成它们是另一个问题......

用于生成:使用无环算法...有很多O(1) - 灰色代码序列结果。 限制整数组合的变化非常少,没有或没有无循环算法。 在整类组合的这类问题中有许多算法,其中大多数是非常具体的,但是对于这个特定问题存在大量现代无循环算法。 超级高效。 除非你有大量的并行计算功能,否则蛮力绝不是解决这个问题的方法。 Google或Google Scholar随时为您服务! :d

希望这可以帮助!

我找到了另一个解决方案,只是递归和没有分隔符:

public class App201210121604 {

public static Vector<int[]> split(int sum, int count) {

    if( sum < 0 ) {
        throw new IllegalArgumentException("Negative sum is not allowed");
    }

    Vector<int[]> ans = new Vector<int[]>();

    // "reserved" end of recursion
    if( count <= 0 ) {
        // nothing to do
    }

    // end of recursion
    else if( count == 1 ) {
        ans.add(new int[] {sum});
    }

    // body of recursion
    else {
        // for each first summand from 0 to summ
        for(int i=0; i<=sum; ++i) {

            // do a recursion to get the "tail"
            for(int[] tail : split(sum-i, count-1)) {

                int[] group = new int[count];
                group[0] = i;
                System.arraycopy(tail, 0, group, 1, count-1);

                ans.add(group);
            }
        }
    }

    return ans;
}

public static void main(String[] args) {

    Vector<int[]> ans = split(8, 4);

    for(int[] group : ans) {
        for(int i=0; i<group.length; ++i) {
            if( i>0 ) System.out.print("+");
            System.out.print(group[i]);
        }
        System.out.println("");
    }
}

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM