Return all subsets whose sum is a given value (subset sum problem)

The subset sum problem is the problem to create an algorithm that takes an array and sum and returns all subsets of the argument array whose sum is equal to the given sum. I am attempting to solve a variety of the subset sum problem (using JavaScript) in which only non-negative integers are allowed and where all the subsets whose sum is equal to the passed in sum are returned.

I have followed a dynamic programming approach to solving the problem and have created a function that returns a two-dimensional boolean array that shows which subsets of the argument array that sum to the argument some. Each row represents a number in the argument array (the first row represents the first element, the second row represents the second element, etc.); and each column represents a value of sum (the leftmost column represents sum = 0 and the rightmost column represents sum = “argument sum”). If the element subsetArray[r][c] == true, then the subset {array[0], array[1], ..., array[r]} has elements that sum to “c”.

const subsetSum = (array, sum) => {
   // Initialize empty 2D boolean array.
   let subsetArray = new Array(array.length)
      .fill(false)
      .map(() => new Array(sum + 1).fill(false));

   for (let e = 0; e < array.length; e++) {
      for (let s = 0; s <= sum; s++) {
         if (s === 0 || s === array[e]) {
            subsetArray[e][s] = true;
            continue;
         }
         if (e > 0) {
            if (
               subsetArray[e - 1][s] === true ||
               subsetArray[e - 1][s - array[e]] === true
            ) {
               subsetArray[e][s] = true;
            }
         }
      }
   }
   return subsetArray;
};

Remark: This function does not solve the problem. It only provides the subsets that are guaranteed to include subsets whose sum is equal to the specified sum.

My problem is that I need to extract the actual subsets whose sum is equal to the specified sum from this boolean array. I have tried to do this, but the logic in my approach (which involves using the boolean array to reduce the number of subsets that I have to analyze) becomes rather complicated and I am having difficulty implementing it.

Does anyone know a good way of finding all subsets whose sum is equal to the given sum when one has the boolean array generated by subsetSum?

Edit 1

Input

Sum: 17
Array 7 2 1 5 1 20 7

Output

7 7 2 1

Answer

You could take an iterative and recursive approach for finding subset sums.

This approach returns two sets, because of the double ones.

It works by taking values form the array or not.

Int he head of the function various checks are made, the first checks if the sum is reached by all element in the temporary array t. If the wanted sum is reached, the temporary array is pushed to the result set.

If the index has the value of the length of the array, the recusrion stops.

The last check looks ahead and if the sum is smaller or equal to the target sum, the the item at the actual index i is taken for the next recursion.

The last call of fork omits the actual item and goes on with the next index.

tl;dr

Take either an element, build the sum or omit the element. Then go on with the next index.

An example of taken numbers:

7
7 2
7 2 1
7 2 1 5
7 2 1 5 1
7 2 1 5 1
7 2 1 5 1
7 2 1 5
7 2 1 5
7 2 1 5
7 2 1
7 2 1 1
7 2 1 1
7 2 1 1
7 2 1
7 2 1
7 2 1 7
7 2 1
7 2
7 2 5
7 2 5 1
7 2 5 1
7 2 5 1
7 2 5
7 2 5
7 2 5
7 2
7 2 1
7 2 1
7 2 1 7
7 2 1
7 2
7 2
7 2 7
7 2
7
7 1
7 1 5
7 1 5 1
7 1 5 1
7 1 5 1
7 1 5
7 1 5
7 1 5
7 1
7 1 1
7 1 1
7 1 1 7
7 1 1
7 1
7 1
7 1 7
7 1
7
7 5
7 5 1
7 5 1
7 5 1
7 5
7 5
7 5
7
7 1
7 1
7 1 7
7 1
7
7
7 7
7

2
2 1
2 1 5
2 1 5 1
2 1 5 1
2 1 5 1 7
2 1 5 1
2 1 5
2 1 5
2 1 5 7
2 1 5
2 1
2 1 1
2 1 1
2 1 1 7
2 1 1
2 1
2 1
2 1 7
2 1
2
2 5
2 5 1
2 5 1
2 5 1 7
2 5 1
2 5
2 5
2 5 7
2 5
2
2 1
2 1
2 1 7
2 1
2
2
2 7
2

1
1 5
1 5 1
1 5 1
1 5 1 7
1 5 1
1 5
1 5
1 5 7
1 5
1
1 1
1 1
1 1 7
1 1
1
1
1 7
1

5
5 1
5 1
5 1 7
5 1
5
5
5 7
5

1
1
1 7
1


7

function getSubsets(array, sum) {

    function fork(i = 0, s = 0, t = []) {
        if (s === sum) {
            result.push(t);
            return;
        }
        if (i === array.length) {
            return;
        }
        if (s + array[i] <= sum) { // shout circuit for positive numbers only
            fork(i + 1, s + array[i], t.concat(array[i]));
        }
        fork(i + 1, s, t);
    }

    var result = [];
    fork();
    return result;
}

console.log(getSubsets([7, 2, 1, 5, 1, 20, 7], 17));
.as-console-wrapper { max-height: 100% !important; top: 0; }

If you like, you could use a generator function with one more parameter for the temporary result set.

function* subsets(values, sum, parts = []) {
    var i, s;

    for (i = 0; i < values.length; i++) {
        s = sum - values[i];
        if (!s) {
            yield [...parts, values[i]];
        } else if (s > 0) {
            yield* subsets(values.slice(i + 1), s, [...parts, values[i]]);
        }
    }
}

console.log([...subsets([7, 2, 1, 5, 1, 20, 7], 17)]);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Leave a Reply

Your email address will not be published. Required fields are marked *