题目描述
实现 p o w ( x , n ) pow(x, n) pow(x,n) ,即计算 x x x 的 n n n 次幂函数(即, x n x^n xn)。不得使用库函数,同时不需要考虑大数问题。
解法
求
x
n
x^n
xn 最简单的方法是通过循环将
n
n
n 个
x
x
x 乘起来,依次求
x
1
,
x
2
,
.
.
.
,
x
n
−
1
,
x
n
x^1, x^2, ..., x^{n-1}, x^n
x1,x2,...,xn−1,xn ,时间复杂度为
O
(
n
)
O(n)
O(n) 。
快速幂法 可将时间复杂度降低至
O
(
l
o
g
2
n
)
O(log_2 n)
O(log2n) ,以下从 “二分法” 和 “二进制” 两个角度解析快速幂法。
快速幂解析(二进制角度):
-
对于任何十进制正整数 n n n ,设其二进制为 " b m . . . b 3 b 2 b 1 b_m...b_3b_2b_1 bm...b3b2b1 "( b i b_i bi 为二进制某位值, i ∈ [ 1 , m ] i \in [1,m] i∈[1,m] ),则有:
- 二进制转十进制: n = 1 b 1 + 2 b 2 + 4 b 3 + . . . + 2 m − 1 b m n = 1b_1 + 2b_2 + 4b_3 + ... + 2^{m-1}b_m n=1b1+2b2+4b3+...+2m−1bm (即二进制转十进制公式) ;
- 幂的二进制展开: x n = x 1 b 1 + 2 b 2 + 4 b 3 + . . . + 2 m − 1 b m = x 1 b 1 x 2 b 2 x 4 b 3 . . . x 2 m − 1 b m x^n = x^{1b_1 + 2b_2 + 4b_3 + ... + 2^{m-1}b_m} = x^{1b_1}x^{2b_2}x^{4b_3}...x^{2^{m-1}b_m} xn=x1b1+2b2+4b3+...+2m−1bm=x1b1x2b2x4b3...x2m−1bm ;
-
根据以上推导,可把计算 x n x^n xn 转化为解决以下两个问题:
- 计算 x 1 , x 2 , x 4 , . . . , x 2 m − 1 x^1, x^2, x^4, ..., x^{2^{m-1}} x1,x2,x4,...,x2m−1 的值: 循环赋值操作 x = x 2 x = x^2 x=x2 即可;
- 获取二进制各位
b
1
,
b
2
,
b
3
,
.
.
.
,
b
m
b_1, b_2, b_3, ..., b_m
b1,b2,b3,...,bm 的值: 循环执行以下操作即可。
- n & 1 n\And1 n&1 (与操作): 判断 n n n 二进制最右一位是否为 1 1 1 ;
- n > > 1 n>>1 n>>1 (移位操作): n n n 右移一位(可理解为删除最后一位)。
-
因此,应用以上操作,可在循环中依次计算 x 2 0 b 1 , x 2 1 b 2 , . . . , x 2 m − 1 b m x^{2^{0}b_1}, x^{2^{1}b_2}, ..., x^{2^{m-1}b_m} x20b1,x21b2,...,x2m−1bm 的值,并将所有 x 2 i − 1 b i x^{2^{i-1}b_i} x2i−1bi 累计相乘即可。
- 当 b i = 0 b_i = 0 bi=0 时: x 2 i − 1 b i = 1 x^{2^{i-1}b_i} = 1 x2i−1bi=1 ;
- 当 b i = 1 b_i = 1 bi=1 时: x 2 i − 1 b i = x 2 i − 1 x^{2^{i-1}b_i} = x^{2^{i-1}} x2i−1bi=x2i−1 ;
快速幂解析(二分法角度):
- 二分推导:
x
n
=
x
n
/
2
×
x
n
/
2
=
(
x
2
)
n
/
2
x^n = x^{n/2} \times x^{n/2} = (x^2)^{n/2}
xn=xn/2×xn/2=(x2)n/2 ,令
n
/
2
n/2
n/2 为整数,则需要分为奇偶两种情况(设向下取整除法符号为 “
/
/
//
//” ):
- 当 n n n 为偶数: x n = ( x 2 ) n / / 2 x^n = (x^2)^{n//2} xn=(x2)n//2 ;
- 当 n n n 为奇数: x n = x ( x 2 ) n / / 2 x^n = x(x^2)^{n//2} xn=x(x2)n//2 ,即会多出一项 x x x ;
- 幂结果获取:
- 根据二分推导,可通过循环 x = x 2 x = x^2 x=x2 操作,每次把幂从 n n n 降至 n / / 2 n//2 n//2 ,直至将幂降为 0 0 0 ;
- 设
r
e
s
=
1
res=1
res=1 ,则初始状态
x
n
=
x
n
×
r
e
s
x^n = x^n \times res
xn=xn×res 。在循环二分时,每当
n
n
n 为奇数时,将多出的一项
x x x 乘入 r e s res res ,则最终可化至 x n = x 0 × r e s = r e s x^n = x^0 \times res = res xn=x0×res=res ,返回 r e s res res 即可。
- 转化为位运算:
- 向下整除 n / / 2 n // 2 n//2 等价于 右移一位 n > > 1 n >> 1 n>>1 ;
- 取余数 n % n \% n% 等价于 判断二进制最右一位值 n & 1 n \& 1 n&1 ;
算法流程:
-
当 x = 0 x = 0 x=0 时:直接返回 0 0 0 (避免后续 x = 1 / x x = 1 / x x=1/x 操作报错)。
-
初始化 r e s = 1 res = 1 res=1 ;
-
当 n < 0 n < 0 n<0 时:把问题转化至 n ≥ 0 n \geq 0 n≥0 的范围内,即执行 x = 1 / x x = 1/x x=1/x , n = − n n = - n n=−n ;
-
循环计算:当 n = 0 n = 0 n=0 时跳出;
- 当 n & 1 = 1 n \& 1 = 1 n&1=1 时:将当前 x x x 乘入 r e s res res (即 r e s ∗ = x res *= x res∗=x);
- 执行 x = x 2 x = x^2 x=x2 (即 x ∗ = x x *= x x∗=x );
- 执行 n n n 右移一位(即 n > > = 1 n >>= 1 n>>=1)。
-
返回 r e s res res。
复杂度分析
- 时间复杂度 O ( l o g 2 n ) O(log_2 n) O(log2n) : 二分的时间复杂度为对数级别。
- 空间复杂度 O ( 1 ) O(1) O(1) : r e s res res, b b b 等变量占用常数大小额外空间。
C++代码实现
class Solution {
public:
double myPow(double x, int n) {
if(x == 0) return 0;
long b = n;
double res = 1.0;
if(b < 0) {
x = 1 / x;
b = -b;
}
while(b > 0) {
if((b & 1) == 1) res *= x;
x *= x;
b >>= 1;
}
return res;
}
};