剑指offer面试题16:数值的整数次方+快速幂算法分析

原题:给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。


/*剑指书中细节:
*1.当底数为0且指数<0时
*会出现对0求倒数的情况,需进行错误处理,设置一个全局变量;
*2.判断底数是否等于0
*由于base为double型,不能直接用==判断
*3.优化求幂函数
*当n为偶数,a^n =(a^n/2)*(a^n/2)
*当n为奇数,a^n = a^[(n-1)/2] * a^[(n-1)/2] * a
*时间复杂度O(logn)
*/
public class Solution { 
      boolean invalidInput=false;    
      public double Power(double base, int exponent) {     
        if(equal(base,0.0)&&exponent<0){
            invalidInput=true;
            return 0.0;
        }
        int absexponent=exponent;
        if(exponent<0)
            absexponent=-exponent;
        double res=getPower(base,absexponent);
        if(exponent<0)
            res=1.0/res;
        return res;
  }
    boolean equal(double num1,double num2){
        if(num1-num2>-0.000001&&num1-num2<0.000001)
            return true;
        else
            return false;
    }
    double getPower(double b,int e){
        if(e==0)
            return 1.0;
        if(e==1)
            return b;
        double result=getPower(b,e>>1);
        result*=result;
        if((e&1)==1)
            result*=b;
        return result;
    }
}

这样的解法依然不是最优.

牛客网下评论置顶第一的是


/**
 * 1.全面考察指数的正负、底数是否为零等情况。
 * 2.写出指数的二进制表达,例如13表达为二进制1101。
 * 3.举例:10^1101 = 10^0001*10^0100*10^1000。
 * 4.通过&1和>>1来逐位读取1101,为1时将该位代表的乘数累乘到最终结果。
 */
public double Power(double base, int n) {
    double res = 1,curr = base;
    int exponent;
    if(n>0){
        exponent = n;
    }else if(n<0){
        if(base==0)
            throw new RuntimeException("分母不能为0"); 
        exponent = -n;
    }else{// n==0
        return 1;// 0的0次方
    }
    while(exponent!=0){
        if((exponent&1)==1)
            res*=curr;
        curr*=curr;// 翻倍
        exponent>>=1;// 右移一位
    }
    return n>=0?res:(1/res);       
}

原理如下:

(1).快速幂

   快速幂,顾名思义就是快速的求次幂,例如:a^b,普通的算法就是累乘,这样的计算方法的时间复杂度就是O(n),而快速幂的方法使得次幂的计算方法的时间复杂度降低到O(logn).

  假设我们要求a^b的结果,这里我们可以将b转换为二进制来求。例如:

                    a^11 = a(2  ^ 0 + 2 ^ 1 + 2 ^ 3) = a ^(1011);

  所以,我们得出结果:a^11 = (a ^ (2 ^ 3)) *( a ^(2 ^ 1)) *( a^(2 ^ 0));

 也就是说,我们只要把次幂化成二进制数,那么我们操作起来就非常的简单了。怎么化成二进制呢?在位运算符中,>>符号表示将一个数字右移一位,也就是说,我们这里可以通过这个符号来一位一位的计算(这里讲的是二进制数,当十进制数使用这个符号来操作的话,我们可以将十进制数当成二进制数右移)。同时我们还可以使用&符号来看看我们二进制数的最后一位数是不是0,比如 a & 1 == 0,表示的是a的二进制数的最后一位是0,反之就是不为0.这里清楚了这一点的话,下面的代码就非常的容易理解。

  这里来举一个例子,例如,我们想要求a ^ b的结果,按照快速幂的求法,代码如下:


public int fastPower(int a, int b) {
        int ans = 1;
        int base = a;
        while (b != 0) {
            if ((b & 1) != 0) { //如果当前的次幂数最后一位(二进制数)不为0的话,那么我们将当前权值加入到最后答案里面去
                ans = ans * base;
            }
            //权值增加
            base = base * base;
            b >>= 1;
        }
        return ans;
    }
比如7的二进制为0111   右移一位为0011 即3  所以在这里需要判定是否为奇数若为奇数 则需要*base.


你可能感兴趣的:(c/c++)