Fibonacci in O(log n): from matrices to fast doubling in Java
Why the naive Fibonacci is exponential, how a 2×2 matrix turns it logarithmic, and a fast-doubling BigInteger implementation that computes F(1 000 000) in a fraction of a second.
The Fibonacci sequence is defined by
The textbook recursive implementation is a classic example of an exponential algorithm: fib(n) calls fib(n-1) and
fib(n-2), and the call tree has roughly nodes, where .
A loop makes it linear. But we can do much better than linear, and the reason is a small piece of linear algebra.
The matrix trick
Write two consecutive Fibonacci numbers as a vector. One step of the recurrence is a matrix multiplication:
Call that matrix . Applying it times gives the identity
Proof by induction. For it’s just itself, since , and . If it holds for , then
Matrix powers can be computed by exponentiation by squaring: and . That takes matrix multiplications instead of additions.
Fast doubling
We can squeeze out more. Squaring and comparing entries with gives two identities:
So from the pair we get with just three multiplications. If is odd, one more addition gives . Walking through the bits of gives the algorithm.
import java.math.BigInteger;
public final class Fibonacci {
/** Returns {F(n), F(n+1)}. */
static BigInteger[] pair(long n) {
if (n == 0) {
return new BigInteger[] {BigInteger.ZERO, BigInteger.ONE};
}
BigInteger[] half = pair(n >>> 1);
BigInteger a = half[0]; // F(k)
BigInteger b = half[1]; // F(k+1)
BigInteger even = a.multiply(b.shiftLeft(1).subtract(a)); // F(2k)
BigInteger odd = a.multiply(a).add(b.multiply(b)); // F(2k+1)
return (n & 1) == 0
? new BigInteger[] {even, odd}
: new BigInteger[] {odd, even.add(odd)};
}
public static BigInteger fib(long n) {
return pair(n)[0];
}
}
The recursion depth is only . Even for it is about 60 frames.
How fast is it, really?
Counting multiplications is only half the story. has about decimal digits ( bits). has 208 988 digits, so the cost is dominated by multiplying huge numbers.
Here the JDK helps: since Java 8, BigInteger.multiply switches from schoolbook multiplication to Karatsuba and
then Toom–Cook 3-way as the operands grow. Both are sub-quadratic. Since Java 19 there’s also
BigInteger.parallelMultiply, which spreads the large multiplications over the common fork-join pool.
The last multiplications dominate. The final step multiplies numbers with about bits each, and each earlier level costs about half as much as the next. The total is therefore within a constant factor of a single multiplication of -bit numbers.
Why not Binet’s formula?
There is a closed form:
It’s beautiful, but with double it only works while fits in the 53-bit mantissa, up to around .
Beyond that you need arbitrary-precision , which is more work than the integer method above.
Takeaways
- Recurrences are linear maps. Any linear recurrence of order can be written as a matrix and raised to a power in steps.
- Exploit structure. Fast doubling is the matrix method with the redundant entries removed.
- Count the real cost. With big numbers, the size of the operands matters more than the number of operations.
The same idea also gives you in , which is a staple of competitive programming. Just replace
BigInteger with long arithmetic modulo .
This article lives on GitHub as plain Markdown. Spotted a typo or want to talk about it?