在处理一些数论题目的时候,我们经常有的时候会遇到一些高指数的问题,就比如在判断素数的时候所使用的ML素性判定(传送门待完善)
快速取幂的原理其实并不复杂,我们以pow(2,10)为例
一般的计算就是
$$2*2*2*2*2*2*2*2*2*2=1024$$
另一种就是使用了分治法的思想
$$2*2*2*2*2*2*2*2*2*2=4*4*4*4*4=4*16*16=64*16=1024$$
而使用了二进制方式的快速幂则更为巧妙
将10转换为二进制以后可以得到
0000 0000 0000 0000 0000 0000 0000 1010
我们可以发现\( pow(2,10) \)其实可以体现出来是 \( pow(2,8)*pow(2,2) \) ,对应到数位中也就是第二位和第四位,也就是说我们只需要在二进制位为1处进行一次乘的操作即可
而取余的性质则如下
$$(a + b) \% p = (a \% p + b \% p) \% p $$
$$(a - b) \% p = (a \% p - b \% p) \% p $$
$$(a * b) \% p = (a \% p * b \% p) \% p $$
$$(a ^ b) \% p = ((a \% p)^b) \% p $$
在快速幂的模板中稍加更改就可以实现在每一步操作中取余的目的
模板如下
/* * @Author: Gehrychiang * @LastEditors : Please set LastEditors * @Website: www.yilantingfeng.site * @E-mail: gehrychiang@aliyun.com */ #include <bits/stdc++.h> using namespace std; long long fastMul(long long a, long long b, long long mod) { long long res = 0; while (b) { if (b & 1) { res = (res + a) % mod; } a = (a + a) % mod; b >>= 1; } return res; } long long fastPowMod(long long a, long long n, long long mod) //long long fastPow(long long a, long long n) { if (n == 0 && a != 0) return 1; if (n != 0 && a == 0) return 0; long long base = a; long long res = 1; while (n) { if (n & 1) { res = fastMul(res, base, mod); //res*=base; } base = fastMul(base, base, mod); //base*=base; n >>= 1; } return res % mod; //return res; } int main() { long long b, q, k; while (cin >> b >> q >> k) { long long ans = fastPowMod(b, q, k); printf("%lld\n",ans); } return 0; }
Update:
更新了使用快速乘的模板,可以有效防止在唯一的一步乘法运算中溢出long long范围