Hacker News new | past | comments | ask | show | jobs | submit login

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



I 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.)


I 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.


Hmm, 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.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: