2

I have written a function for partitioning a number:

var combinations = function (i) {
    var mem = [];
    function inner(n, r, m) {
        for (var k = m; k <= n; k++) {
            if (k == n) {
                r.push(k);
                mem[r] = 1;

                return mem;
            }
            else {
                var copy = r.slice(0);
                copy.push(k);
                inner(n - k, copy, k);
            }
        }
    }

    return inner(i, [], 1);
}

In second step I would like to add a memoization to this algorithm, but can't think of implementing it the right way, beause there is no return statement until the very end (when return is specified e.g. in faactorial or fibbinacci I can add the memoization). Can anybody drive me to the right direction?

[edit] I need this algorithm to be as fast as possible. This is a competition for a kata at codewars: link There is a requirement it must be executed under 6000ms for input up to 330. That's the best algorithm I can think of, except how to store the partial results.

3
  • What numbers do n, r, m represent? Commented Apr 20, 2016 at 19:54
  • Are you trying to compute all partitions or just the number of partitions? Commented Apr 20, 2016 at 20:43
  • @Iszk, see updated answer. :-) I added an explanation. Commented Apr 22, 2016 at 15:55

3 Answers 3

0

Here's a much simpler code that works:

function nr_partitions(n) { return p(n, n); }

function p(sum,largest) {
    if (largest == 0) { return 0; }
    if (sum == 0) { return 1; }
    if (sum < 0) { return 0; }
    return p(sum, largest-1) + p(sum-largest, largest);
}

It uses a well-known recurrence, p(n,k) = p(n,k-1) + p(n-k, k), where p(n.k) denotes the number of partitions of n where the largest part is at most k (e.g. p(3, 2)=2 because we only count 1+1+1,1+2, but not 3). For k=n we get the number of all partitions of n.

Adding memozation involves storing dictionary mapping pairs (sum, largest) to p(sum, largest).

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

Comments

0

I would go along the lines of:

var cache = {};

var combinations = function (i) {
    if ( cache[i] ){
        return cache[i];
    };

    var mem = [];
    function inner(n, r, m) {
        for (var k = m; k <= n; k++) {
            if (k == n) {
                r.push(k);
                mem[r] = 1;

                return mem;
            }
            else {
                var copy = r.slice(0);
                copy.push(k);
                inner(n - k, copy, k);
            }
        }
    }

    cache[i] = inner(i, [], 1);
    return cache[i];
}

But you'll have to modify your algorithm to make use of that cache (compute the biggest terms first ?)

Comments

0

Depending on your other requirements, you might want to consider using underscore.js which has its own _.memoize function.

The secret of memoization is that it exploits how closures work. When you define a function inside another scope, it has access to everything in that scope. When you return that function to somewhere outside the scope, it carries references to everything it can see inside the scope.

So to implement memorization, you need to make a function that returns another function, one that does the memorization check before calling the inner one.

Your code will look something like this:

/**
 * Because we'll be returning "a function that returns a function" below,
 * this needs to be executed immediately so combinations() is just
 * a standalone function.
 */
var combinations = (function(i) {

    /**
     * mem needs to stay outside the scope of your inner function.
     * If it's in a closure like this, JavaScript will keep its value
     * around as long as combinations still exists.
     */ 
    var mem = [];

    /**
     * A memoization wrapper should return a memoized function
     */
    return function(i) {

        /**
         * Check if mem[i] is set and return it if it has been
         */
        if(mem[i] !== undefined) {
            console.log('returning memoized value');
            return mem[i];    
        }

        function inner(n, r, m) {
            for (var k = m; k <= n; k++) {
                if (k == n) {
                    r.push(k);
                    mem[r] = 1;

                    return mem;
                }
                else {
                    var copy = r.slice(0);
                    copy.push(k);
                    inner(n - k, copy, k);
                }
            }
        }

        /**
         * If the value needs to be computed, we can set it at the same time
         * as we return it instead of putting it in a temporary variable.  
         */ 
        console.log('computed');
        return mem[i] = inner(i, [], 1);
    }

}()); /** <--- That's the rest of the automatic execution */

console.log(combinations(5));
console.log(combinations(5));

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.