3 ms·
http://stackoverflow.com/questions/4327846/calculating-fibonacci/4328194#4328194 http://stackoverflow.com/questions/4327846/calculating-fibon... It's straightf
by ephemient 15y ago
http://stackoverflow.com/questions/4327846/calculating-fibonacci/4328194#4328194 http://stackoverflow.com/questions/4327846/calculating-fibon...
It's straightforward (if messy) to calculate two rationals exactly, then approximate √5 to within certain error bounds.
I wrote some code which does this, just as an experiment... and it's unbearably slow. Just try it.
import Control.Arrow
import Data.Ratio
newtype Matrix2 a = Matrix2 (a, a, a, a) deriving (Show, Eq)
instance (Num a) => Num (Matrix2 a) where
Matrix2 (a, b, c, d) * Matrix2 (e, f, g, h) =
Matrix2 (a*e+b*g, a*f+b*h, c*e+d*g, c*f+d*h)
fromInteger x = let y = fromInteger x in Matrix2 (y, 0, 0, y)
fib n = let Matrix2 (_, x, _, _) = Matrix2 (1, 1, 1, 0) ^ n in x
binom n =
scanl (\a (b, c)-> a*b `div` c) 1 $
takeWhile ((/=) 0 . fst) $ iterate (pred *** succ) (n, 1)
evens (x:_:xs) = x : evens xs
evens xs = xs
odds (_:x:xs) = x : odds xs
odds _ = []
iterate' f x = x : (iterate f $! f x)
powers b = iterate (b *) 1
esqrt e n = x where
(_, x):_ = dropWhile ((<=) e . abs . uncurry (-)) $ zip trials (tail trials)
trials = iterate (\x -> (x + n / x) / 2) n
fib' n = (a, b) where
d = 2 ^ n
a = sum (zipWith (*) (odds $ binom n) (powers 5)) % d
b = sum (zipWith (*) (evens $ binom n) (powers 5)) % d
fib2 n = numerator r `div` denominator r where
(a, b) = fib' n
l = lcm (denominator a) (denominator a)
r = a + esqrt (1 % l) (b * b / 5) + 1 % 2
- vog 15y agoI think the biggest issue in this code in the naive implementation of "powers". It runs in O(b) while it should take at most O(log(b)). Also, the multiplication might be a bottleneck, there has been a lot of research in this area. (the original article mentions some) It might be a good idea to use an arbitrary precision library like GMP, which provides super-fast multiplication and thus power() algorithms. (It also has an implementation of Fib() which outperforms both approaches here, but that's not my point.)
- ephemient 15y agoI have not profiled the code, but I expect that the Newtonian approximation of sqrt is the slowest part. It would surprise me if "powers" was a big problem relative to the rest. And no shortcuts: each of [1, b b², b³, b⁴, b⁵, …] (up to n) is used in the binomial expansion.
- ephemient 15y agoHmm, I screwed up and wrote iterate instead of iterate' where I intended. Not that it really makes a difference. In any case, the average time to print the first 20 Fibonacci numbers is 0.4157ms with matrix exponentiation and 1.707063s with exact integer math (yes, the units are different) on a T400.