繁体   English   中英

查找总计为n的集合的所有子集

[英]Find all subsets of a set that sum up to n

这是我提出的代码:

static void findNumbers(int[] list, int index, int current, int goal, String result)
{ 
  if (list.length < index || current>goal)
          return;
   for (int i = index; i < list.length; i++) {
      if (current + list[i] == goal)   {
         System.out.println(result + " " + String.valueOf(list[i]));
       }
       else if (current + list[i] < goal) {
           findNumbers(list, i + 1, current + list[i], goal, result + " " + String.valueOf(list[i]));
        }
   }
}

用它来调用:

findNumbers(array, starting_index, current_sum_till_now, target_sum, "");

有人可以帮我弄清楚这段代码的时间复杂性,我相信它是指数级的。

解决此问题的最佳方法是什么? 它是否正在使用回溯?

有人指出我犯了一个错误。 当我应该添加它们时,我正在增加递归调用的复杂性。 所以C(N) = C(N-1) + C(N-2) + ... 这同样适用于C(N-1)C(N-2)等。这意味着复杂度不是' O(N!)

这让我从另一个角度思考算法。 它正在检查每个可能的子集。 由于存在2^N - 1可能的子集(不考虑空子集),因此复杂度为O(2^N) ,我认为这是您的原始赌注。

您可以修改您的代码,使其工作原理是“如果数字是好的 - 添加它;忽略任何条件跳过当前数字”。 在这种情况下,代码将是:

static void findNumbers(int[] list, int index, int current, int goal, String result)
{ 
  if (list.length <= index || current>goal) // I've added the "=" which is missing in your code.
          return;
  if (current + list[index] == goal)   {
      System.out.println(result + " " + String.valueOf(list[i]));
  }
  else if (current + list[index] < goal) {
      findNumbers(list, index + 1, current + list[i], goal, result + " " + String.valueOf(list[i]));
  }
  findNumbers(list, index + 1, current, goal, result);
}

在这种情况下,复杂度将是O(2^n) ,这对于n=>5然后是O(n!)更好。 正如所指出的,如果对数组进行排序,则复杂性会降低。 这意味着您可以将第二个递归调用放在else if因为您将确保后面的所有数字都大于当前list[index]意味着跳过此值没有用,因为此调用的所有后续分支都不会生成任何有效的子集。

在这种情况下,最坏的情况是O(2^l) ,其中l是一个数字的索引,该数字大于你的目标并且在你的数组中,或者如果这样的数字不存在则为n

调用应该是: findNumbers(list,0,0,goal,"")

刚刚指出它比N ^ 2差,实际上它看起来像O(N!)。 您可以保存一些,因为您可以提前退出某些循环,但保存的程度取决于消除可能性的速度。

对于一个更加优化的解决方案,你将要努力,这是递归的一个很好的例子,因为任何基于循环的构造都将是可怕的。 您可以通过预先对数据进行排序来节省一些时间,以便首先获得更大的值,从而更快地达到目标(这将基本上消除列表中立即大于目标的任何内容)。 在消除了太大的条目后,我不确定它是否会直接帮助,因为您仍然需要将所有内容与所有内容进行比较,但它可能会改进处理器分支预测。

这是一种使用动态编程和背包类比的方法: -

  1. 按升序对集进行排序

  2. 评估子集直到list[i] <= N

  3. 解决背包的容量N和物品的价值和重量作为他们的list[i]

  4. 如果在最终背包容量N ==最大利润时,则存在至少一个解决方案子集。

  5. 使用成本矩阵回溯所有背包解决方案并获取所有解决方案子集。

时间复杂度: O(|S|*N + K) |S|- length of set and K is number of subsets. 这是伪多项式时间算法。

注意:问题是NP-hard没有发现多项式时间算法。

编辑: -从布尔矩阵中回溯解决方案

void retrace(int n,boolean[] solution,int target) {

   if(n>=0) {

        if(table[target][n-1]) {

            solution[n] = false;
            retrace(n-1,solution,target); 
        }

       if(table[target-numbers[n]][n-1]) {

            solution[n] = true;
            retrace(n-1,solution,target-numbers[n]);
       }
   }

   else {
       printf("\nsubset:-\n");
       for(int i=0;i<solution.length;i++) {

            if(solution[i]) {
                printf(number[i]+" ");
            }
       }

   }


}



  Call : - retrace(numbers.length-1,new boolean[numbers.length],target);

暂无
暂无

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

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