在Haskell中生成Fibonacci数?

时间:2009-07-09 18:41:14

标签: haskell fibonacci

在Haskell中,如何基于第n个Fibonacci数等于第(n-2)个Fibonacci数加上第(n-1)个Fibonacci数的属性生成Fibonacci数?

我见过这个:

fibs :: [Integer]
fibs = 1 : 1 : zipWith (+) fibs (tail fibs)

我真的不明白,或者它是如何产生无限列表而不是包含3个元素的列表。

我如何通过计算实际定义编写haskell代码,而不是通过列表函数做一些非常奇怪的事情?

10 个答案:

答案 0 :(得分:80)

这是一个计算第n个Fibonacci数的另一个更简单的函数:

fib :: Integer -> Integer
fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)

您所指的实现是关于Fibonacci中的值如何相互关联的一些观察的继承(以及Haskell如何根据自身定义数据结构实际上创建无限数据结构)

你问题中的函数是这样的:

假设您已经拥有无限的Fibonacci数字列表:

   [ 1, 1, 2, 3, 5,  8, 13, .... ]

此列表的tail

   [ 1, 2, 3, 5, 8, 13, 21, .... ]

zipWith使用给定的运算符逐个元素地组合两个列表:

   [ 1, 1, 2, 3,  5,  8, 13, .... ]
+  [ 1, 2, 3, 5,  8, 13, 21, .... ]
=  [ 2, 3, 5, 8, 13, 21, 34, .... ]

因此可以通过将元素11添加到使用Fibonacci数的无限列表的尾部压缩无限的Fibonacci数列表的结果来计算Fibonacci数的无限列表。 +运营商。

现在,要获得第n个Fibonacci数,只需获得Fibonacci数无限列表的第n个元素:

fib n = fibs !! n

Haskell的优点在于它不需要计算Fibonacci数列表中的任何元素。

我的头爆了吗? :)

答案 1 :(得分:22)

按照定义,斐波纳契系列的每个项目都是前两个项的总和。将这个定义放入lazy haskell给你这个!

fibo a b = a:fibo b (a+b)

现在只需从0开始从fibo中取n个项目

take 10 (fibo 0 1)

答案 2 :(得分:19)

扩展dtb的答案:

“简单”解决方案之间存在重要差异:

fib 0 = 1
fib 1 = 1
fib n = fib (n-1) + fib (n-2)

你指定的那个:

fibs = 1 : 1 : zipWith (+) fibs (tail fibs)

简单的解决方案需要O(1.618NN)时间来计算第N个元素,而你指定的那个元素需要O(N 2 )。那是因为您指定的那个考虑到计算fib nfib (n-1)(计算它需要)共享fib (n-2)的依赖关系,并且可以为两者计算一次省时间。 O(N 2 )用于N个O(N)数字的加法。

答案 3 :(得分:5)

Fibonacci序列here有许多不同的Haskell算法。 “天真”的实现看起来就像你所追求的那样。

答案 4 :(得分:1)

unfoldr可以很容易地实现产生无限斐波那契数列的懒惰方式,如下所示;

fibs :: [Integer]
fibs = unfoldr (\(f,s) -> Just (f,(s,f+s))) (0,1)

答案 5 :(得分:1)

斐波那契(n)的定义是:

fibonacci (n) = fibonacci (n-1) + fibonacci (n-2)

Haskell中的天真实现

fibonacci :: Integer -> Integer
fibonacci 0 = 1
fibonacci 1 = 1
fibonacci x = fibonacci (x-1) + fibonacci (x-2)

所有公式都可以追溯到这个定义,有些运行速度非常快,其中一些运行速度非常慢。上面的实现有O(n)= 2 ^ n

根据你的问题的精神,让我删除列表的使用,并给你一些在O(n)运行的东西。让我们不要将所有的斐波那维从0到n保持在列表中。

如果我们有三个(一个有三个成员的元组)看起来像:

(n, fibonacci[n-1], fibonacci[n])

记住初始定义,我们可以计算出上一个三联的下一个三元

(n+1, fibonacci[n], fibonacci[n-1] + fibonacci[n]) = (n+1, fibonacci[n], fibonacci[n+1])

上一个三联赛的下一个三联赛: (n+2, fibonacci[n+1], fibonacci[n] + fibonacci[n+1]) = (n+1, fibonacci[n+1], fibonacci[n+2])

依此类推 ......

n = 0 => (0,0,1) 
n = 1 => (1,1,1) - calculated from the previous triple
n = 2 => (2,1,2) - calculated from the previous triple
n = 3 => (3,2,3) - calculated from the previous triple
n = 4 => (4,3,5) - calculated from the previous triple
n = 5 => (5,5,8) - calculated from the previous triple

让我们在Haskell中实现它并使用自解释变量名称:

nextTripleIfCurrentNIsLessThanN :: (Int, Integer, Integer) -> Int -> (Int, Integer, Integer)
nextTripleIfCurrentNIsLessThanN (currentN, x, y) n = if currentN < n
then nextTripleIfCurrentNIsLessThanN (currentN + 1, y, x + y) n
else (currentN, x, y)

thirdElementOfTriple :: (x,y,z) -> z
thirdElementOfTriple (x,y,z) = z

fibonacci :: Int -> Integer
fibonacci n = thirdElementOfTriple (nextTripleIfCurrentNIsLessThanN (0,0,1) n)

这将在O(n)中起作用[它是温和的二次曲线,大量出现。原因是添加大数字比添加小数字更昂贵。但这是关于计算模型的单独讨论。]

fibonacci 0
1
fibonacci 1
1
fibonacci 2
2
fibonacci 3
3
fibonacci 4
5
fibonacci 5
8
fibonacci 5000
6276302800488957086035253108349684055478528702736457439025824448927937256811663264475883711527806250329984690249846819800648580083040107584710332687596562185073640422286799239932615797105974710857095487342820351307477141875012176874307156016229965832589137779724973854362777629878229505500260477136108363709090010421536915488632339240756987974122598603591920306874926755600361865354330444681915154695741851960071089944015319300128574107662757054790648152751366475529121877212785489665101733755898580317984402963873738187000120737824193162011399200547424034440836239726275765901190914513013217132050988064832024783370583789324109052449717186857327239783000020791777804503930439875068662687670678802914269784817022567088069496231111407908953313902398529655056082228598715882365779469902465675715699187225655878240668599547496218159297881601061923195562143932693324644219266564617042934227893371179832389642895285401263875342640468017378925921483580111278055044254198382265567395946431803304304326865077742925818757370691726168228648841319231470626

答案 6 :(得分:0)

LOL,我喜欢Haskell模式匹配,但它在标准的Fibonacci函数中变得无用。标准清单由右侧构成。要使用模式匹配和缺点,必须从左侧构建列表。嗯,至少,一个安慰是,这真的很快。 ~O(n),它应该是。需要辅助函数来反转无限列表(你只能在Haskell中做的事情,欢乐),这个函数输出每个后续的运行列表,所以'last'也用在辅助函数管道中。

f (x:y:xs) = (x+y):(x:(y:xs))

助手

fib n = reverse . last . take n $ iterate f [1,0]

这是一个列表版本,我认为,它阐明了如何构建列表的目的。我想做一个元组版本。

编辑3/15/2018

首先,Will Ness启发了我的知识,即每次迭代生成的整个列表是不必要的,只需要使用最后两个值,结果列表的值是每个列表的第一个值或者对生成。太有趣了。 Will告诉我列表的值是列表的第一个值,我运行它并看到每个列表的每个头的值为0,1,1,2,3,5,8,13,我说WTF,我的PC上的代码会改变吗?价值观在那里,但如何!?过了一会儿,我意识到他们一直都在那里,但我只是没有看到他们。啊。 Will的函数和辅助函数的版本是:

f = (\(x:y:xs) -> (x+y):x:xs) -- notice, no y: put back only x+y & x

和他的辅助函数重写

fib n = map head . take n $iterate f [0,1]

我认为,他们现在可以合并:

fib n = take n . map head $ iterate (\(x:y:xs) -> (x+y):x:xs) [0,1]

作为一个无关紧要的东西,该函数也可以与元组一起

fib n = take n . map fst $ iterate (\(a,b) -> (b,a+b)) (0,1)

另一种形式,列表理解形式,也可以为所有人编写:

fib n = take n [ fst t | t <- iterate (\(a,b) -> (b,a+b)) (0,1)]

这些都是迭代和健壮的。最快的是用于fib 5000的12.23秒列表的地图。对于fib 5000,在13.58秒时,元组理解度是第二快的。

答案 7 :(得分:0)

输入代码,您的定义是

fib :: Int -> Integer
fib 0 = 1
fib 1 = 1
fib n = fib (n-1) + fib (n-2)
  -- i.e.
  -- fib (n+2) = fib (n+1) + fib n

Int -> a ~= [a]因为

from f = map f [0..]     -- from :: (Int -> a) -> [a]
to = (!!)                -- to :: [a] -> (Int -> a)

因此

fibs :: [Integer]
fibs = from fib 

fibs !! 0 = 1
fibs !! 1 = 1
fibs !! (n+2)    = fibs !! (n+1)     +  fibs !! n
-- or,
drop 2 fibs !! n = drop 1 fibs !! n  +  fibs !! n
                 = zipWith (+) (tail fibs) fibs !! n
-- i.e.
take 2 fibs = [1,1]
drop 2 fibs = zipWith (+) (tail fibs) fibs
-- hence, 
fibs = take 2 fibs ++ drop 2 fibs
     = 1 : 1 : zipWith (+) (tail fibs) fibs

或者,如a, b = (0,1) : (b, a+b)

fibs :: [Integer]
fibs = a
  where
  (a,b) = unzip $ (0,1) : zip b (zipWith (+) a b)

答案 8 :(得分:0)

我正在做CIS194的作业6,发现您可以用这种方式编写。 计算前n个元素仅需要O(n)个加法运算。

fibs2 :: [Integer]
fibs2 = [0, 1] ++ [fibs2 !! (n-1) + fibs2 !! (n-2) | n <- [2..]]

答案 9 :(得分:-1)

使用迭代

fibonaci = map fst (iterate f (0,1)) where f (x,y) = (y,x+y)

使用

take 10 fibonaci

[0,1,1,2,3,5,8,13,21,34,55,89,144,233,377]