我认为C#没有足够的浮点精度和范围来单纯地处理这个。
如果你确实想要走这条路,你可以注意到共轭
![\Phi=\phi^{-1}=\phi-1=\frac{-1+\sqrt 5}{2}](https://chart.apis.google.com/chart?cht=tx&chl=%5cPhi=%5cphi%5e%7b-1%7d=%5cphi-1=%5Cfrac%7B-1%2b%5Csqrt%205%7D%7B2%7D)
小于1,因此
![-\frac{(-\Phi)^n}{\sqrt 5}](https://chart.apis.google.com/chart?cht=tx&chl=-%5cfrac%7b%28-%5cPhi%29%5en%7d%7b%5csqrt%205%7d)
与四舍五入到最近的整数相同,因此你可以简化你的解决方案,找到
![\left\lfloor\frac{\phi^n}{\sqrt 5}+\frac12\right\rfloor](https://chart.apis.google.com/chart?cht=tx&chl=%5cleft%5clfloor%5cfrac%7b%5cphi%5en%7d%7b%5csqrt%205%7d%2b%5cfrac12%5cright%5crfloor)
。然后使用二项式展开,这样你只需要计算
![\left\lfloor a+b\sqrt 5\right\rfloor](https://chart.apis.google.com/chart?cht=tx&chl=%5cleft%5clfloor%20a%2bb%5csqrt%205%5cright%5crfloor)
,使用适当的
a和
b(它们是有理数,并且可以通过BigInteger精确计算)。如果你仍然回到Double,你仍然不会比1475更进一步,但你应该能够弄清楚如何仅使用精确的整数运算来完成这部分☺。
![\frac{\phi^n}{\sqrt 5}=\frac{(1+\sqrt 5)^n}{2^n\sqrt 5}=\frac{\sum_{k=0}^n{n\choose k}\sqrt 5^k}{2^n\sqrt 5}](https://chart.apis.google.com/chart?cht=tx&chl=%5cfrac%7b%5cphi%5en%7d%7b%5csqrt%205%7d=%5cfrac%7b%281%2b%5csqrt%205%29%5en%7d%7b2%5en%5csqrt%205%7d=%5cfrac%7b%5csum_%7bk=0%7d%5en%7bn%5cchoose%20k%7d%5csqrt%205%5ek%7d%7b2%5en%5csqrt%205%7d)
还有一种计算斐波那契数列的巧妙方法,使用矩阵乘法:
![\left(\begin{matrix}1&1\1&0\end{matrix}\right)^n=\left(\begin{matrix}F_n&F_{n-1}\F_{n-1}&F_{n-2}\end{matrix}\right)](https://chart.apis.google.com/chart?cht=tx&chl=%5cleft%28%5cbegin%7bmatrix%7d1%261%5c%5c1%260%5cend%7bmatrix%7d%5cright%29%5en=%5cleft%28%5cbegin%7bmatrix%7dF_n%26F_%7bn-1%7d%5c%5cF_%7bn-1%7d%26F_%7bn-2%7d%5cend%7bmatrix%7d%5cright%29)
如果你足够聪明,这可以在O(log n)的时间内完成。
我最终使用Haskell实现了这些代码。 fib1
是矩阵幂的算法,而fib2
则是上面描述的闭式公式的确切整数转换。当使用GHC 7.0.3编译时,它们各自的运行时间如下,由Criterion测量:
import Control.Arrow
import Data.List
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)
fib1 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 % max 3 l) (b * b / 5) + 1 % 2