美文网首页
斐波那契数列

斐波那契数列

作者: Wu杰语 | 来源:发表于2019-08-04 23:07 被阅读0次

    序言

    在网易公开课《麻省理工-算法导论》的视频课程中,分治算法讲解了斐波那契数列。对于斐波那契数列,简单来看,不就是一个简简单单的计算吗,好像也没有什么深度,但是从应用和算法上开仔细琢磨,还是有很多有意思的地方。

    斐波那契作为模型

    斐波那契最重要的当然是应用,作为一些应用的模型。最常见的是动态规划中的应用,例如最经典的上楼梯的例子,有N阶楼梯,一个小朋友上楼,他只能一次走一阶或者走两阶,问有多少种不同的走法。

    对于这个题目,是可以比较自然的联想到用动态规划的思路的,从最后一阶考虑,到达最后一阶可以有走一步到达或者走两步到达两种方式,所以f(n) = f(n-1) + f(n-2)。很明显,这就是一个斐波那契数列,得到了动态规划公式,按照动态规划的思路继续完成编码。

    斐波那契的计算

    递归

    先用代码描述:

    def fib(n):
      if n <= 1: return 1
      return fib(n-1) + fib(n-2)
    

    这个算法通过递归树分析,其计算复杂度T(n) = O(2^n)

    按照这个思路继续,怎么提升效率,因为递归树展开后有大量的重复计算,所以考虑进行缓存,缓存后其时间复杂度T(n) = O(n),而空间复杂度由O(1)上升到O(n)

    from functools import lru_cache
    @lru_cache(maxsize=1000)
    def fib(n):
      if n <= 1: return n
      return fib(n-1) + fib(n-2)
    

    这里直接使用lru_cache缓存,但是这里maxsize需要设置的和n一样大才能达到O(n)的复杂度。

    迭代

    在SICP的第一章中,讲解了fib的计算,所有能够用递归(recursion)完成的,都可以用迭代(iteration)的方式完成。
    代码如下:

    def fib(n):
       if n <= 1: return n
       a, b = 0, 1
       for k in range(2, n):
            a, b = b, a + b 
       return b          
    

    这个算法时间复杂度是T(n) = O(n),比上述缓存的算法节省了空间复杂度O(n),肯定是优于递归的做法的。

    通项公式

    通项公式是从数学的角度来发掘的


    image.png

    这个通项公式好像挺牛逼的,从字面上看是T(n) = O(n)的时间复杂度,但是为何很少用,主要是因为这里有浮点数计算,浮点数计算CPU指令速度是小于整数加法计算的,肯定是没有迭代的方式计算的快。那么还有没有更好一点的做法呢。

    矩阵

    回到序言中,麻省理工的公开课中,讲解的就是矩阵的做法。离散数学中,矩阵是基本的工具,是集合关系的一种表示。
    斐波那契公式,通过矩阵形式表达


    image.png

    继续推导可以得到


    image.png
    通过上述推导,斐波那契数列转换成了矩阵的阶乘计算

    按照这个思路进一步的优化,阶乘有阶乘的优化,可以提前把2次方、n次方计算出来缓存起来,这样可以节省计算,获取更高更快性能。

    小结

    一个简单的斐波那契数列,演变出来真是让人惊叹,从数学的角度,有演变出通项公式和矩阵的算法,实际上递归和迭代是最基础的数学了。我们得出两个结论,一是数学是算法的基础,而是学习算法很有趣。

    相关文章

      网友评论

          本文标题:斐波那契数列

          本文链接:https://www.haomeiwen.com/subject/jqkudctx.html