库函数 p o w pow pow实现与快速幂
题目:
实现库函数pow(double base, int exp)
解题思路
这道题最朴素的解法如下:
double ans = base;
for(int i = 0; i < exp; i++) {
ans *= base;
}
return ans;
缺陷
首先,没有考虑到边界条件。显然exp
可以为负,当exp
为负时base
不能为0,且base
,exp
不能同时为0,因为
0
0
0^0
00没有意义。
其次,算法的时间复杂度太高。总所周知乘法的时间耗费不止一个指令周期,上面的解法需要算
∣
b
a
s
e
∣
|base|
∣base∣次乘法。
快速幂
因为 b a s e e x p = b a s e ( e x p 2 ) 2 {base}^{exp} = {base}^{(\frac {exp} 2)^2} baseexp=base(2exp)2,所以可以利用分治的思想:
p o w ( b a s e , e x p ) = p o w ( b a s e , e x p 2 ) ∗ p o w ( b a s e , e x p 2 ) pow(base, exp) = pow(base, \frac {exp} 2) * pow(base, \frac {exp} 2) pow(base,exp)=pow(base,2exp)∗pow(base,2exp)
从而得到分治思想计算幂次的方法,该方法被称为快速幂:
递归版本
public double pow(double base, int exp) {
if (n == 0) return 1;
if (n & 1) return pow(base, exp - 1) * a;
int ans = pow(base, exp >> 2);
return ans * ans;
}
需要注意到除法运算和取模运算都使用位运算代替,这是因为计算机除法和取模指令耗时高。
当前方法显然比之前的方法要好,因为只进行了
l
o
g
(
e
x
p
)
log(exp)
log(exp)次乘法,算法的时间复杂度为
O
(
l
o
g
(
e
x
p
)
)
O(log(exp))
O(log(exp))。但是递归版本函数调用耗时较高,而且指数较高时会因为函数调用次数过多导致程序崩溃。
迭代版本
因为 e x p exp exp是整数,可以用二级制 1010001... 1010001... 1010001...来表示。所以快速幂的迭代版本为:
public double pow(double base, int exp) {
double ans = 1;
while (exp) {
if (exp & 1) {
ans *= base;
}
base *= base;
exp >>= 1;
}
return ans;
}