从斐波那契数列理解快速幂
发布于2022-08-27 02:16:48,更新于2022-08-28 21:21:42,标签:java algorithm 文章会持续修订,转载请注明来源地址:https://meethigher.top/blog斐波那契数列概念
F(0)=0,F(1)=1
当n>1时,且n为正整数时,F(n)=F(n-1)+F(n-2)
三种实现方式
- 递归,时间复杂度O(2^n)
- 动态规划,时间复杂度O(n)
- 矩阵快速幂,时间复杂度O(log₂n)
一、递归
递归的思路,从上往下算。
1 | public static int fib(int n) { |
二、动态规划
动态规划的思路,从下往上算。
通过递归的结果,查看前十项规律。
0 1 1 2 3 5 8 13 21 34
可知每一项都是前两项之和,这三个元素可以通过一个数组存储计算。
1 | public static int fibDP(int n) { |
三、矩阵快速幂
3.1 快速幂
正常的求高次方a^n,时间复杂度是O(n)
比如
1 | public static int power(int a, int n) { |
但是这个方式是可以优化的,也就是所谓的快速幂
快速幂的思路,就是将之前遍历相乘n次,降低到只需遍历相乘<n次。
举例来说,对于\(a^{11}\),推导过程如下
$$
a^{11}=a^{1*2^3+0*2^2+1*2^1+1*^0}=a^{2^3}*a^{2^1}*a^{2^0}=a^8*a^2*a^1
$$
通过第一个等号后面的内容的乘方,可知指数的系数恰好是十进制11的二进制1011,最后的结果就是二进制位为0时,不计算。
那么计算\(a^{11}\)只需要遍历3次。
再次验证\(a^{12}\),推导过程如下
$$
a^{12}=a^{1*2^3+1*2^2+0*2^1+0*2^0}=a^{2^3}*a^{2^2}=a^8*a^4
$$
那么计算\(a^{12}\)只需要遍历2次。
求多个相同因数的积的运算叫做乘方,乘方的结果叫做幂,相同因数就是底数,而因数的个数是指数。
接下来是如何求二进制,这个简单,放个例子。
1 | public static String DecToBin(int dec) { |
结合上步的推导结果,如果二进制位为0时,不计算,由此实现简单的快速幂。
1 | public static int fastPower(int a, int n) { |
快速幂算法的核心思想就是通过二进制右移计算,依次求二进制的低位,如果低位是1,就乘上底数,同时相应的底数每次自身都会做平方运算,这样不仅能把非常大的指数给不断变小,所需要执行的循环次数也变小,并且计算结果一样。
3.2 优化解法
知道了快速幂的解法。根据斐波那契数列的规定,构造递推关系
$$
\begin{bmatrix}
1&1 \\
1&0
\end{bmatrix}\begin{bmatrix}
F(n) \\
F(n-1)
\end{bmatrix}=\begin{bmatrix}
F(n)+F(n-1)\\
F(n)
\end{bmatrix}=\begin{bmatrix}
F(n+1)\\
F(n)
\end{bmatrix}
$$
$$
=\begin{bmatrix}
1&1 \\
1&0
\end{bmatrix}^n\begin{bmatrix}
F(1) \\
F(0)
\end{bmatrix}
$$
$$
=\begin{bmatrix}
1&1 \\
1&0
\end{bmatrix}^{n}\begin{bmatrix}
1 \\
0
\end{bmatrix}
$$
经如上推导,求斐波拉契数列的关键一步,就是求出n次方矩阵M来,最终的F(n)=M[1][0];
代码实现
1 | /** |
代码中的关系式推导过程如下