逆元的三种求法
费马小定理,扩展欧几里得,递推求阶乘逆元
逆元
对于一个实数
A
A
A 如果存在一个
x
x
x 使得
A
x
=
1
Ax = 1
Ax=1,我们就把这个
x
x
x 叫做
A
A
A 的逆元,记做
x
=
A
−
1
x = A^{-1}
x=A−1。
在一般数学中,我们所说的逆元就是倒数。
但是在数论中,如果一个数字 A A A 存在一个对 p p p 的逆元 x x x,就可以写成 A x ≡ 1 m o d p Ax≡1\ mod\ p Ax≡1 mod p 的形式(此处 p p p 与 A A A 互质,若不互质,则不存在逆元)。
逆元的作用
我们知道 取余 的性质:
- ( a − b ) % c = ( a % c − b % c ) % c (a - b)\%c = (a\%c - b\%c)\%c (a−b)%c=(a%c−b%c)%c
- ( a + b ) % c = ( a % c + b % c ) % c (a + b)\%c = (a\%c+b\%c)\%c (a+b)%c=(a%c+b%c)%c
- ( a × b ) % c = ( a % c × b % c ) % c (a\times b)\%c=(a\%c\times b\%c)\%c (a×b)%c=(a%c×b%c)%c
对于基本的四种运算而言,加减乘都符合“分配率”,唯独除法不满足。
( a ÷ b ) % c = ( a % c ÷ b % c ) % c (a\div b)\%c=(a\%c\div b\%c)\%c (a÷b)%c=(a%c÷b%c)%c
上面这种运算是错误的!
如果要实现这种运算,就要把除法转化为乘法,假设
b
−
1
b^{-1}
b−1 是
b
b
b 关于
c
c
c 的逆元。
(
a
÷
b
)
%
c
(a\div b)\%c
(a÷b)%c 可以转化为
(
a
×
b
−
1
)
%
c
=
(
a
%
c
×
b
−
1
%
c
)
%
c
(a\times b^{-1})\%c=(a\%c\times b^{-1}\%c)\%c
(a×b−1)%c=(a%c×b−1%c)%c。
逆元求法
费马小定理
费马小定理:假设
p
p
p 是一个质数,且
g
c
d
(
a
,
p
)
=
1
gcd(a, p) = 1
gcd(a,p)=1,那么
a
p
−
1
≡
1
m
o
d
p
a^{p-1}≡1\ mod\ p
ap−1≡1 mod p。
我们也可以的得到一个费马小定理的特例:假设
a
a
a 是一个整数,且
g
c
d
(
a
,
p
)
=
1
gcd(a, p) = 1
gcd(a,p)=1,那么
a
p
−
1
≡
1
m
o
d
p
a^{p-1}≡1\ mod\ p
ap−1≡1 mod p。
根据费马小定理
a
p
−
1
≡
1
m
o
d
p
a^{p-1}≡1\ mod\ p
ap−1≡1 mod p ,可以发现
a
p
−
2
×
a
≡
1
m
o
d
p
a^{p-2}\times a≡1\ mod\ p
ap−2×a≡1 mod p 也成立。
是不是很像上面说到的逆元的形式:
A
x
≡
1
m
o
d
p
Ax≡1\ mod\ p
Ax≡1 mod p,
x
x
x 是
A
A
A 关于
p
p
p 的逆元。
那根据费马小定理也可得知
a
p
−
2
a^{p-2}
ap−2 是
a
a
a 关于
p
p
p 的逆元。
所以求
a
a
a 的逆元,就直接用快速幂求
a
p
−
2
a^{p-2}
ap−2 就可以了。
LL power(LL a, int x) {
LL ans = 1;
while(x) {
if(x&1) ans = (ans * a) %mod;
a = (a * a) %mod;
x >>= 1;
}
return ans;
}
LL inv(LL a) {
return power(a, mod - 2);
}
扩展欧几里得
扩展欧几里得:
a
x
+
b
y
=
g
c
d
(
a
,
b
)
ax +by=gcd(a,b)
ax+by=gcd(a,b) 的解一定存在。
当我们要求
a
a
a 关于
p
p
p 的逆元时,若逆元存在,则
g
c
d
(
a
,
p
)
=
1
gcd(a,p)=1
gcd(a,p)=1。假设逆元为
x
x
x,即:
a
x
≡
1
m
o
d
p
ax ≡ 1\ mod\ p
ax≡1 mod p。
我们可以展开一下变成
a
x
=
1
+
p
k
ax = 1 + pk
ax=1+pk,由于
k
k
k 可正可负。
所以我们可以得到
a
x
+
p
k
=
1
ax + pk=1
ax+pk=1,其实就是
a
x
+
p
k
=
g
c
d
(
a
,
p
)
ax + pk= gcd(a,p)
ax+pk=gcd(a,p)。
所以我们用扩展欧几里得求出一个最小的
x
x
x 就是
a
a
a 关于
p
p
p 的一个逆元啦。
我们来试着解这个欧几里得吧!
现在已经有了
a
x
+
b
y
=
g
c
d
(
a
,
b
)
ax + by=gcd(a,b)
ax+by=gcd(a,b) 了。我们想试着求出一个特解
x
x
x。
根据欧几里得算法我们可以知道 g c d ( a , b ) = g c d ( b , a % b ) gcd(a,b)=gcd(b,a\%b) gcd(a,b)=gcd(b,a%b)。
而且我们可以看出
b
x
+
(
a
%
b
)
y
=
g
c
d
(
b
,
a
%
b
)
bx+(a\%b)y=gcd(b,a\%b)
bx+(a%b)y=gcd(b,a%b)
由此我们可得:(由于两边的
x
x
x,
y
y
y 值不同,我们用
x
′
x'
x′ 和
y
′
y'
y′ 进行区分)
b
x
′
+
(
a
%
b
)
y
′
=
a
x
+
b
y
bx'+(a\%b)y'\ =\ ax + by
bx′+(a%b)y′ = ax+by
我们想要把式子化简一下,可以从
a
%
b
a\%b
a%b 入手,即
a
%
b
=
a
−
⌊
a
b
⌋
×
b
a\%b\ =\ a-\lfloor\frac{a}{b}\rfloor \times b
a%b = a−⌊ba⌋×b。
所以我们可以化简得到:
a
x
+
b
y
=
b
x
′
+
(
a
−
⌊
a
b
⌋
b
)
y
′
ax + by\ =\ bx'+(a-\lfloor\frac{a}{b}\rfloor b)y'
ax+by = bx′+(a−⌊ba⌋b)y′
移项:
a
x
+
b
y
=
a
y
′
+
b
(
x
′
−
⌊
a
b
⌋
y
′
)
ax + by=ay'+b(x'-\lfloor\frac{a}{b}\rfloor y')
ax+by=ay′+b(x′−⌊ba⌋y′)
系数相等,所以我们可以解得
{
x
=
y
′
y
=
(
x
′
−
⌊
a
b
⌋
y
′
)
\begin{cases} x=y'\\ y=(x'-\lfloor\frac{a}{b}\rfloor y')\\ \end{cases}
{x=y′y=(x′−⌊ba⌋y′)
根据欧几里得算法,我们一直递归下去,总会到要一个最终位置的
a
%
b
=
0
a\%b=0
a%b=0 。
所以式子变成了
a
x
=
g
c
d
(
a
,
b
)
ax=gcd(a,b)
ax=gcd(a,b)。此时我们取一个特解
x
=
1
x=1
x=1,
y
=
0
y=0
y=0。然后往回推,就可以得到一开始的那个
x
x
x。
此时解出来的
x
x
x 就是
a
a
a 关于
p
p
p 的一个逆元。
void exgcd(LL a, LL b, LL &x, LL &y) {
if (b == 0) {
x = 1;y = 0;
} else {
exgcd(b, a%b, y, x);
y -= (a/b) * x;
}
}
递推求阶乘逆元。
我们经常会用到阶乘的逆元,我们可以考虑用费马小定理先求出最大那个阶乘的逆元,然后再往回推,直接看代码再解释。
void init() {
fact[0] = 1;
for (int i = 1; i < maxn; i++) {
fact[i] = fact[i - 1] * i %mod;
}
inv[maxn - 1] = power(fact[maxn - 1], mod - 2);
for (int i = maxn - 2; i >= 0; i--) {
inv[i] = inv[i + 1] * (i + 1) %mod;
}
}
我们可以假设把
n
!
n!
n! 的逆元表示为
[
n
!
]
−
1
[n!]^{-1}
[n!]−1。
我们要求
(
n
−
1
)
!
(n-1)!
(n−1)! 的逆元,我们可以考虑给
(
n
−
1
)
!
(n-1)!
(n−1)! 乘上一个
n
n
n 把他变为
n
!
n!
n!。
即
(
n
−
1
)
!
×
n
[
n
!
]
−
1
≡
1
m
o
d
p
(n-1)!\times n[n!]^{-1}≡1\ mod\ p
(n−1)!×n[n!]−1≡1 mod p
因此
n
[
n
!
]
−
1
n[n!]^{-1}
n[n!]−1 是
(
n
−
1
)
!
(n-1)!
(n−1)! 关于
p
p
p 的一个逆元。