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; 
  }