正式讲一下斐波那契数:
首先我们知道有一个著名的算法面试题:
一共有n个台阶,你一次可以走一个台阶,或者两个台阶。那么,走到台阶顶时,一共有多少种走法。
比如三个台阶,你可以 1,2。。。或者1,1,1 或者2,1。。一共三种走法。
网络上会有几种做法,
1.直接思路:
这好像是这道题目的标准“解法”
如果我们现在在n阶,那么我们可以迈一步或者迈两步,结果就是
f(n) = f(n-1) + f(n-2)。。。即n的台阶的种数应该是,n-1阶的种数与n-2阶种数之和(由于第一步就不一样,所有,后面的二者互相独立)。
所以
int f(int n) {
if(n ==0 || n == 1) {
return 1;
}
return f(n-1)+f(n-2);
}
2. 动态规划
1的东西虽然给出了这个题的一个突破口,但还是有些问题比如:第1中的时间复杂度时是指数次的,过于庞大。。
所以可以考虑用动态规划。。第三版第15章的动态规划内容
如果有我们可以预先纪录下f(n-1),或者f(n-2),避免重复计算。
所以
a[n] = {0};
a[0] = 1; a[1] = 1;
int f(int n) {
if(a[n] != 0) {
return a[n];
}
a[n] = f(n-1)+f(n-2)
return a[n];
}
这个时间复杂度就比较好了,空间和时间都是O(n) 。(当然后面我们会看到,这么说不是很恰当)
3. 动态规划二
2的思路比较好了,但是一来有比较大的空间复杂度,二来,递归调用,让人不爽。
所以我们可以用算法导论(动态规划)那一章的另一个思路直接正着计算。
结果出乎意料的简单:
int f(int n) {
int a = 1; b = 1;
int c = 0;
for(int i = 1; i < n; i++) {
c = a;
a = a+b;
b = c;
}
return a;
}
--------------------上面的东西足够应付面试-----------------------
-------------------下面只是本人借着算法导论开脑洞的结果-----------------
4. 矩阵快乘
3的解法已经很不错了。基本上就可以了,下面即将开启二逼的求知之旅。。这是算法导论,第三十一章的后面大题
考虑2*2矩阵
0 1
1 1
这是一个 如果我们将这个矩阵乘以 A1 , A2
0 1 A1 A2
1 1 * A2 = A1+A2
由于斐波那契数列, A3 = A1+A2 ...结果我们发现这个东西其实是实现了 A1, A2 到 A2,A3的转换。。。 如果我们用 A2,A3, 去乘,结果自然是 A3 , A2+A3 也就是 A3,A4
所以我们可以这么写,我们令M表示矩阵 ,
0 1
1 1
那么 (M .... (M* (M * (A1,A2)T))...) = (An-1, An),其中一共有n-2层的M.
考虑到相容矩阵相乘具有如下性质 (M*N)*Q = M*(N*Q).
所以变换括号,(M*M*.....M)*(M*M.....*M)*(A1, A2)T
其中我们让那些括号里面的M个数相同(如果是奇数的话单独拎一个M到最外层),结果,我们其实只需要算出前面的,后面的也不用去算了。。
这个就是分治的矩阵快乘。
名义上的时间复杂度是O(log(N))...比前面的都要屌。
5. 斐波那契通项公式
这个东西就不多介绍了。。因为百度百科上有,好多种方法。。。而且算法导论本身也给了一种。 第三版的4-4里面也给了一个。用生成函数的办法做的。
总之结果就是这个东西,大概是 ((( 1 + sqrt(5) ) / 2) ^ n) / sqrt(5)。
6. 基于位运算的批判与重新思考
这个部分是毁三观的,无破则无立。
首先我们通过5可以知道,这个斐波那契数,随着n几乎是一个指数式的增长。。。位数大概是:log(( 1 + sqrt(5) ) / 2) ^ n) ) 就是 n*log(( 1 + sqrt(5) ) / 2) ) => O(N)
我们方便计算就说它有n位的二进制数吧。
现在所有的计算机都是有限位数的,32位,64位机器。。或者128位。。。所以 n/128 其实也是 O(N)。。也就是说,我们这个n稍微大一点(比如说1000),那么前面的那些1,2,3部分代码的int 类型就不够了。。即使用long long 也是不够的。。。
具体可以用大整数这个东西处理。
下面是31-3的第d小题的分析
如果我们用大整数处理,那么时间空间复杂度的结果会有问题,比如“2里面说:空间和时间都是O(n)”。这个就不恰当。空间上,到最后的数有n位,那么空间复杂度自然也就是(1+2+3+...+n) = O(N*N)..时间上其实也是这样的,N位数N-1位数相加,时间复杂度自然是O(N)。。。所以时间复杂度自然是 (1+2+3+...+n) = O(N*N)
3的情况与2类似,只是空间复杂度变小了一点而已。。。。
对于4的矩阵情况,我们知道最后是两个几乎一样的矩阵相乘最后得到一个 n位的结果,那么很明显,我们大概需要 n/2的数乘以 n/2位的数,由于31-3的d小题,约定两个n/2数相乘需要 O(N*N/4)的时间。结果自然就是时间复杂度O(N*N)...那个 log(N)其实是名义上的。这大概就是算法导论题目里面说的斐波那契数的终极奥义了。
7. 大数乘法的归并运算与究极思考。
额,前面约定"约定两个n/2数相乘需要 O(N*N/4)的时间",这个东西能不能改进呢?,结果当然是能。
比如用算法导论30-1的方法,我们可以 在 O(N的log3次方)时间内算出来。 所以整体的时间复杂度优化成 O(N的log3次方)
由于这个东西用到了归并运算,可以考虑用算法导论第27章的知识进行优化。实现多核同步运算。。。
这个都究极时间复杂度大概是算法导论 第31章第一小部分里面讲的 那个 O(N logN loglogN)
8. 扯蛋的扩展,
如果我们可以一次迈1,2,3阶该怎么办。
很简单啊,
f(n) = f(n-1) + f(n-2) + f(n-3)。。然后照着做就可以了。矩阵哪里也改一下就可以了。