1.快速幂
作用:可以快速求出
a
k
m
o
d
p
a^k mod p
akmodp的值,时间复杂度是
O
(
l
o
g
k
)
.
O( log k).
O(logk).
核心思路:反复平方法
①预处理出:
a
2
0
m
o
d
p
、
a
2
1
m
o
d
p
、
a
2
2
m
o
d
p
、
…
、
a
2
log
2
k
m
o
d
p
一共
l
o
g
2
k
个数
对于这预处理出的数,观察可以发现:
a
2
1
=
(
a
2
0
)
2
、
a
2
2
=
(
a
2
1
)
2
、
a
2
3
=
(
a
2
2
)
2
、
…
、
a
2
l
o
g
2
k
=
(
a
2
l
o
g
2
k
−
1
)
2
.
即每个数是前面一个数的平方
②对于
a
k
,可将
a
k
拆成
a
k
=
a
2
x
1
×
a
2
x
2
×
⋯
×
a
2
x
t
=
a
2
x
1
+
2
x
2
+
⋯
+
2
x
t
可得到
k
=
2
x
1
+
2
x
2
+
⋯
+
2
x
t
,即
k
为
l
o
g
2
k
个数之和
那么
a
k
m
o
d
p
=
(
a
2
x
1
m
o
d
d
)
∗
(
a
2
x
2
m
o
d
d
)
.
.
.
(
a
2
x
t
m
o
d
d
)
再由①中得到的每个数是前一个数的平方,故
a
k
m
o
d
p
=
(
a
2
x
1
m
o
d
d
)
∗
[
(
a
2
x
1
m
o
d
d
)
]
2
m
o
d
d
∗
.
.
.
∗
(
前一个结果的平方
m
o
d
d
)
③对于怎么得到
a
k
=
a
2
x
1
+
2
x
2
+
⋯
+
2
x
t
,简单,直接取
k
的二进制数。
如求
4
5
m
o
d
10
,则
k
=
(
101
)
2
,那么
4
5
=
4
(
101
)
2
=
4
2
0
×
4
2
2
而
4
2
0
m
o
d
10
=
4
,
4
2
1
m
o
d
10
=
4
2
m
o
d
10
=
6
,
4
2
2
m
o
d
10
=
6
2
m
o
d
10
=
6
,
(
这里就体现了后面的结果为前一个结果的平方再取模
)
得到最终结果即为
4
5
m
o
d
10
=
4
∗
6
m
o
d
10
=
4
①预处理出:a^{2^{0}} mod\ p、a^{2^{1}} mod\ p、a^{2^{2}} mod\ p、\dots、a^{2^{\log_{2}{k} }} mod\ p 一共log_{2}{k}个数\\ 对于这预处理出的数,观察可以发现:a^{2^{1}}=(a^{2^{0}})^2、a^{2^{2}}=(a^{2^{1}})^2、a^{2^{3}}=(a^{2^{2}})^2、\dots、a^{2^{log_{2}{k}}}=(a^{2^{log_{2}{k}-1}})^2.\\即每个数是前面一个数的平方\\ ②对于a^{k},可将 a^{k} 拆成 a^{k} = a^{2^{x_{1}}} \times a^{2^{x_{2}}} \times \dots \times a^{2^{x_{t}}} = a^{2^{x_{1}}+2^{x_{2}}+\dots+2^{x_{t}}}\\可得到k=2^{x_{1}}+2^{x_{2}}+\dots+2^{x_{t}}\ ,即k为log_{2}{k}个数之和\\那么a^{k} mod\ p=(a^{2^{x_{1}}}mod \ d)*(a^{2^{x_{2}}}mod \ d)...(a^{2^{x_{t}}}mod \ d)\\再由①中得到的每个数是前一个数的平方,故a^{k} mod\ p=(a^{2^{x_{1}}}mod \ d)*[(a^{2^{x_{1}}}mod \ d)]^2 mod\ d\ * ...*\\(前一个结果的平方mod\ d)\\\\③对于怎么得到a^{k}=a^{2^{x_{1}}+2^{x_{2}}+\dots+2^{x_{t}}},简单,直接取k的二进制数。\\ \\如求4^5 mod \ 10,则k=(101)_2,那么4^{5}=4^{(101)_{2}}=4^{2^{0}}\times 4^{2^{2}}\\而4^{2^{0}} mod\ 10=4,4^{2^{1}}mod\ 10=4^2 mod \ 10=6,4^{2^{2}}mod\ 10=6^2 mod \ 10=6,\\(这里就体现了后面的结果为前一个结果的平方再取模)\\得到最终结果即为4^5 mod \ 10=4*6 \ mod \ 10=4
①预处理出:a20mod p、a21mod p、a22mod p、…、a2log2kmod p一共log2k个数对于这预处理出的数,观察可以发现:a21=(a20)2、a22=(a21)2、a23=(a22)2、…、a2log2k=(a2log2k−1)2.即每个数是前面一个数的平方②对于ak,可将ak拆成ak=a2x1×a2x2×⋯×a2xt=a2x1+2x2+⋯+2xt可得到k=2x1+2x2+⋯+2xt ,即k为log2k个数之和那么akmod p=(a2x1mod d)∗(a2x2mod d)...(a2xtmod d)再由①中得到的每个数是前一个数的平方,故akmod p=(a2x1mod d)∗[(a2x1mod d)]2mod d ∗...∗(前一个结果的平方mod d)③对于怎么得到ak=a2x1+2x2+⋯+2xt,简单,直接取k的二进制数。如求45mod 10,则k=(101)2,那么45=4(101)2=420×422而420mod 10=4,421mod 10=42mod 10=6,422mod 10=62mod 10=6,(这里就体现了后面的结果为前一个结果的平方再取模)得到最终结果即为45mod 10=4∗6 mod 10=4
Acwing 875.快速幂
具体实现代码(详解版):
#include <iostream>
using namespace std;
typedef long long LL;
// 快速幂函数:计算 m^k % p
LL qmi(int m, int k, int p) {
// res 初始化为 1 % p,确保即使 p == 1 也能正常工作(防止 p = 1 时除 0 错误)
int res = 1 % p;
int t = m; // t 是当前的底数,从 m 开始
// 当 k 不为 0 时,继续循环
while (k) {
// 如果 k 的最低位是 1,意味着当前需要将 t 乘到结果中
if (k & 1)
res = (LL)res * t % p; // 计算 (res * t) % p,并更新 res
// 无论 k 的最低位是否为 1,都要将 t 平方,并取模 p
t = (LL)t * t % p;
// 右移 k,去掉最低位,这相当于将 k 除以 2
k >>= 1;
}
// 返回最终的结果,即 m^k % p
return res;
}
int main() {
int n;
cin >> n;
while (n--) {
int m, k, p;
cin >> m >> k >> p;
cout << qmi(m, k, p) << endl; // 输出 m^k % p 的结果
}
return 0;
}
2.快速幂求逆元
Acwing 快速幂求逆元
实现思路:
- 由(a/b)mod m恒等a*x mod m===>
(b*x) mod m = 1
,x为b的逆元 - 结合费马定理(欧拉函数的应用):b与m互质,且m为质数,则b^(m-1) mod m=1;
- 上面两式结合:得b的逆元b^(m-2),则模m乘法逆元
x=b^(m-2)%m
,本质上就是求快速幂,但多了一个要求:b和m互质 - 注意:当b和m不互质时,无解;否则必存在逆元
具体实现代码(详解版):
#include <iostream>
using namespace std;
typedef long long LL; // 定义 LL 为 long long 类型,用于处理大整数
// 快速幂函数:计算 m^k % p
LL qmi(int m, int k, int p) {
// res 初始化为 1 % p,确保即使 p == 1 也能正常工作(防止 p = 1 时除 0 错误)
int res = 1 % p;
int t = m; // t 是当前的底数,从 m 开始
// 当 k 不为 0 时,继续循环
while (k) {
// 如果 k 的最低位是 1,意味着当前需要将 t 乘到结果中
if (k & 1)
res = (LL)res * t % p; // 计算 (res * t) % p,并更新 res
// 无论 k 的最低位是否为 1,都要将 t 平方,并取模 p
t = (LL)t * t % p;
// 右移 k,去掉最低位,这相当于将 k 除以 2
k >>= 1;
}
// 返回最终的结果,即 m^k % p
return res;
}
int main() {
int n;
cin >> n;
while (n--) {
int m, p;
cin >> m >> p; // 读取基数 m 和模数 p
// 根据费马小定理计算 m 的模逆元,公式为 m^(p-2) mod p
int res = qmi(m, p - 2, p);
// 检查 m 是否与 p 互质(即 m % p 不为 0)
if (m % p)
cout << res << endl; // 如果互质,输出结果
else
puts("impossible"); // 如果不互质,输出 "impossible"
}
return 0;
}
快速幂的应用: