快速幂
个人博客:https://sgeekioi.github.io/2019/07/31/fastpower/#more
【介绍】
顾名思义,快速幂就是快速算底数的n次幂。其时间复杂度为 O(log₂N), 与朴素的O(N)相比效率有了极大的提高。
【描述】
计算a的n次方就是将n个a乘起来 a n = a × a × a . . . × a ⎵ n个a a^n = \underbrace{a \times a \times a...\times a}_{\text{n个a}} an=n个a a×a×a...×a。但是,当n的值特别大的时候,这种累乘的方法就不合适了。不过,由结合律我们知道 a b + a c = a b c , a b × a b = a 2 b = a b 2 a^b + a^c = a^{bc} , a^b \times a^b= a^{2b} = {a^b}^2 ab+ac=abc,ab×ab=a2b=ab2。二进制取幂的想法是,我们将取幂的任务按照指数的二进制表示来分割成更小的任务。我们来举个例子
11的二进制数为 1011。 对进制不太理解的戳这里
3 11 = 3 1011 = 3 8 ⋅ 3 2 ⋅ 3 1 3^{11} = 3^{1011} = 3^8 \cdot 3^2 \cdot 3^1 311=31011=38⋅32⋅31
上面这个算式是怎么来的呢。看下面
3 1 = 3 2 0 = 3 3 2 = 3 2 1 = 9 3 8 = 3 2 3 = 6561 3^1 = {3^2}^0 = 3 \\ 3^2 = {3^2}^1 = 9 \\ 3^8 = {3^2}^3 = 6561 31=320=332=321=938=323=6561
也就是为了计算 3 11 3^{11} 311,我们只需要将二进制位对应为1的整系数幂乘起来就行了:
3 11 = 3 × 9 × 6561 = 177147 3^{11} = 3 \times 9 \times 6561 = 177147 311=3×9×6561=177147
将上述过程说的形式化一些,如果把n写作二进制为 ( n 1 n t − 1 … n 1 2 1 + n 0 2 0 ) (n_1n_{t-1}…n_12^1+n_02^0) (n1nt−1…n121+n020),那么有:
n
=
n
t
2
t
+
n
t
−
1
2
t
−
1
+
…
+
n
1
2
1
+
n
0
2
1
n=n_t2^t+n_{t-1}2^{t-1}+…+n_12^1+n_02^1
n=nt2t+nt−12t−1+…+n121+n021
其中
n
ϵ
0
,
1
n \epsilon 0,1
nϵ0,1 。那么就有
a n = ( a n t 2 t + … + n 0 2 0 ) = a n 0 2 0 × a n 1 2 1 × … × a n 0 2 0 a^n=(a^{n_t2^t+…+n_02^0}) \\ =a^{n_02^0} \times a^{n_12^1} \times … \times a^{n_02^0} an=(ant2t+…+n020)=an020×an121×…×an020
根据上式我们发现,原问题被我转换成了形式相同的子问题的乘积,并且我们可以在常数时间内从 2 i 2^i 2i项推出 2 i + 1 2^{i+1} 2i+1项。这个算法的复杂度是 O ( l o g 2 n ) O(log_2n) O(log2n)的。
##【代码实现】
递归方法
public double power1(double a, int n) {
if (n == 0) {
return 1;
}
double res = power(a,n/2);
if((n%2)==1){
return res * res * a;
}else{
return res * res;
}
}
非递归式
它在循环的过程将二进制为1时对应的幂累乘到答案中。尽管两者的理论复杂度相同,但第二种在实践过程中的速度会比递归式更快的,因为递归会重复运算花费一定开销。
public double power2(double a,int n){
double res = 1;
while (n>0){
if((n&1)==1){
res*=a;
}
a*=a;
n>>=1;
}
return res;
}