快速幂是优化的幂运算算法。
快速幂取模是快速幂+同余定理。
一、暴力幂运算
考虑计算 311,可以让3累乘11次,复杂度是O(n)
int Pow1(int a, int b) {
int ans = 1;
for (int i = 0; i < b; i++) ans *= a;
return ans;
}
int main() {
cout << Pow1(3, 11) << endl;
return 0;
}
输出:
177147
二、快速幂
以 311 举例。把指数11写成二进制形式是1011
,所以
11 = 23 + 0 × 22 + 21 + 20
所以 311 又可以写成:
311 =3^ (23 + (0 × 22) + 21 + 20)
拆括号得:
311 = 38 × (0 × 34) × 32 × 31
可以发现乘法因子是以平方的形式增长的,比如 32 等于 31 的平方,38 等于 34 的平方。
由此规律,可以写出快速幂算法了。
我们先定义一个ans=1
来保存答案,然后从低到高遍历指数b的每一个二进制位,同时我们让底数a随着循环每次做一个平方,a, a2, a4, a8,…。
可以写出快速幂的框架:
int Pow(int a, int b) {
int ans = 1;
while (b) {
...
a *= a;
b >>= 1;
}
return ans;
}
因为11是1011,311 = 38 × (0 × 34) × 32 × 31,可以发现,当二进制位等于1时,ans要累乘一次a。因此完整代码如下:
#include <iostream>
using namespace std;
int Pow(int a, int b) {
int ans = 1;
while (b) {
//因为1的二进制是0000 0001,所以 b & 1 为真就意味着 b 的最低位是1。
if (b & 1) ans *= a;
a *= a;
b >>= 1;
}
return ans;
}
int main() {
cout << Pow(3, 11) << endl;
return 0;
}
以上就是快速幂算法的分析。
三、快速幂取模
快速幂取模可以用来解决 “1145141919810 除以6的余数等于多少”或者是“230的后6位是多少”之类的问题。它是在快速幂的基础上,结合了同余定理。
#include <iostream>
using namespace std;
int Pow(int a, int b, int c) {
int ans = 1;
while (b) {
if (b & 1) ans = ans * a % c;
a = a * a % c;
b >>= 1;
}
return ans;
}
int main() {
cout << Pow(114514, 1919810, 6) << endl;
cout << Pow(2, 30, 1000000) << endl;
return 0;
}
输出:
4
741824