Ike Naar said:
OK:
typedef unsigned long long number;
static number fib_aux(number a, number b, int n)
{
if (n <= 1) return a;
else return fib_aux(b, a+b, n-1);
}
number rfib(int n) { return fib_aux(1, 1, n); }
Very nice, a recursive solution that computes Fib(n) in linear time.
Here's one that should be more efficient for large n,
it computes Fib(n) in logarithmic time: [snip program]
It's based on the observation that (in matrix notation)
[ Fib(n) ] [ p q ] [ 0 ]
[ Fib(n+1) ] = [ q r ] * [ 1 ]
where
n
[ p q ] [ 0 1 ]
[ q r ] = [ 1 1 ]
There's a two-component version based on d'Ocagne's identity.
(I learned the technique years ago but just learned the name
of the identity.) That identity is:
f(2n) = f(n) * (f(n+1) + f(n-1))
The idea is that if we have the pair ( f(n-1), f(n) ), we can
calculate either ( f(2n-1), f(2n) ) or ( f(2n), f(2n+1) ),
according to the binary decomposition of the input argument.
Based on the above identity, and using a for f(n-1) b for f(n),
the formulas are:
f(2n) = 2*a*b + b*b
f(2n-1) = f(2n) - f(2n-2) = a*a + b*b
f(2n+1) = f(2n-1) + f(2n) = a*a + 2*a*b + 2*b*b
It's pretty easy to write a recursive formulation using these
identities, but a straightforward definition is kind of klunky.
What we would like to do is pass a pair of values /inward/ rather
than /outward/, and have a function that returns a single value
using tail recursion: This approach can be programmed as:
typedef unsigned long long Number;
unsigned high_bit_mask( unsigned m, unsigned n );
Number ff2( Number a, Number b, unsigned high_ones, unsigned n );
Number
fast_fibonacci( unsigned n ){
return ff2( 1, 0, high_bit_mask( -1, n ), n );
}
unsigned
high_bit_mask( unsigned m, unsigned n ){
return (m & n) == 0 ? ~m ^ ~m>>1 : high_bit_mask( m<<1, n );
}
Number
ff2( Number a, Number b, unsigned m, unsigned n ){
return m == 0 ? b
: m & n ? ff2( 2*a*b + b*b, a*a + 2*a*b + 2*b*b, m>>1, n )
: ff2( a*a + b*b, 2*a*b + b*b, m>>1, n );
}
As a side benefit this illustrates a technique for turning a
"high-to-low" recursion using even-/odd-ness into a form that
is nicely tail recursive. (Notice how the bit mask 'm' picks
off the bits of 'n' from top to bottom.)
Incidentally, the optimized generated code for this formulation
has a property I didn't expect, namely, the main loop in ff2()
is compiled as a single path with only one conditional branch
in it (and in particular not two). The compiler (gcc something
on x86-64) was smart enough to notice the common subexpressions
between the two recursive calls, and tricks out the parameter
passing using conditional move instructions.
For anyone interested: which program line arguably ought to
have a comment, and what comment would be appropriate?