根据题目,需要我们自己定义一个快速求幂的方法 pow(x, n)
。
方法一:迭代 + 二进制 + 移位。
求解
x
n
x^n
xn 最快速的方法就是循环
n
n
n 次,将
n
n
n 个
x
x
x 乘起来;我们可以利用二进制 和已知的
x
x
x 来减少循环次数;
假设
n
=
11
n=11
n=11,则对应的二进制为
1011
1011
1011,因此结果就会变成
x
11
=
x
8
⋅
x
0
⋅
x
2
⋅
x
1
=
x
1
∗
8
+
0
∗
4
+
1
∗
2
+
1
∗
1
x^{11} = x^8 · x^0 · x^2 · x^1 = x^{1*8 + 0*4 + 1*2 + 1*1}
x11=x8⋅x0⋅x2⋅x1=x1∗8+0∗4+1∗2+1∗1 ;
相当于,从
x
x
x 开始,然后每次循环都进行都对 x
进行赋值操作
x
=
x
2
=
x
⋅
x
x = x^2 = x · x
x=x2=x⋅x;并且判断
n
n
n 的二进制最后一位是不是 1(若是 1,则当前的
x
x
x 与结果相乘,若为 0 则跳过次步),最后进行右移操作。
对于 n < 0
,相当于求
1
x
n
\frac{1}{x^n}
xn1,因此将
x
x
x 取倒数,将
n
n
n 取反。
流程 myPow(double x, int n)
:
(1) 定义变量 res = 1
;
(2) 若 n != 0
, 循环:
① 与操作 n & 1
,判断最后一位是否为 1:若结果为 1,则将 res = res * x
,若为 0,则跳过这个步骤;
② x
进行赋值操作 x = x*x
,并将 n
右移一位 n >>= 1
;
(3) 返回结果 res
。
总结:利用二进制,通过不断地将指数 n n n 对半递减(右移),同时通过 x = x 2 x = x^2 x=x2 来累积结果;每当指数的最低位为 1 时,将当前的 x x x 乘入结果中;最终返回 x n x^n xn 的值。
方式二:递归
求解
x
n
x^{n}
xn 时,我们可以看成
x
13
=
x
6
+
1
∗
x
3
∗
x
1
+
1
∗
x
0
+
1
∗
1
x^{13} = x^{6 + 1} * x^{3} * x^{1+1} * x^{0 + 1} * 1
x13=x6+1∗x3∗x1+1∗x0+1∗1
我们按上面的公式求解
x
n
x^n
xn:我们可以先递归地计算出
y
=
x
⌊
n
/
2
⌋
y=x^{⌊n/2⌋}
y=x⌊n/2⌋;
假设递归的结果
y
y
y,判断
n
n
n 的奇偶:当
n
n
n 为奇数时,返回结果就多乘一个
x
x
x,即
y
=
y
⋅
x
y = y·x
y=y⋅x,若是
n
n
n 为偶数时,就直接返回
y
y
y 即可。
递归流程 quickMul(x, n)
:
(1) 终止条件:n == 0
,若为 0,则返回 1.0;
(2) 计算 y = quickMul(x, n/2)
;
(3) 根据 n % 2 == 1
判断 n 的奇偶情况,若为奇数就多乘一个
x
x
x,若为偶数,则直接返回
y
y
y。
总结:每次递归都会使得指数减少一半,因此递归的层数为 O ( l o g n ) O(logn) O(logn),算法可以在很快的时间内得到结果。
// 迭代 + 二进制 + 移位
class Solution {
public:
double myPow(double x, int d) {
double res = 1;
long n = d;
if (n < 0) {
// 这里可能有溢出风险 abs(负数)
// 因此将 n 的数据类型变为 long
n = -n;
x = 1 / x;
}
while (n > 0) {
if (n & 1)
res = res * x;
x = x * x;
n = n / 2; // 右移 1 位 n >>= 1; n = n >> 1
}
return res;
}
};
// 递归
class Solution {
public:
double myPow(double x, int d) {
long n = d;
return n < 0 ? quickMul(1 / x, -n): quickMul(x, n);
}
double quickMul(double x, long n) {
if (n == 0) return 1.0;
double y = quickMul(x, n/2);
return n % 2 == 1 ? y * y * x: y * y;
}
};