Reputation: 7236
I've written a pretty standard fibonacci function through fast doubling using an algorithm derived from the matrix exponentiation algorithm which should run in O(log(n)) time and calls, but stalls out at around over 1,000,000 - even when that should be just around 25 calls.
Code:
"""
O(log(n)) time fibonacci algorithm using fast doubling derived from the matrix
squaring algorithm for the same thing.
"""
def fibonacci(num):
"O(log(n)) implementation of fibonacci using fast doubling."
if num >= 0:
return fib_helper(num)[0]
def fib_helper(num):
"Helper function for fibonacci()."
if num == 0:
return (0, 1)
elif num == 1:
return (1, 1)
else:
f_k, f_k_1 = fib_helper(num // 2)
f_k_even = f_k * (2 * f_k_1 - f_k)
f_k_odd = f_k_1 * f_k_1 + f_k * f_k
return (f_k_even, f_k_odd) if num % 2 == 0 else (f_k_odd, f_k_even +
f_k_odd)
This code should only generate log(n) calls to fib_helper and one call to fibonacci. For numbers greater than 1,000,000 it just stalls out and doesn't return.
I've tried implementing a basic decorator to to count function calls which tells me that it is only running 32 calls for 2^32, but it still stalls out at the end.
Why is this slowing to a halt on large integers?
Upvotes: 4
Views: 1319
Reputation: 70592
Just for interest, since my decimal
comments were apparently inscrutable ;--), here's the code:
import decimal
from decimal import Decimal as D
DO = D(0)
D1 = D(1)
def fibonacci(num):
from math import log10
if num >= 0:
ndigits = int(log10(1.62) * num + 100)
decimal.getcontext().prec = ndigits
decimal.getcontext().Emax = ndigits
return fib_helper(num)[0]
def fib_helper(num):
if num == 0:
return (D0, D1)
elif num == 1:
return (D1, D1)
and the rest of fib_helper()
is unchanged (see the original message). In Python 3, decimal
is implemented in C, and computation time is comparable to using native (binary) integers. But the point is the time for output conversion to a decimal string: instead of being a huge bottleneck, it becomes a trivial part of the runtime.
For example, fibonacci(100000000)
(a hundred million) takes about 30 seconds on this box, but after that:
>>> from time import clock as now # on this box, `clock()` is wall-clock time
>>> if 1:
... t = now()
... print(len(str(x)))
... print(now()-t)
...
20898764
0.10466078789343337
So a tenth of a second to produce the string of 20+ million decimal digits. For argument one billion, about 0.9 seconds to produce the 208,987,640-digit decimal string: clearly linear in the number of decimal digits.
Note: decimal
is really aiming at decimal floating- and fixed-point calculations. While it can be used fine for high precision integer calculations, you have to specify the number of digits you'll need, and the maximum exponent, in advance. It has no "unbounded" mode. The code above uses that fibonacci(n)
is approximately equal to 1.618**n
.
Upvotes: 3
Reputation: 304147
Try running you code like this
print "calculating fib(1000000)"
res = fib(1000000)
print "displaying the result..."
print res
The problem is that fib(1000000)
is a quite large number (>10200000). Your computer can operate on these numbers quite quicky because everything is done in binary.
When you try to display the number, it needs to be converted to base 10. This can be very time consuming!
Converting to hex is much easier. The bits just need to be grouped into fours, so
print hex(res)
will start spitting stuff out quite quickly
Upvotes: 9