ACM的竞赛中,经常会出现涉及到大数模幂运算的题目,如求解2的10000次方模100000009的结果,这就需要我们设计一种有效的求幂算法。本文将结合的以上应用场景,分析以下几种常用的求幂算法并给出java代码的实现:
- 递归方法:二分快速求幂(又叫矩阵快速幂算法)
- 非递归方法:二进制转换法
二分快速求幂
这种方法的设计思想很简单:对于A的n次幂,当n为偶数时,A^n = A^(n/2) * A^(n/2);当n为奇数时,A^n = A^(n/2) * A^(n/2) * A (其中n/2取整)。因此,直接用递归求解即可。时间复杂度为O(lgn)。这里就不再多说,贴出代码:
public static long pow(int a,int n){
if(n==0) return 1;
if(n==1) return a;
if(n%2==0)
return pow(a*a,n/2);
else
return pow(a*a,n/2)*a;
}
二进制转换法
以上方法效率是不高的,因为函数调用的代价非常昂贵。这里我们考虑用循环的方法进行改善,举个例子:
对于5的900次方:
900=29+28+27+24+23+21=512+256+128+16+8+2
因此计算可转化为:
5900=52∗28∗516∗5128∗5256∗5512
我们可以按以下步骤求解:
52=5∗5
54=52∗52
58=54∗54
516=58∗58
532=516∗516
564=532∗532
5128=564∗564
5256=5128∗5128
5512=5256∗5256
其实细想以下,数学原理跟第一种方法是一样的,但是在代码实现层面,我们进行了优化,看一下代码:
public static double pow(double a, int n){
double res=1;
while(n>0){
if((n&1)==1){ //相当于n%2 != 0
res *= a;
}
a*=a;
n>>=1;
}
return res;
}
我们知道定义long型整数的话,当结果大于2的64次方就溢出了,往往遇到大数求幂题目会给出模数,对结果进行求模处理,根据公式a^n% m=(…((a*a % m)*a%m)……)*a%m,我们对上述代码做以下改进:
public static long pow(int a, int n, int m){ //m表示模数
long res = 1;
while (n > 0) {
if ((n & 1) == 1)
res = (res * a) % m;
a = (a * a) % m;
b >>= 1;
}
return res;
}