Search code examples
c++algorithmbacktracking

Does this algorithm actually work? Sum of subsets backtracking algorithm


I want to know if this backtracking algorithm actually works.

In the text book Foundations of Algorithms, 5th edition, it is defined as follows:

Algorithm 5.4: The Backtracking Algorithm for the Sum-of-Subsets Problem

Problem: Given n positive integers (weights) and a positive integer W, determine all combinations of the integers that sum up to W.

Inputs: positvie integer n, sorted (nondecreasing order) array of positive integers w indexed from 1 to n, and a positive integer W.

Outputs: all combinations of the integers that sum to W.

void sum_of_subsets(index i, 
                    int weight, int total)  {
  if (promising(i))
     if (weight == W)
        cout << include[1] through include [i];
     else {
        include[i + 1] = "yes";               // Include w[i + 1].
        sum_of_subsets(i + 1, weight + w[i + 1], total - w[i + 1]);
        include[i + 1] = "no";                // Do not include w[i + 1].
        sum_of_subsets(i + 1, weight, total - w[i + 1]);
     }
}

bool promising (index i);  {
  return (weight + total >= W) && (weight == W || weight + w[i + 1] <= W);
}

Following our usual convention, n, w, W, and include are not inputs to our routines. If these variables were defined globally, the top-level call to sum_of_subsets would be as follows:

sum_of_subsets(0, 0, total);

At the end of chapter 5, exercise 13 asks:

  1. Use the Backtracking algorithm for the Sum-of-Subsets problem (Algorithm 5.4) to find all combinations of the following numbers that sum to W = 52:

    w1 = 2     w2 = 10     w3 = 13     w4 = 17     w5 = 22     w6 = 42

I've implemented this exact algorithm, accounting for arrays that start at 1 and it just does not work...

 void sos(int i, int weight, int total) {
    int yes = 1;
    int no = 0;

    if (promising(i, weight, total)) {
        if (weight == W) {
            for (int j = 0; j < arraySize; j++) {
                std::cout << include[j] << " ";
            }
            std::cout << "\n";
        }
        else if(i < arraySize) {
            include[i+1] = yes;
            sos(i + 1, weight + w[i+1], total - w[i+1]);
            include[i+1] = no;
            sos(i + 1, weight, total - w[i+1]);
        }
    }
}


int promising(int i,  int weight, int total) {
    return (weight + total >= W) && (weight == W || weight + w[i+1] <= W);
}   

I believe the problem is here:

sos(i + 1, weight, total - w[i+1]);
sum_of_subsets(i+1, weight, total-w[i+1]);

When you reach this line you are not backtracking correctly.

Is anyone able to identify a problem with this algorithm or actually code it to work?


Solution

  • I personally find the algorithm problematic. There is no bounds checking, it uses a lot of globals, and it assumes an array is indexed from 1. I don't think you can copy it verbatim. It's pseudocode for the actual implementation. In C++ arrays always start from 0. So you're likely to have problems when you try do include[i+1] and you are only checking i < arraySize.

    The algorithm also assumes you have a global variable called total, which is used by the function promising.

    I have reworked the code a bit, putting it inside a class, and simplified it somewhat:

    class Solution
    {
    private:
        vector<int> w;
        vector<int> include;
    
    public:
        Solution(vector<int> weights) : w(std::move(weights)),
            include(w.size(), 0) {}
    
        void sos(int i, int weight, int total) {
            int yes = 1;
            int no = 0;
            int arraySize = include.size();
    
            if (weight == total) {
                for (int j = 0; j < arraySize; j++) {
                    if (include[j]) {
                        std::cout << w[j] << " ";
                    }
                }
                std::cout << "\n";
            }
            else if (i < arraySize)
            {
                include[i] = yes;
                //Include this weight
                sos(i + 1, weight + w[i], total);
                include[i] = no;
                //Exclude this weight
                sos(i + 1, weight, total);
            }
        }
    };
    
    int main()
    {   
        Solution solution({ 2, 10, 13, 17, 22, 42 });
        solution.sos(0, 0, 52);
        //prints:    10 42
        //           13 17 22
    }