前言
第一次看到用数学公式去简化,感觉数学真的是理工科的基础,理工科的根。
通过矩阵递推公式来简化算法,把斐波拉契数列的时间复杂度降到了O(log2N)。
一、从数学出发
1、思想
这是leetcode官方解答的原图。
可以看到这个题就是初等矩阵的变换,左乘初等矩阵执行相应的行变换,即把第二行加到第二行,然后再交换两行。这也是题目想要的变换。不断的进行这种变换,就意味着左边不断乘上这样的初等矩阵E。
所以想得到F(N+1)和F(N)就乘En。
转化成数学问题,再进行数学的化简,瞬间高级了起来。这种思想值得学习。
2、源码
//设置取模的大小MOD和初等矩阵E
static final long MOD = 1000000007;
static final int[][] E = {{1,1},{1,0}};
public int fib2(int n) {
if(n==0 || n==1)
return n;
//执行E的n-1次方得到F(N)和F(N-1)
//这里只需要看[0][0]位,毕竟F(1) == 1,F(0)==0,相加只用得到resultE[0][0] * 1 + 0 * resultE[0][1];
return pow(n-1)[0][0];
}
public int[][] pow(int n){
int[][] resultM = {{1,0},{0,1}};
int[][] tempM = E;
while(n > 0){
//等于1就把前面准备好的矩阵乘上,即指数加上。否则对resultM矩阵不做任何操作,毕竟该位为0
if((n & 1) == 1){
resultM = multiply(resultM,tempM);
}
//计算下一位等于1时的矩阵值。左边一位是0是1都把它乘上,为更左边的1做准备。
tempM = multiply(tempM,tempM);
//右移一位,推动循环
n >>= 1;
}
return resultM;
}
public int[][] multiply(int[][] M1,int[][] M2){
int[][] R = new int[2][2];
for(int i = 0;i < 2;i++){
for(int j = 0;j<2;j++){
R[i][j] = (int)((long)(M1[i][0] * M1[0][j]) + (long) (M2[i][1] * M2[1][j]) % MOD);
}
}
return R;
}
二、普通做法
普通做法实在是太简单了,有递归,虽代码简洁,但是会有很多多余的计算,很耗费时间;有非递归,根据递推公式一步一步的走,时间复杂度为O(N)。