定义
定义:如果 a ∗ x = 1 ( m o d p ) , 且 g c d ( a , p ) = 1 a*x = 1(mod p),且gcd(a, p) = 1 a∗x=1(modp),且gcd(a,p)=1(等价于a,p互质),那 x 就为模 p 意义下 a 的乘法逆元。
用途
小定理:
(
a
/
b
)
%
p
=
(
a
∗
x
)
%
p
=
1
(a/b) \% p = (a*x) \%p= 1 % p
(a/b)%p=(a∗x)%p=1;(x为模p意义下a的逆元)
证明:
a
/
b
∗
b
∗
x
≡
a
∗
x
(
m
o
d
p
)
且
b
∗
x
=
1
(
m
o
d
p
)
a/b * b * x\equiv a * x (mod~p) 且b * x = 1(mod p)
a/b∗b∗x≡a∗x(mod p)且b∗x=1(modp);
将除法转换成了乘法,就可以直接取模了。
算法
扩展欧几里得求逆元
a
∗
x
≡
1
(
m
o
d
p
)
a*x \equiv1(mod ~p)
a∗x≡1(mod p)
扩展欧几里德算法
在这篇博客举的例子就是关于上面这个公式的求解。
贴份代码。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void exgcd(ll a, ll b, ll &x, ll &y) {
if(!b) {
x = 1, y = 0;
return ;
}
exgcd(b, a%b, x, y);
ll temp = x;
x = y;
y = temp - a/b*y;
}
int main() {
ll n, m, x, y;
scanf("%lld%lld", &n, &m);
exgcd(n, m, x, y);
printf("%lld", (x%m + m)%m);
return -0;
}
费马小定理或欧拉定理
费马小定理:
p
是
素
数
,
a
是
整
数
,
g
c
d
(
a
,
p
)
≡
1
(
m
o
d
p
)
=
>
a
p
−
1
≡
1
(
m
o
d
p
)
p是素数,a是整数,gcd(a,p) \equiv1(mod ~p)=>a^{p-1}\equiv1(mod ~p)
p是素数,a是整数,gcd(a,p)≡1(mod p)=>ap−1≡1(mod p)
逆元定义:
a
∗
x
=
1
(
m
o
d
p
)
,
且
g
c
d
(
a
,
p
)
=
1
=
>
x
是
a
在
模
p
下
的
逆
元
a *x = 1(mod p),且gcd(a, p) = 1=>x是a在模p下的逆元
a∗x=1(modp),且gcd(a,p)=1=>x是a在模p下的逆元
可以看出
a
p
−
2
a^{p-2}
ap−2就是a在模p下的逆元
求个快速幂就行了。
ll Power(ll x, ll y) {
ll n = y, ans = 1, res = x;
while(n){
if(n & 1) ans = ans * res % mod;
n >>= 1;
res = res * res % mod;
}
return ans%mod;
}
例题:Walk
题解:Walk–求组合数
用阶乘的乘除法求组合数,除法取模要用到逆元。
费马小定理只适用于p为素数的时候,当p不是素数时就可以用上欧拉定理了。
欧拉定理(Euler’s theorem):
g
c
d
(
a
,
p
)
=
1
=
>
a
φ
(
p
)
≡
1
(
m
o
d
p
)
gcd(a, p) = 1=>a^{\varphi(p)}\equiv1(mod~p)
gcd(a,p)=1=>aφ(p)≡1(mod p)
其中
φ
(
p
)
\varphi(p)
φ(p)表示的是小于p且和p互质的数的个数。假如p是质数,那么
φ
(
p
)
\varphi(p)
φ(p)就等于
p
−
1
p-1
p−1,也就是上面的费马定理。
欧拉函数
φ
(
p
)
\varphi(p)
φ(p)是有通式的(还不会),先贴份链接 欧拉函数的定义与计算
公式递推
用来求
已知
1
的
逆
元
1
−
1
=
1
1的逆元1^{-1}=1
1的逆元1−1=1,我们将逆元存在inv数组中,inv[1] = 1。
设
k
=
p
/
i
,
r
=
p
%
i
k=p/i,r=p\%i
k=p/i,r=p%i
k
∗
i
+
r
=
p
=
>
k
∗
i
+
r
≡
0
(
m
o
d
p
)
k*i+r=p=>k*i+r\equiv0(mod~p)
k∗i+r=p=>k∗i+r≡0(mod p),
在左右同乘
i
−
1
i^{-1}
i−1和
r
−
1
=
>
k
∗
r
−
1
+
i
−
1
=
0
r^{-1}=>k*r^{-1}+i^{-1}=0
r−1=>k∗r−1+i−1=0
所以
i
−
1
=
−
k
∗
r
−
1
<
=
>
i
−
1
=
−
(
p
/
i
)
∗
(
p
%
i
)
−
1
i^{-1} = -k*r^{-1}<=>i^{-1}=-(p/i)*(p\%i)^{-1}
i−1=−k∗r−1<=>i−1=−(p/i)∗(p%i)−1
(
p
/
i
)
(p/i)
(p/i)是一个实数,
(
p
%
i
)
−
1
(p\%i)^{-1}
(p%i)−1是小于在第i个逆元之前的逆元,按递推可以求得。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 100007;
ll inv[N] = {0, 1};
int main() {
for(int i=2; i<=N; i++) {
inv[i] = -(N/i) * inv[N%i];//递推关系
inv[i] = (inv[i] % N + N) % N;//确保inv[i] > 0;
}
for(int i=0; i<100; i++) {
cout << inv[i] << ' ';
}
return 0;
}