剑指offer:数值的整数次方
题目描述
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
解题思路
- 当底数为0且指数小于0时:会出现对0求倒数的错误情况,可以采用三种错误处理方式的任意一种,可以与面试官讨论决定。
方式 | 优点 | 缺点 |
---|---|---|
返回值 | 和系统API一致 | 不能方便地使用计算结果 |
全局变量 | 能够方便的使用计算结果 | 用户可能会忘记检查全局变量 |
异常 | 可以为不同的出错原因定义不同的异常类型,逻辑清晰明了 | 有些语言不支持,抛出异常时对性能有负面影响 |
- 判断double类型底数是否等于0:需要使用近似值来比较
- 由于0的0次方在数学上是没有意义的,因此无论输出0还是1都是可以接受的,需要与面试官讨论,表明我们已经考虑到这个边界值了。
- 优化求幂函数
- 当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 isVaild = true; public double Power(double base, int exponent) { // 需要考虑底数为零且指数为0的特殊情况 if (equals(base, 0.0) && exponent<=0){ isVaild = false; return 0.0; } int absExponent = exponent; if (exponent < 0) absExponent = -exponent; double ret = getPower(base, absExponent); if (exponent < 0) ret = 1.0 / ret; return ret; } public double getPower(double base, int exponent){ if (exponent == 0) return 1.0; if (exponent == 1) return base; // 优化求幂算法,O(logn) double ret = getPower(base, exponent>>1); ret = ret*ret; // 位操作代替求余 if ((exponent&1) == 1) ret = ret * base; return ret; } // 判断double类型是否为0 public boolean equals(double x, double y){ if(x-y < 0.00001 && x-y > -0.00001) return true; else return false; } }
我们也可以对上面那个求幂函数转换为二进制进行计算:
写出指数的二进制表达,例如13表达为二进制1101。
举例:10^1101 = 10^0001*10^0100*10^1000。
所以base ^ 13 = (base ^ 1) * (base ^ 4) * (base ^ 8)
代码如下:
/** * 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){ // 为1时将该位代表的乘数累乘到最终结果 if((exponent&1)==1) res*=curr; curr*=curr;// 2的指数次方翻倍 exponent>>=1;// 右移一位 } return n>=0?res:(1/res); }