【算法学习笔记】33:快速幂的递归及循环实现
快速幂原理
要计算 a b a^b ab, a b m o d p a ^ b~mod~p ab mod p,可以考虑用折半的方式缩小计算量。
例如要计算 2 13 2^{13} 213,只要计算 2 6 2^6 26乘以自己,再乘以一个多出来的2。
而要计算 2 6 2^6 26,只要计算 2 3 2^3 23乘以自己。
而要计算 2 3 2^3 23,只要计算 2 1 2^1 21乘以自己,再乘以一个多出来的2。
也就是每次把
b
b
b折半下取整,当
b
b
b是偶数时:
a
b
=
a
b
2
⋅
a
b
2
a^b = a^{\frac{b}{2}} \cdot a^{\frac{b}{2}}
ab=a2b⋅a2b
当
b
b
b是奇数时只要多乘以一个
a
a
a:
a
b
=
a
b
2
⋅
a
b
2
⋅
a
a^b = a^{\frac{b}{2}} \cdot a^{\frac{b}{2}} \cdot a
ab=a2b⋅a2b⋅a
如果需要对 p p p取模,只要在每一步乘法之后取模就可以了。
例题:AcWing 875. 快速幂
递归实现
从前面的原理很容易得到如下的递归实现。
#include <iostream>
using namespace std;
typedef unsigned long long ULL;
ULL qmi(ULL a, ULL b, ULL p) {
if (b == 1) return a % p;
ULL res = qmi(a, b >> 1, p);
res = res * res % p;
if (b & 1) res = res * a % p;
return res;
}
int main() {
int t; cin >> t;
while (t -- ) {
ULL a, b, p; cin >> a >> b >> p;
cout << qmi(a, b, p) << endl;
}
return 0;
}
循环实现
从前面的原理理解循环实现不直观。可以考虑 b b b的二进制表示。例如 b = 0 b 1101 b = 0b1101 b=0b1101也就是 2 3 + 2 2 + 2 0 = 13 2^3+2^2+2^0=13 23+22+20=13,所以:
a 2 3 + 2 2 + 2 0 = a 2 3 ⋅ a 2 2 ⋅ a 2 0 a^{2^3 + 2^2 + 2^0} = a^{2^3} \cdot a^{2^2} \cdot a^{2^0} a23+22+20=a23⋅a22⋅a20
这正是因为 b b b的从低到高0、2、3二进制位置是1导致的。
又因为
(
a
2
i
)
2
=
a
2
i
+
1
(a^{2^i})^2 = a^{2^{i+1}}
(a2i)2=a2i+1
因此只需要循环的从最低位(第0位)开始,每次通过把
a
a
a和自己相乘计算一下
a
2
i
a^{2^i}
a2i,只要
b
b
b的相应二进制位置是1,就把这个数乘进res
里即可。
#include <iostream>
using namespace std;
typedef unsigned long long ULL;
ULL qmi(ULL a, ULL b, ULL p) {
ULL res = 1;
while (b) {
if (b & 1) res = res * a % p;
b >>= 1;
a = a * a % p;
}
return res;
}
int main() {
int t; cin >> t;
while (t -- ) {
ULL a, b, p; cin >> a >> b >> p;
cout << qmi(a, b, p) << endl;
}
return 0;
}
原文地址:https://blog.csdn.net/SHU15121856/article/details/145169364
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!