快速幂
标准幂
在编程中我们往往会遇到幂的运算,正常情况下许多初学者会用以下两者方式计算(ab)
1.使用for循坏进行幂运算
点击查看代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ll a, b, ans = 1;
cin >> a >> b;
for (int i = 1; i <= b; ++i) {
ans *= a;
}
cout << ans;
return 0;
}
2.直接调用pow()函数进行幂运算
点击查看代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ll a, b;
cin >> a >> b;
cout << pow(a , b);
return 0;
}
但实际情况下,在计算大数据的题目时,由于两者的时间复杂度都为O(n),我们一般都是会T的(TLE,即运行超时),这时我们应当考虑快速幂了
快速幂
1.原理
我们先假设需要计算 211,这时我们可以将其分解为 2(8+2+1) = 28 * 22 * 21。这时它的时间复杂度就会大大缩减成O(log2n)
(1)幂次与二进制的关系
其中 21,22,24,28…… 的幂次都是2的倍数,所有幂2i都是倍乘关系,可以进行逐级递推
(2)幂次用二进制分解
学过二进制的都知道,11的二进制为1011,二进制换算成十进制时的展开式为 28 + 22 + 21,问题迎刃而解
(3)跳过没有的幂次
24 如何处理?只需做一个判断,这时候我们可以使用二进制的位运算实现,即
b & 1,取 b 的二进制的最后一位数,判断这一位是否需要跳过
b >>= 1,把 b 右移一位,此目的为把刚处理过的 b 的二进制的最后一位去掉
(4)代码实现
点击查看代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ll a, b, ans = 1;
cin >> a >> b;
while (b) {
if (b & 1) ans *= a;
a *= a;
b >>= 1;
}
cout << ans;
return 0;
}
当然,实际情况下幂运算结果往往是一个大数,题目一般会要求取模后在输出。不过不能对结果直接进行取模,因为结果可能早已超出 long long 的数据范围。我们需要用到取模的性质 ab % mod n = (a % n)b mod n,则代码可为
点击查看代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ll a, b, ans = 1, n = 1000000007;
cin >> a >> b;
a %= n;
while (b) {
if (b & 1) ans = (ans * a) % n;
a = (a * a) % n;
b >>= 1;
}
cout << ans;
return 0;
}
不过你要注意,写代码时应该要让主代码简便,所以我们需要把快速幂变为一个函数,使用时调用即可,以下为上述两种代码的函数
点击查看代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
ll fastPow1(ll a, ll b) {
ll ans = 1;
while (b) {
if (b & 1) ans *= a;
a *= a;
b >>= 1;
}
return ans;
}
ll fastPow2(ll a, ll b, ll n) {
ll ans = 1;
a %= n;
while (b) {
if (b & 1) ans = (ans * a) %n;
a = (a * a) % n;
b >>= 1;
}
return ans;
}
int main() {
ll a, b, n = 1000000007;
cin >> a >> b;
cout << fastPow1(a, b) << ' ' << fastPow2(a, b, n);
return 0;
}
(5)代码理解
以 a = 2,b = 11 为例
b(原:11) | a(原:2) | ans(原:1) |
---|---|---|
11 | 4 | 2 |
5 | 16 | 8 |
2 | 256 | 8 |
1 | 65536 | 2048 |
表格第一行:
- b = 11 时,即 while(11) 为true;
- 11 的二进制为 1011,1011 位于上 1,可得 1(即 1011 位于 0001),即 if(1) 为 true,ans *= a 执行,ans 为2;
- a *= a 执行,a = 4;
- b >= 1 执行,b = 5(二进制为 101);
表格第二行:
- b = 5 时,即 while(5) 为true;
- 5 的二进制为 101,101 位于上 1,可得 1,即 if(1) 为 true,ans *= a 执行,ans 为8;
- a *= a 执行,a = 16;
- b >= 1 执行,b = 2(二进制为 10);
表格第三行:
- b = 2 时,即 while(2) 为true;
- 2 的二进制为 10,10 位于上 1,可得 0,即 if(0) 为 false,ans *= a 不执行;
- a *= a 执行,a = 256;
- b >= 1 执行,b = 1(二进制为 1);
表格第四行:
- b = 1 时,即 while(1) 为true;
- 1 的二进制为 1,1 位于上 1,可得 1,即 if(1) 为 false,ans *= a 执行,ans 为2048;
- a *= a 执行,a = 65536,不过已经没什么用了;
- b >= 1 执行,b = 0(二进制为 0),下次循坏 while(0) 为 false,程序结束;
由此可知,在处理没有的幂次时,直接跳过 ans *= a 这一条语句,直接执行 a *= a 这一语句,这样就能实现 a2 直接变为 a8 了
而 b >= 1 这一语句则能完美实现把处理过的位数删去,代码就这样水灵灵的出来了
(6)注意
- 如果遇到特别大的数(a 特别大,超出 long long 的范围),快速幂无法计算,只能用高精度了,当然没也可以用 Python,只要你不怕T;
- 快速幂使用任何求幂,除了特别大的数;
- 写代码时需要谨慎,用快速幂的函数替代 pow() 函数;
我只是分享了一下学习心得,共给大家查阅,如有不妥处,欢迎大家指正꒰ᐢ⸝⸝•༝•⸝⸝ᐢ꒱