1

I am trying to optimize this function that I found online.

Example input: [1, 2, 3]
Example output: [[1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]

Here's the code:

const combinations = arr => {
  let parentValue = [];
  let cursor = 0;

  for (const parentThis of arr) {
    const value = [[parentThis]];
    for (const thiss of parentValue) {
      value.push(thiss.concat([parentThis]));
    }
    parentValue = parentValue.concat(value);
  }
  return parentValue;
};

(the variable names are weird because I'm running this as a MongoDB aggregation)

Running this 10k times on an array of 10 elements takes about 23 seconds. How can I make it run faster? I am open to some tradeoffs.

An obvious improvement I found is to decrease the amount of elements. Running it 10k times on an array of 9 elements takes 9 seconds.

I suspect another improvement would be to reject some outputs before they are generated (single-element combinations and the all-elements combination might not be too useful), but I can't figure out how to put this into code.

I tried to improve by removing the first iteration (supplying [arr[0]] as the initialValue and starting the outer for loop from the second element) but it doesn't seem to make a significant difference.

1
  • 1
    Please visit help center, take tour to see what and How to Ask. This question possibly belongs at codereview Commented May 7, 2021 at 19:26

2 Answers 2

2

About 1 second for 10k times on an array of 10 elements.

function powerset(A){
  const n = A.length;
  const numSets = (1 << n) - 1;
  const result = new Array(numSets);

  for (let k=1; k<=numSets; k++){
    const set = [];
    result[k - 1] = set;
    let temp = k;
    let i = 0;
    while (temp){
      if (temp & 1)
        set.push(A[i]);
      temp >>= 1;
      i += 1;
    }
  }
  
  return result;
}


var A = [1,2,3,4,5,6,7,8,9,10];
var start = new Date;
for (let i=0; i<10000; i++)
  var t = powerset(A);
console.log((new Date - start) / 1000);

As noted by georg in the comments under this answer, the line, const result = new Array(numSets); can be replaced with const result = []; and the assignment to result.push(set); in order to possibly see further improvement in efficiency. (Tested on node with array size 15-20.)

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

9 Comments

@BuğraGündüz it uses iteration rather than recursion, and sets up the result array in advance rather than pushing to it, although I'm not sure the latter matters that much.
you're the best.
v8 doesn't like preallocations, so result = [] and result.push will perform better.
@georg it doesn't seem to in whatever runs the snippet in this answer. Just tested. I welcome demonstrations showing otherwise
@georg I added a note about this in the answer.
|
0

Maybe a simple push could speed the code a bit.

// parentValue = parentValue.concat(value);
parentValue.push(...value);

Comments

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.