从给定数字中生成N的所有可能总和的有效方式

时间:2017-09-03 23:39:36

标签: java arrays algorithm sum brute-force

所以我在想这个问题的算法:
想象一下,我们有以下数组

a = {1, 2, 4, 5, 7}

并希望从这些数字中得到所有可能的总和,这些数字等于给定的数字N(目前,求和的顺序并不重要)。
如果N=8有几个有效答案:

  

1 + 2 + 5
1 + 7
2 + 2 + 2 + 2 ...

所以现在我会向你解释我的方法。

首先,我们创建一个长度为b的新数组N+1,并将数组x中的每个数字a放到索引处的数组b [N-x]并用-1填充其余元素。
这应该创建以下数组:

b = {-1, 7, -1, 5, 4, -1, 2, 1, -1}

正如您所看到的,b的每个元素都不是-1,需要添加到其索引中以获取N。 (示例:b[1]=7 =>索引= 1,值= 7 => 7 + 1 = 8 = N

我们现在做的是,我们遍历此数组x的每个索引b[x]!=-1并从头开始整个过程​​,但这次我们说N=x以便我们得到获取索引x的所有可能总和(如上所示)是我们需要添加到b[x]元素值的值。

我们以递归的方式完成所有这些操作,一旦到达索引等于0的点,我们就会打印整个连接链。



我用Java实现了它,它工作正常:

public static void main(String[] args) {
        int[]numbers = {1, 2, 4, 5, 7};
        int n = 8;

        numbers = summandArray(numbers, n);
        printArray(numbers);


        getSums(numbers, n, "");

    }

    public static void getSums(int[] numbers, int n, String summands) {
        String startSummands = summands;
        for(int i = 0; i < numbers.length; i++) {
            summands = startSummands;
            if(numbers[i] != -1) {
                int neededSummand = i;
                if(neededSummand == 0) {
                    summands+=numbers[i];
                    System.out.println(summands);
                }
                else {
                    int[] newNumbers = summandArray(numbers, neededSummand);
                    summands+=numbers[i]+"+";
                    getSums(newNumbers, neededSummand, summands);
                }
            }

        }

    }

    public static int[] summandArray(int[] array, int n) {
        int[] result = new int[n+1];
        for(int i = 0; i < result.length; i++) {
            result[i] = -1;
        }
        for(int i = 0; i < array.length; i++) {
            if(array[i] <= n && array[i] != -1) {
                int index = n-array[i];
                result[index] = array[i];
            }
        }
        return result;
    }

但是我不太确定算法的执行情况。我的意思是,这不是一个非常复杂的蛮力版本,因此效率非常低吗?

感谢您抽出宝贵时间:)

2 个答案:

答案 0 :(得分:0)

您要做的是获取数组的排列。使用每个排列,您可以根据需要对值进行总计。还有另一个StackExchange托管站点允许完成代码审查。您可能希望与其他算法进行比较:

Permutation of array

Print out all permutations of an Array

答案 1 :(得分:0)

你的方法就像子集和问题的有效动态编程算法一样(你的任务就是这个问题),但似乎它没有重用计算数据。请注意,使用DP计算可能变量的数量需要伪多项式时间O(N * Sum),而所有变量的输出都呈指数增长。

您可以将已计算的子任务存储在其他数组/列表中 - 这是memoization(从上到下)的方法,很容易修改递归以利用它。

另一种DP方法 - 自下而上填表:

 Make array Sol[n+1] of lists of lists
 For every input item X:
      Scan Sol array from the last to the first index i
         if (i==X) or (Sol[i-X] is not empty): 
               Add all Sol[i-X][j]+X to  Sol[X] list

  example for [1,2,3,5] array after outer loops:
  [] [1] []...
  [] [[1]] [[2]] [[1,2]]...
  [] [[1]] [[2]] [[1,2],[3]] [[1,3]] [[2,3]] [[1,2,3]]
  [] [[1]] [[2]] [[1,2],[3]] [[1,3]] [[2,3],[5]] [[1,2,3],[1,5]] 
       [[2,5]] [[1,2,5],[3,5]] [[1,3,5]] [[2,3,5]] [[1,2,3,5]]