快速幂
题意:
求 x n x^n xn
-100.0 < x < 100.0
- - 2 31 2^{31} 231 <= n <= 2 31 − 1 2^{31-1} 231−1
n
是一个整数- 要么
x
不为零,要么n > 0
- − 1 0 4 -10^4 −104 <= x n x^n xn <= 1 0 4 10^4 104
思路一:循环将x乘n次,龟速幂。但是当n很大时,会因迭代次数过多而超时。
思路二:二进制优化。
对于正整数n,可以分解为 n = 2 n 1 + 2 n 2 + . . . + 2 n k n=2^{n_1}+2^{n_2}+...+2^{n_k} n=2n1+2n2+...+2nk。即 x n = x 2 n 1 + 2 n 2 + . . . + 2 n k = x 2 n 1 x 2 n 2 . . . x 2 n k x^n=x^{2^{n_1}+2^{n_2}+...+2^{n_k}}=x^{2^{n_1}}x^{2^{n_2}}...x^{2^{n_k}} xn=x2n1+2n2+...+2nk=x2n1x2n2...x2nk。
例如n=5,二进制表示为0101,观察1的位置(分别是第0、2位),所以 5 = 2 0 + 2 2 5=2^0+2^2 5=20+22;若x=3,则 3 5 = 3 2 0 + 2 2 = 3 1 3 4 3^5=3^{2^0+2^2}=3^13^4 35=320+22=3134。
对于
n<0
的情况,转化一下即可: x n → ( 1 x ) − n x^n \rightarrow (\frac{1}{x})^{-n} xn→(x1)−n。注意当n=-2^31
是,其负数是2^31
超出了int范围的上界2^31-1
,所以代码上要用long
类型接收n
。
这样的话,只要计算 3 1 3 4 3^13^4 3134,而不用计算 3 ∗ 3 ∗ 3 ∗ 3 ∗ 3 3*3*3*3*3 3∗3∗3∗3∗3。只需要最多32位即可表示int范围内的整数n,所以只需log(n)次运算就可以完成幂的计算。
关键在于如何完成转化:
3
5
=
3
2
0
+
2
2
=
3
1
3
4
3^5=3^{2^0+2^2}=3^13^4
35=320+22=3134。我们可以发现拆分的项数是和二进制1的数量相同的,只需要在每次遇到二进制1时乘对应值即可,而对应的值即是累乘x
。
如 3 5 = 3 1 3 4 3^5=3^13^4 35=3134, 5 = ( 0101 ) b , a n s = 1 5=(0101)_b,ans=1 5=(0101)b,ans=1:
- 对于第0位(1),
a
n
s
ans
ans乘上此时的对应值
3
2
0
=
3
1
3^{2^0}=3^1
320=31,然后
x
累乘得第1位的对应值 3 ∗ 3 = 3 2 1 = 3 2 3*3=3^{2^1}=3^2 3∗3=321=32 - 对于第1位(0),不需要计算答案,继续累乘
x
得第2位的对应值 3 2 3 2 = 3 2 2 = 3 4 3^23^2=3^{2^2}=3^4 3232=322=34 - 对于第2位(1), a n s ans ans乘上此时的对应值 3 4 3^4 34;
- 对于第3位(0),不需要计算答案,继续累乘
x
得第2位的对应值 - b的二进制位已遍历结束,最后 a n s = 3 1 3 4 ans=3^13^4 ans=3134
显然遍历b的二进制位的时间复杂度为 l o g ( n ) log(n) log(n),代码如下:
// x^n
class Solution {
public:
double myPow(double x, int n) {
double ans = 1; // 最终答案
long b = n; // long接受防止越界
if(b < 0) // 处理负数幂的情况
x = 1 / x,b = -b;
while(b){ // 遍历b的二进制位(移位)
if(b & 1) // 二进制位为1
ans *= x; // 累乘答案
x *= x; // 对应值
b >>= 1; // 右移(除2)
}
return ans;
}
};