文章目录
超级详细的基础算法和数据结构合集:
https://blog.csdn.net/GD_ONE/article/details/104061907
摘要
本文主要介绍快速幂算法,快速幂虽然代码简单,但是往往会与其他算法相结合,很重要。
引言
当我们计算
n
k
n^k
nk时,常用的做法是对
n
n
n连乘
k
k
k次, 但如果
k
k
k特别大,假如
k
=
1
e
6
k = 1e6
k=1e6, 如果仍然对
n
连
乘
1
e
6
n连乘1e6
n连乘1e6次的话,时间消耗就太大了。那么我们如何
在短时间内求出一个数的
k
k
k次方呢。
求 n k n^k nk
我们可以考虑对朴素方法进行优化。
1:当我们计算得到
n
2
n^2
n2时, 我们可以直接使用
n
2
n^2
n2连乘
k
/
2
k/2
k/2次。 这样做时间复杂度变为了
O
(
n
/
2
)
O(n/2)
O(n/2)
2:有人可能会问,我们既然可以用
n
2
连
乘
k
/
2
n^2连乘k/2
n2连乘k/2次,为什么不用
n
4
n^4
n4连乘
k
/
4
k/4
k/4次呢,当然可以这样做之后时间复杂度变为了
O
(
n
/
4
)
O(n/4)
O(n/4);
3 : 我们当然也可以
n
8
,
n
16
.
.
.
连
乘
n^8,n^{16}...连乘
n8,n16...连乘
4:所以我们可以用:
n
1
∗
n
2
∗
n
4
∗
n
8
.
.
.
n
p
n^1 * n^2 * n^ 4 *n^8...n^p
n1∗n2∗n4∗n8...np
快速幂就是类似的思想。
快速幂
先举个例子:
3
11
=
3
1
∗
3
1
∗
3
1
∗
3
1
∗
3
1
∗
3
1
∗
3
1
∗
3
1
∗
3
1
∗
3
1
∗
3
1
3^{11} = 3^1* 3^1 *3^1*3^1*3^1*3^1*3^1*3^1*3^1*3^1*3^1
311=31∗31∗31∗31∗31∗31∗31∗31∗31∗31∗31
11
的
二
进
制
表
示
为
:
1011
11的二进制表示为: 1011
11的二进制表示为:1011
怎么把一个二进制数转化为十进制数呢?
计算过程是:
11
=
1
∗
2
0
+
1
∗
2
1
+
0
∗
2
2
+
1
∗
2
3
=
1
+
2
+
0
+
8
11 = 1*2^0 + 1 * 2^1 + 0 * 2^2+ 1*2^3 = 1 + 2 + 0 + 8
11=1∗20+1∗21+0∗22+1∗23=1+2+0+8
我们可以发现:如果将最开始求
3
11
3^{11}
311变为:
3
11
=
3
1
∗
3
2
∗
3
0
∗
3
8
3^{11} = 3^1 * 3^2 * 3^0*3^8
311=31∗32∗30∗38, 计算量将会大大减少,原本需要乘11次,现在只需要乘4次。
另外,我们需要判断二进制数的某一位是不是0,我们可以使用位运算方便的解决该问题。
快速幂代码:
public static int qmi(int a, int b){ //求 a^b
int res = 1; // res保存结果
while(b != 0){
if((b & 1) == 1){ //如果k的二进制数的最后一位是 1。 比如1011 & 1 = 1
res = (res * a) % mod;//取模, 防止结果溢出。
}
a = a * a % mod;//得到 a^1, a^2, a^4, a^8, .....
b = b >> 1; //将b右移一位,去掉最低位。为了开始判断下一位。
}
return res;
}
代码很简单,多看几遍就懂了。如果对位运算不熟悉可以翻看之前的博客。