What technique do I use for when I want to check all possible combinations of a set?

后端 未结 9 2055
既然无缘
既然无缘 2021-01-18 23:38

I\'m working through an interview question that goes like:

Given an array of integers and sum, check whether any combination adds up to the sum.

相关标签:
9条回答
  • 2021-01-19 00:12

    Recursively. Pseudo-code would be something like this:

    function f(set,currentelement,selectedelements,sum,wantedsum)
    {
    for (thiselement=currentelement+1 to lastelement)
       {
       if (sum+thiselement==wantedsum) print out selectedelements+thiselement;
       if (sum+thiselement<wantedsum)
          {
          f(set,thiselement,selectedelements+thiselement,sum+thiselement,wantedsum);
          }
       }
    
    0 讨论(0)
  • 2021-01-19 00:16

    There are multiple ways of solving this problem. One is the classical DP solution which others have posted. I'm going to post a solution that uses only O(S) memory, where S is the sum of all integers in the array (can be changed to mean the desired sum too) and another that uses a very efficient randomized algorithm that can be tested to be very fast for even hundreds of thousands of numbers of any size, and even rational and negative numbers.

    DP solution in O(nS) time and O(S) memory:

    //let F[i] = 1 if we can get sum i and 0 otherwise
    F[0] = 1; // we can always make sum 0
    for ( int i = 1; i <= n; ++i )
      for ( int j = S; j >= numbers[i]; --j )
        F[j] |= F[j - numbers[i]]; // basically, if F[j - numbers[i]] == 1, then we 
                                   // can add numbers[i] to make F[j] 1, otherwise 
                                   // we can't. A bitwise or operation will save us 
                                   // an if/else structure basically.
    

    Pseudocode for randomized algorithm: Let Used = list of numbers that you sum. Let Unused = list of numbers that you DON'T sum. Let tmpsum = 0. Let S = desired sum you want to reach.

    for ( each number x you read )
      toss a coin:
        if it's heads and tmpsum < S
          add x to Used
        else
          add x to Unused
    
    while ( tmpsum != S )
      if tmpsum < S 
        MOVE one random number from Unused to Used
      else
        MOVE one random number from Used to Unused
    
    print the Used list, containing the numbers you need to add to get S
    

    This will be much faster than the dynamic programming solution, especially for random inputs. The only problems are that you cannot reliably detect when there is no solution (you could let the algorithm run for a few seconds and if it doesn't finish, assume there is no solution) and that you cannot be sure you will get the solution with minimum number of elements chosen. Again, you could add some logic to make the algorithm keep going and trying to find a solution with less elements until certain stop conditions are met, but this will make it slower. However, if you are only interested in a solution that works and you have a LOT of numbers and the desired sum can be VERY big, this is probably better than the DP algorithm.

    Another advantage of this approach is that it will also work for negative and rational numbers with no modifications, which is not true for the DP solution, because the DP solution involves using partial sums as array indexes, and indexes can only be natural numbers. You can of course use hashtables for example, but that will make the DP solution even slower.

    To generate all combinations, you should look up backtracking: http://en.wikipedia.org/wiki/Backtracking

    For this problem, you need to use something like this:

    void back(int k)
    {
      if ( k > numElements )
      { 
        // add all the nums[i] for which st[i] == 1 and check
        // if their sum is what you desire, then return;
      }
    
      for ( int i = 0; i <= 1; ++i )
      {
        st[k] = i;
        back(k + 1);
      }
    }
    

    You should run it on paper for small number of elements to see how it works. You can optimize it by calculating the sum as you go, thus avoiding the final summation. This is the general idea.

    0 讨论(0)
  • 2021-01-19 00:18

    If you have positive as well as negative integers, you are going to run into a combinatorial explosion, where whatever algorithm you choose will slow by a fixed percentage for every increase in length of your array. (If you only have positive integers, you can bound your search once the target sum is exceeded.)

    A boundary question: are you allowed to reuse integers as well?

    You should search for 'combinatorial algorithms'. Knuths' tome-in-progress could help you quite a lot if you want to dig deeper into the question.

    0 讨论(0)
提交回复
热议问题