我正在阅读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
我还实现了一个返回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我们可以得到一个快速而精确的fibonacci序列,比使用toRational的旧实现要好得多。
答案 0 :(得分:4)
(您无法使用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)