序言
这篇文章在 notion 那边放了很久了,现在拉过来这边重新复刻一遍,其实只是为了试试博客的 mathjax 没有正常工作()
记得那个时候写这篇文章的时候,大家在出面试题,这里就提到了青蛙跳格子的经典问题(就是 斐波那契数列 求 n 项的变种),所以顺手就整理了一篇文章出来。
解法
朴素递归求解方式
快速脑补法算时间复杂度 (大雾)
易得该求解方式可以构造为二叉树,但不是一个满二叉树,其运算次数约等于树上的节点,而满二叉树的节点总数为: 。
所以 时间复杂度至少为 但此解应当不是一个足够小的逼近上界,毕竟我可以胡扯他的 时间复杂度 是 ,你也不能说我是错的,毕竟结果在这个区间里面。
废话文学
话是这么说没错,但是按这个理论岂不是所有 算法 复杂度都可以表示
当然我们易证此解大小应当小于 且数量级大致相差不大,那么我们讨巧的可以构造如下方程式求解(因为时间复杂度 的求解上,我们可以近似认为下述式子是相等的):
显然为单调递减,不符合结果,所以 时间复杂度可以估算为
数学证明方式
那么如果需要精确求解时间复杂度,可不能 “大概”、“脑补”、“易得”了,我们得通过数学方法分析(如果只用高中知识的话可以凑出一个等比数列求解——类似于上面的快速脑补法)。
首先朴素递归方式可以这样计算 时间复杂度:
设 调用的时间为 那么必有:
因为 可以忽略,那么只需要求出 即可
那么这个式子本身就是一个斐波那契数列,而斐波那契数列是一个二阶常系数齐次线性递推数列:
故该齐次递推数列有如下特征方程:
代入 可求解出上述方程式的解为 :
代入通项公式:
代入后转换为大O(其实直接求级数后去掉常数项就可以了):
尾递归/迭代法
我们可以清楚的发现上面那个求解方法有很多重复项,而且编程中常见的有尾递归(就是 goto)的优化策略。而任何尾递归写法基本都可以转化为迭代法求解方法。比如可以这么写:
所以不用算:
通项公式解法
上文我们已经求解出了通项公式,那么易得:
很多材料上都会说这玩意儿是 复杂度,用屁股想想都知道不可能,你看上面有两个大大 n 次方。而且通项公式还容易出现精度丢失,出现 浮点数 的问题(当然你可以强行转换为整数 ,但是迭代情况下精度累计丢失可能使得结果不正确)
首先求指数最简单的方法是迭代求解,都不用写代码,易得 时间复杂度 是:
当然求指数最快的方法是矩阵求解方法,俗称快速幂。
比如求解 , 假定求 ,我们可以转化为二进制求法 ,直接拼成:
那么我们往上代换以下马上可以得到通项公式:
显然 n 有 个二进制位,所以假定我们知道 ,那么只需要做 次乘法以后就可以快速获得结果。
大家是不是发现问题了,显然所在位很好求,所以其实只需要求出 的关系就可以,那么关系好求么?那可太好求了,直接拿上一项平方一下就可以拿到了,注意一下是不是和之前的迭代法就比较像了?
那么这个算法的总体复杂度会是多少呢?
我们首先花了的时间求了第一项,随后花了 的时间相乘,那么总体 时间复杂度就是:
本文标题:斐波那契数列算法时间复杂度
永久链接:https://iceprosurface.com/2022/fibonacci/
作者授权:本文由 icepro 原创编译并授权刊载发布。
版权声明:本文使用「署名-非商业性使用-相同方式共享 4.0 国际」创作共享协议,转载或使用请遵守署名协议。