// Recursive
function fib(n){
if (n < 2) return n;
return fib(n-1) + fib(n-2);
}
// Memoized
function memFib(n){
return memFib[n] || (memFib[n] = fib(n));
}
// Mathematical
function mathFib(n){
return Math.floor(Math.pow(Phi, n)/Math.sqrt(5) +.5);
}
How do they compare in speed?
[fib, memFib, mathFib].map(function(fn){
var t0 = +new Date
fn(30);
return +new Date - t0
});
Don't get your panties in a bunch just yet. This is an unfair fight:
fibis simply horrible, performance-wise. It's the easiest to understand and best for demonstrating recursive functions, but that's it.
memFibis a great example for memoization, as it executes almost linearly, except for those numbers which have not been used yet. If you try runningmemFibwithn=40, you will start feeling the pain, but only the first time.
An important point to make here about memoization is, if your function will be called many times and always returns the same result given the same arguments, memoizing is your friend.
// Y-combinator
var Yfib = Y(function(fn){ return (function(n){
if (n < 2) return n;
return fn(n-1) + fn(n-2);
}); });
// Memoized Y-combinator
var YmemFib = Ymem(function(fn){ return (function(n){
if (n < 2) return n;
return fn(n-1) + fn(n-2);
}); });
This is a whole different game, with n=1000:
[mathFib, YmemFib].map(function(fn){
var t0 = +new Date
fn(1000);
return +new Date - t0
});
Firefox 3.6.10 cries somewhere between n=1000 and n=2000 about too much recursion, and the difference is less than 10 milliseconds in favor of the mathematical formula.Chrome 6.0.472.63 can handle at least
n=6000 before exhausting its stack and has a difference of about 2 milliseconds. BOOM.
The Much Anticipated Morale Of The Leonardo Of Pisa Thingamajigs
- Go for the mathematical solution first. If you are not smart enough, ask Google. If Google is not smart enough, ask Tyler. Seriously.
- Memoize. Remember this. Get it?
- Use a Y-combinator. You don't have to understand how to use it, and good luck trying. Seriously. Here you go:
function Y(fn) { return function(x) { return (fn(function(n){ return (Y(fn))(n); }))(x); }; }To help you understand, try: matt.might.net, fixed point combinator on Wikipedia, Electronics and Computing Systems at the University of Cincinnati, the book To Mock a Mockingbird, and/or some entheogens.
/**
* Memoizing Y-combinator (aka triptonomikon).
*/
function Ymem(fn) {
var mem = this;
return function(x) {
return mem[x] || (mem[x] = (fn(function(n){
return (Ymem.call(mem, fn))(n);
}))(x));
};
}
This should keep you busy until the next post.