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:
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?
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
}