3

The question is in the title.
As an example:

Given this array = [1,1,2,2,3,7,9,1] and sum = 7, I expect this result:

1,1,2,2,1
1,1,2,3
...
7

A quadratic time answer is trivial, I am looking for a sub-quadratic-time solution...
My current sloooow solution:

function subsetSum(numbers, target, partial) {
  var s, n, remaining;

  partial = partial || [];

  // sum partial
  s = partial.reduce(function (a, b) {
    return a + b;
  }, 0);

  // check if the partial sum is equals to target
  if (s === target) {
    console.log(partial.join(","))
  }

  if (s >= target) {
    return; // if we reach the number we don't bother to continue
  }

  for (var i = 0; i < numbers.length; i++) {
    n = numbers[i];
    remaining = numbers.slice(i + 1);
    subsetSum(remaining, target, partial.concat([n]));
  }
}

subsetSum([1,1,2,2,3,7,9,1], 7);

Output:

1,1,2,2,1
1,1,2,3
1,1,2,3
1,2,3,1
1,2,3,1
1,2,3,1
1,2,3,1
2,2,3
7

For 2k 1-digit numbers, after 4 minutes of crunching, in my box nodejs (v11.14.0) spits out:

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory

:-(

9

1 Answer 1

4

You could take a recursive call by avoiding built in array methods (iteration and most expensive slicing) and take a recursive function which get an index for the next number to get, an array of collected values and a new target, without the last taken value.

This approach returns all possible values, they are not unique, depending on the given data. This approach works for positive numbers only.

function subsetSum(numbers, target) {
    function iter(index, right, delta) {
        if (!delta) return result.push(right);
        if (index >= numbers.length) return;
        if (delta - numbers[index] >= 0)
            iter(index + 1, [...right, numbers[index]], delta - numbers[index]);
        iter(index + 1, right, delta);
    }
    var result = [];
    iter(0, [], target);
    return result;
}

subsetSum([1, 1, 2, 2, 3, 7, 9, 1], 7).map(a => console.log(...a));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Sign up to request clarification or add additional context in comments.

5 Comments

Clever solution. Though, even with only 150 one-digit numbers, Javascript heap is out of memory, on my box... :-(
have you eliminated to large values, or too much small same numbers?
I don't get you, sorry... My test numbers are: [6,7,2,5,8,0,1,3,6,3,2,8,7,9,1,5,7,5,1,2,2,3,7,9,4,1,2,2,2,2,3,7,9,4,1,2,2,3,7,9,4,1,2,2,3,7,9,4,1,2,2,3,7,9,4,1,2,2,3,7,9,4,1,2,2,3,7,9,4,1,2,2,3,7,9,4,1,2,2,3,7,9,4,1,2,2,3,7,9,4,1,2,2,3,7,9,4,1,2,2,3,7,9,4,1,2,2,3,7,9,4,1,2,2,3,7,9,4,1,2,2,3,7,9,4,1,2,2,3,7,9,4,1,2,2,3,7,9,4,1,2,2,3,7,9,4]. They are 146, so I can run the algorithm without memory errors...
you could filter all values greater than the wanted sum, and eliminates smaller values which sum is greater than the wanted sum.
Yes, thanks... That should be an improvement... But I'm looking for a FASTER and LESS MEMORY CONSUMING solution, if it exists...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.