Problem 2: Even Fibonacci numbers

Problem 2

Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:

            1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...

By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.

解答

斐波那契数列中的每一项被定义为前两项之和。要求找出该数列中值为不超过 4 百万的偶数的项之和。

这道题稍微有点难度。这里使用 Haskell:

Prelude> sum $ takeWhile (<=4000000) $ filter even $ map fst $ iterate (\(a,b)->(b,a+b)) (1,2)

这个程序只有一行,我们从这行最右边开始分析:

  • (1, 2) 这个元组表示斐波那契数列的头两项。

  • 其左边的 lambda 表达式将元组 (a, b) 变为 (b, a+b) 。

  • iterate 函数使用其右边的两个参数,生成一个无限列表:[ (1,2), (2,3), (3,5), (5,8), (8,13), ...] 。不要担心无限列表会耗尽内存,Haskell 是惰性求值的,只会生成你所需要的项。

  • map fst 函数取出列表中元组的第一个元素,生成一个新的列表:[ 1, 2, 3, 5, 8, ... ] 。

  • filter even 函数过滤列表中的元素,只留下值为偶数的项:[ 2, 8, ... ] 。

  • takeWhile (<=4000000) 函数也用来过滤列表,停止于列表中最后一个不大于 4 百万的元素。所以在这里我们得到了一个有限的列表:[ 2,8,...,3524578 ],该列表共计 11 项。

  • sum 函数将列表中的所有元素求和。如果把这里的 sum 函数改为 last 函数,就变成取列表最后一项。如果把 sum $ 去掉,就得到整个列表了。

  • 这里所有的 $ 都是函数,用在这里是为了减少代码中括号的数目,你可以理解为分隔符。

陷阱

千万不要写出以下程序:

main = print $ sum $ takeWhile (<=4000000) $ filter even $ map fib [1..]

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

这个程序的算法是正确的:

  • 第 3 行到第 5 行的 fib 函数就是用来计算斐波那契数列的第 n 项的。这是经典的递归算法。

  • 第 1 行最右边的 [1..] 生成一个无限列表:[ 1, 2, 3, 4, 5, ... ] 。

  • map fib 函数生成斐波那契数列:[ 1, 2, 3, 5, 8, ... ] 。

  • 剩下的事情就和第一个程序一样了。

但这个程序在我的PC上要运行 16.588 秒(使用 Unix 的 time 命令计时)。第一个程序仅需 0.003 秒。而且这差别还不是线性的,随着要计算的项数的增加,效率相差越来越大。直接使用递归计算斐波那契数列是算法教科书上著名的反例。

改进

虽然第一个程序已经非常快了,但还可以稍微改进一下:

Prelude> sum $ takeWhile (<=4000000) $ map fst $ iterate (\(a,b)->(b,a+4*b)) (2,8)

这个 Haskell 程序和第一个程序的差别如下:

  • 作为起始条件的元组 (1,2) 被修改为 (2,8) 了。

  • lambda 表达式中对元组的第二项乘了四倍。

  • 不再过滤掉列表中值为奇数的项了。

这个程序的运行结果和前面两个程序是一样的。为什么呢?我们来观察斐波那契数列(不要说这个数列和题目中的不一样,斐波那契数列的头两项是什么数学家们就没有取得一致过(。不过这不影响我们的答案,因为 1 不是偶数。):

1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...

可以看到,每三项就有一项是偶数。这样,就每三项取一个数:

2, 8, 34, 144, ...

可以证明,这个新数列的每一项(从第三项起)是前一项的四倍再加上再前一项。这样,我们就得到了改进的程序。

详细的论述请参见:002_overview.pdf 。

posted @ 2020-11-25 06:39  璃奈ちゃんボード  阅读(71)  评论(0编辑  收藏  举报