1

I'd like to find a way to combine in an array all possiblities between four percentages.

Results wanted :

possibilities = [[100,0,0,0],[99,1,0,0],[99,0,1,0],...,[0,0,1,99],[0,0,0,100]]

I'm using this function but it's very slow and doesn't seem to generate all possibilities.

combinePossibilities : function(a, min, max) {
    var deferred = $q.defer();
    function toObject(arr) {
        var rv = {};
        for (var i = 0; i < arr.length; ++i){
            rv['fund'+i] = arr[i];
        }
        return rv;
     }
     var fn = function(n, src, got, all) {
       if (n === 0) {
           if (got.length > 0) {
                var total = 0;
                angular.forEach(got, function(value){
                    total += value; //GET TOTAL OF THE COMBINATION
                });
                if(total === 100){
                    all.push(toObject(got));
                }
            }
            return;
         }
         for (var j = 0; j < src.length; j++) {
             fn(n - 1, src.slice(j + 1), got.concat([src[j]]), all);
         }
         return;
     };
     var all = [];
     for (var i = min; i <= max; i++) {
        console.log(a);
        fn(i, a, [], all);
     }
     deferred.resolve(all);
     return deferred.promise;
}

I found this function here Find all possible subset combos in an array? and modified it to only take in my array results equal to 100%.

Any clue ?

Thank you.

Community
  • 1
  • 1
Matt Walterspieler
  • 2,073
  • 2
  • 21
  • 34

1 Answers1

2

This proposal is a functional recursive function, which takes sum and length. It returns an array with arrays of the combined values from the sum to zero.

function combine(sum, length, part) {
    var result = [],
        i = sum;

    part = part || [];
    if (length === 1) {
        return [part.concat(sum)];
    }
    if (length === 0) {
        return [part];
    }
    do {
        result = result.concat(combine(sum - i, length - 1, part.concat(i)));
    } while (i--);
    return result;
}

How it works:

It starts with the given sum and length and an empty result set, as well as an iterator variable i with the value of sum.

If part is not given, then an empty array is assigned.

Now follows some checks for the (leftover) length and their special treatment, if it is

  • 1: This is last iteration and the sum is left, only. Then return the partial result part concatenated by sum in an array.

  • 0: No more iteration, then return the partial result part in an array.

If the length is neither 1 nor 0, then interate over the sum to zero.

The call of combine takes the reduced sum, the decremented length and the partial result part with the value of i.

The result of the call of combine() is concatenated to the result set.

Example for combine(5, 3):

length: 21
[
    [5, 0, 0],
    [4, 1, 0],
    [4, 0, 1],
    [3, 2, 0],
    [3, 1, 1],
    [3, 0, 2],
    [2, 3, 0],
    [2, 2, 1],
    [2, 1, 2],
    [2, 0, 3],
    [1, 4, 0],
    [1, 3, 1],
    [1, 2, 2],
    [1, 1, 3],
    [1, 0, 4],
    [0, 5, 0],
    [0, 4, 1],
    [0, 3, 2],
    [0, 2, 3],
    [0, 1, 4],
    [0, 0, 5]
]

Working code with two examples:

  • combine(5, 3)
  • combine(10, 4)

function combine(sum, length, part) {
    var result = [],
        i = sum;

    part = part || [];
    if (length === 1) {
        return [part.concat(sum)];

    }
    if (length === 0) {
        return [part];
    }
    do {
        result = result.concat(combine(sum - i, length - 1, part.concat(i)));
    } while (i--);
    return result;
}

function print(array) {
    document.write('<pre>length: ' + array.length + '\n' + JSON.stringify(array, 0, 4) + '</pre>');
}

print(combine(5, 3));
print(combine(10, 4));
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392