有没有更好的方法来使用有理数来实现斐波那契公式?

Is there a better way to implement the fibonacci formula using rational numbers?

我正在看Learn you some Haskell for the Greater Good这本书,因为我在Haskell中玩递归我实现了斐波那契函数,递归版本很简单,可能可以改进:

-- recursive fibonacci numbers
rfib :: Int -> Int
rfib 0 = 0
rfib 1 = 1
rfib n =   rfib (n-1) + rfib(n-2)

当我在谷歌上搜索以了解更多信息时,我偶然发现了这篇文章: http://java.dzone.com/articles/what-fibonacci-taught-me-about

作者展示斐波那契公式:

我决定在 Haskell 中使用有理数来实现它以避免浮点不精确。我的实现如下所示:

fibMultiplier   = (toRational 1) / (toRational (sqrt 5))
firstFibTerm  n = (((toRational 1)  + (toRational (sqrt 5))) / toRational 2) ^ n
secondFibTerm n = (((toRational 1)  - (toRational (sqrt 5))) / toRational 2) ^ n

fib :: Int -> Int
fib n = truncate (fromRational (fibMultiplier * firstFibTerm n) - (fibMultiplier * secondFibTerm n))

作为初学者,我相信上面的代码可以大大改进,你能指出我可以改进的地方或我犯的一些错误吗?

感谢您的帮助。

更新

因此,在尝试了这些建议之后,我发现使用 Data.Real.Constructible 既快速又精确,没有舍入错误。我的最终实现是:

fib :: Int -> Construct
fib n = ( ( (1 / (sqrt 5)) * ( (( 1 + (sqrt 5) ) / 2) ^ n ) ) -
          ( (1 / (sqrt 5)) * ( (( 1 - (sqrt 5) ) / 2) ^ n ) ) )::Construct

我还实现了一个函数,returns n 个斐波那契数的列表:

fibList :: Int -> [Construct]
fibList n = [fib(x) | x <- [0..n]]

使用这个函数我们可以比较不同实现的结果:

-- recursive fibonacci numbers
rfib :: Int -> Int
rfib 0 = 0
rfib 1 = 1
rfib n =   rfib (n-1) + rfib(n-2)

-- recursive fibonacci sequence
rfibList :: Int -> [Int]
rfibList n = [rfib(x) | x <- [0..n]]
-- rfibList 20 returns: [0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765]


-----------------------------------


-- fibonacci number using Double and truncate
doubleFib :: Integer -> Integer
doubleFib n = truncate ( ( (1 / (sqrt 5)) * ( (( 1 + (sqrt 5) ) / 2) ^ n ) ) -
                         ( (1 / (sqrt 5)) * ( (( 1 - (sqrt 5) ) / 2) ^ n ) ) )

-- fibonacci list using Double and truncate
doubleFibList :: Integer -> [Integer]
doubleFibList n = [doubleFib(x) | x <- [0..n]]
-- doubleFibList 20 returns: [0,1,0,2,3,5,8,13,21,34,55,89,143,232,377,610,986,1597,2584,4181,6764]


-----------------------------------


-- fibonacci number using Construct
constructFib :: Int -> Construct
constructFib n = ( ( (1 / (sqrt 5)) * ( (( 1 + (sqrt 5) ) / 2) ^ n ) ) -
                   ( (1 / (sqrt 5)) * ( (( 1 - (sqrt 5) ) / 2) ^ n ) ) )::Construct

-- fibonacci list using construct
constructFibList :: Int -> [Construct]
constructFibList n = [constructFib(x) | x <- [0..n]]
-- constructFibList 20 returns: [0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765]

请注意,我们在 doubleFibList 上出现舍入错误,第 16 个数字应该是 987,但我们得到 986。递归实现很慢,Double 实现不精确,但是使用 Construct 我们可以得到一个快速而精确的斐波那契数列,比我以前使用 toRational 的实现要好得多。

(您不能使用 sqrt 版本,请改用 Data.Real.Constructible

import Data.Real.Constructible 

fib :: Int -> Construct 
fib n = (((1+sqrt(5))/2)^n - ((1-sqrt(5))/2)^n)/sqrt(5)