leetcode50:https://leetcode-cn.com/problems/powx-n/
解题思路:
求
x
n
x^{n}
xn 最简单的方法是通过循环将
n
n
n 个
x
x
x 乘起来, 依次求
x
1
,
x
2
,
…
,
x
n
−
1
,
x
n
,
x^{1}, x^{2}, \ldots, x^{n-1}, x^{n},
x1,x2,…,xn−1,xn, 时间复杂度为
O
(
n
)
O(n)
O(n)
快速幂方法可将时间复杂度降低至 O(log n),以下从“二分法”和“二进制”两个角度解析快速景法。
快速审解析(二进制角度):
利用十进制数字
n
n
n 的二进制表示,可对快速幂进行数学化解释。
- 对于任何十进制正整数
n
,
n,
n, 设其二进制为
"
b
m
…
b
3
b
2
b
1
"
(
b
i
" b_{m} \ldots b_{3} b_{2} b_{1} "\left(b_{i}\right.
"bm…b3b2b1"(bi 为二进制某位值,
i
∈
[
1
,
m
]
)
,
\left.i \in[1, m]\right),
i∈[1,m]), 则有:
- 二进制转十进制: n = 1 b 1 + 2 b 2 + 4 b 3 + … + 2 m − 1 b m \quad n=1 b_{1}+2 b_{2}+4 b_{3}+\ldots+2^{m-1} b_{m} \quad 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 \quad x^{n}=x^{1 b_{1}+2 b_{2}+4 b_{3}+\ldots+2^{m-1} b_{m}}=x^{1 b_{1}} x^{2 b_{2}} x^{4 b_{3}} \ldots 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}, \ldots, 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}, \ldots, b_{m} b1,b2,b3,…,bm 的值: \quad 循环执行以下操作即可。
- n & 1 n \& 1 \quad n&1 (与操作) : 判断 n n n 二进制最右一位是否为 1 ;
- n > > 1 n>>1 \quad n>>1 (移位操作) : n \quad 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}}, \ldots, x^{2^{m-1} b_{m}}
x20b1,x21b2,…,x2m−1bm 的值, 并将所有
x
2
i
−
1
b
i
x^{2^{i-1} b_{i}}
x2i−1bi 累计相乘即可, 其中:
x 2 i − 1 b i = { 1 , b i = 0 x 2 i − 1 , b i = 1 x^{2^{i-1} b_{i}}=\left\{\begin{array}{ll} 1 & , b_{i}=0 \\ x^{2^{i-1}} & , b_{i}=1 \end{array}\right. x2i−1bi={1x2i−1,bi=0,bi=1
快速审解析(分治法角度):
快速绵实际上是分治思想的一种应用。 -
x
n
=
x
n
/
2
×
x
n
/
2
=
(
x
2
)
n
/
2
,
x^{n}=x^{n / 2} \times x^{n / 2}=\left(x^{2}\right)^{n / 2},
xn=xn/2×xn/2=(x2)n/2, 令
n
/
2
n / 2
n/2 为整数, 则需要分为奇偶两种情况(设向下取整除法符号为 "
/
/
"
/ / "
//" ) :
x n = { ( x 2 ) n / / 2 , n 头偶数 x ( x 2 ) n / / 2 , n 头奇数 x^{n}=\left\{\begin{array}{ll} \left(x^{2}\right)^{n / / 2} & , n \text { 头偶数 } \\ x\left(x^{2}\right)^{n / / 2} & , n \text { 头奇数 } \end{array}\right. xn={(x2)n//2x(x2)n//2,n 头偶数 ,n 头奇数
观察发现,当 n n n 为奇数时, 二分后会多出一项 x x x 。 - 审结果获取:
- 根据推导, 可通过循环 x = x 2 x=x^{2} x=x2 操作, 每次把审从 n n n 降至 n / / 2 , n / / 2, n//2, 直至将审降为 0 ;
- 设
r
e
s
=
1
,
r e s=1,
res=1, 则初始状态
x
n
=
x
n
×
r
e
s
x^{n}=x^{n} \times r e s
xn=xn×res 。在循环二分时, 每当
n
n
n 为奇数时, 将多出的一项
x
x
x 乘入
r
e
s
,
r e s,
res, 则 最终可化至
x
n
=
x
0
×
r
e
s
=
r
e
s
,
x^{n}=x^{0} \times r e s=r e s,
xn=x0×res=res, 返回
r
e
s
r e s
res 即可。
- 转化为位运算:
- 向下整除 n / / 2 n / / 2 n//2 等价于 右移一位 n > > 1 n>>1 n>>1;
- 取余数 n % 2 n \% 2 n%2 等价于 判断二进制最右位 n & 1 n \& 1 n&1;
算法流程:
- 当 x = 0.0 x=0.0 x=0.0 时:直接返回 0.0 , 0.0 , 0.0, 以避免后续 1 除以 0 操作报错。分析:数字 0 的正数次审恒为 0 ; 0 0 ; \quad 0 0;0 的 0 次审和负 数次审没有意义, 因此直接返回 0.0 即可。
- 初始化 r e s = 1 res = 1 res=1
- 当 n < 0 n<0 n<0 时:把问题转化至 n ≥ 0 n \geq 0 n≥0 的范围内, 即执行 x = 1 / x , n = − n x=1 / x, n=-n x=1/x,n=−n
- 循环计算: 当
n
=
0
n=0
n=0 时跳出。
- 当 n & 1 = 1 n \& 1=1 n&1=1 时: 将当前 x x x 乘入 res (即 res ∗ = x ) \operatorname{res} *=x) res∗=x) 。
- 执行 x = x 2 ( x=x^{2} \quad( x=x2( 即 x ∗ = x ) x *=x) x∗=x)
- 执行 n n n 右移一位 ( ( ( 即 n > > = 1 ) n>>=1) n>>=1) 。
- 返回 res
复杂度分析:
- 时间复杂度 O ( log n ) : O(\log n): O(logn): 二分的时间复杂度为对数级别。
- 空间复杂度 O ( 1 ) : r e s , b O(1): \quad r e s, b O(1):res,b 等变量占用常数大小额外空间。
PY
class Solution:
def myPow(self, x: float, n: int) -> float:
if x == 0.0: return 0.0
res = 1
if n < 0: x, n = 1 / x, -n
while n:
if n & 1: res *= x
x *= x
n >>= 1
return res
GO
func myPow(x float64, n int) float64 {
if x == 0.0 {
return 0.0
}
res := 1.0
if n < 0{
x , n = 1/x , -n
}
for i := n ; i > 0; i>>=1 {
//fmt.Println(i)
if (i % 2 == 1){
res *= x
}
x *= x
//fmt.Println(x)
}
return res
}