递归的方式大家可以看看官方题解,官方写得挺好懂:
LeetCode 50. Pow(x, n) 官方题解
然后呢,官方的迭代方式写得有点复杂,我写一下我的理解,应该会简单一点(啰嗦亿点)
首先呢我们来算算 x 77 x^{77} x77。我们本能的想, x 77 x^{77} x77就是将77个 x x x相乘。那么有没有更快的方法呢?
77个
x
x
x相乘,其实就是
x
1
+
1
+
1
+
⋯
+
1
(
一
共
77
个
1
)
x^{1+1+1+\cdots+1}(一共77个1)
x1+1+1+⋯+1(一共77个1)。指数一直加1当然可以得出77,但是1+4+8+64也可以得出77,而且比单独的加法快很多(当幂运算指数越大,这种方式效率越高)但是为什么
77
=
1
+
4
+
8
+
64
77=1+4+8+64
77=1+4+8+64呢?拉回我们的问题,我们可以知道,等式:
x
77
=
x
1
×
x
4
×
x
8
×
x
64
x^{77}=x^{1} \times x^{4} \times x^{8} \times x^{64}
x77=x1×x4×x8×x64成立。但是为什么会成立呢?
我们将77分解成二进制看一下:
(
77
)
10
=
(
1001101
)
2
(77)_{10} = (1001101)_2
(77)10=(1001101)2可以发现,这些指数 1,4,8 和 64,恰好就对应了二进制表示
(
1001101
)
2
(1001101)_2
(1001101)2中的每个 1!这是为什么呢?因为它是二进制呀:
我们知道,二进制就是满二进一,第 n n n个二进制位的数值就表示有多少个 2 n − 1 2^{n-1} 2n−1。(如果不理解,可以代入我们熟悉的十进制思考一下) 同时因为二进制只有0或1,所以如果二进制为位0,就代表有0个 2 n − 1 2^{n-1} 2n−1
所以 ( 1001101 ) 2 (1001101)_2 (1001101)2就表示:
77有一个 2 0 2^0 20(第一个二进制位是1),有0个 2 1 2^1 21(第二个二进制位为0),有一个 2 2 2^2 22(第三个二进制位是1),同理,有一个 2 3 2^3 23和 2 7 2^{7} 27(第4和第8个二进制位是1)。
所以77的二进制位的意思就是: 77 = 1 + 4 + 8 + 64 77=1+4+8+64 77=1+4+8+64
最终我们知道: x 77 = x 1 × x 4 × x 8 × x 64 x^{77}=x^{1} \times x^{4} \times x^{8} \times x^{64} x77=x1×x4×x8×x64
也就是: x 77 = x 2 0 × x 2 2 × x 2 3 × x 2 7 x^{77}=x^{2^0} \times x^{2^2} \times x^{2^3} \times x^{2^7} x77=x20×x22×x23×x27。我们写代码就要用这个式子,这样子方便我们循环算出指数。
有了这个思路,我们就可以写出迭代版的快速幂算法了。大家可以直接看代码,但是如果还没有思路,或者代码看不懂,可以看一下下面的说明:
把77没有的二进制位补上,即:
77
=
2
0
+
2
2
+
2
3
+
2
6
=
1
×
2
0
+
0
×
2
1
+
1
×
2
2
+
1
×
2
3
+
0
×
2
4
+
0
×
2
5
+
1
×
2
6
77=2^0+2^2+2^3+2^6 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\\ =1\times2^0+ 0\times2^1+ 1\times2^2+ 1\times2^3+ 0\times2^4+ 0\times2^5+ 1\times2^6
77=20+22+23+26 =1×20+0×21+1×22+1×23+0×24+0×25+1×26
写成最初的幂运算:
x
77
=
x
1
×
2
0
+
0
×
2
1
+
1
×
2
2
+
1
×
2
3
+
0
×
2
4
+
0
×
2
5
+
1
×
2
6
=
x
1
×
2
0
×
x
0
×
2
1
×
x
1
×
2
2
×
x
1
×
2
3
×
x
0
×
2
4
×
x
0
×
2
5
×
x
1
×
2
6
x^{77}= x^{ 1\times2^0+ 0\times2^1+ 1\times2^2+ 1\times2^3+ 0\times2^4+ 0\times2^5+ 1\times2^6 } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \\=x^{1\times2^0}\times x^{0\times2^1}\times x^{1\times2^2}\times x^{1\times2^3}\times x^{0\times2^4}\times x^{0\times2^5}\times x^{1\times2^6}
x77=x1×20+0×21+1×22+1×23+0×24+0×25+1×26 =x1×20×x0×21×x1×22×x1×23×x0×24×x0×25×x1×26
思路:写成这样子我们发现,我们可以用循环,每一次都将指数翻倍(也就是将上一个项平方),然后通过判断二进制的最低位是否为1,来判断最终的式子是否要乘上当前循环的
2
n
−
1
2^{n-1}
2n−1的值。
代码:
/**
x为底数,N为指数。计算x^N
*/
double quickMul(double x, long N) {
double ans = 1.0;
// 在对 N 进行二进制拆分的同时计算答案
while (N > 0) {
/*
当二进制位为0时,表示有0个2^n-1
此时我们将x平方,但是不乘在算式中
(注意此时的x经过循环已经不是最初的x了,它可能是x^4,平方后就是x^8)
如果 N 二进制表示的最低位为 1,那么与ans相乘
这一步作用就是:
通过循环的将x^1,x^4,x^8,x^64这几个需要相乘的数乘在一起
*/
if (N % 2 == 1) {
ans *= x;
}
// 通过前面的文字分析可以知道,每一次循环都要将底数平方(指数翻倍)
x *= x;
// 舍弃 N 二进制表示的最低位,这样我们每次只要判断最低位是否为1即可
N /= 2;
}
return ans;
}
public double myPow(double x, int n) {
long N = n;
// 如果求负数幂,则等价于求倒数: 1/(x^-n)
return N >= 0 ? quickMul(x, N) : 1.0 / quickMul(x, -N);
}