目录
- 零、前言
- 一、逆元
- 二、扩展欧几里得算法
- 三、费马小定理
- 四、总结
零、前言
距离25年蓝桥杯还有大概三个月时间,接下来重点应该会放在蓝桥杯备考方向,一起努力,一起加油
一、逆元
什么是逆元?这是数论中的一个基本概念。如果存在
a
x
≡
1
(
m
o
d
n
)
ax\equiv1(mod \ n)
ax≡1(mod n)则
x
x
x为
a
a
a在模
n
n
n意义下的逆元。可以简单地理解为,求一个
x
x
x使得
a
x
m
o
d
n
=
1
ax\ mod\ n=1
ax mod n=1
逆元的意义在哪呢?如果遇到需要求
a
/
b
m
o
d
n
a/b\ mod\ n
a/b mod n的情况,则可以转化为分子a乘以分母b的逆元的形式,这样我们将除法转化为乘法,提高了计算效率。
二、扩展欧几里得算法
那么怎么求解逆元呢?由前面给出的公式
a
x
≡
1
(
m
o
d
n
)
ax\equiv1(mod \ n)
ax≡1(mod n),不难看出求逆元
x
x
x就是求解等式的解:
a
x
+
n
y
=
1
ax + ny = 1
ax+ny=1因为
(
a
x
+
n
y
)
m
o
d
n
=
a
x
m
o
d
n
(ax+ny)\ mod\ n = ax\ mod\ n
(ax+ny) mod n=ax mod n,求出了
x
x
x和
y
y
y,那么
x
x
x就是
a
a
a的逆元。
为了方便推导,我们暂时把
n
n
n换成
b
b
b,等号右边换位
m
m
m,求解的更一般的形式。
a
x
+
b
y
=
m
ax+by=m
ax+by=m根据裴蜀定理:对于任意整数
a
a
a,
b
b
b,
m
m
m,求解未知整数
x
x
x,
y
y
y时,必须满足
g
c
d
(
a
,
b
)
∣
m
gcd(a,b)|m
gcd(a,b)∣m,即
m
m
m是
g
c
d
(
a
,
b
)
gcd(a,b)
gcd(a,b)的倍数时才有解。
所以我们将计就计,如果
m
m
m是
g
c
d
(
a
,
b
)
gcd(a,b)
gcd(a,b)的一倍,也就是
m
=
g
c
d
(
a
,
b
)
m = gcd(a,b)
m=gcd(a,b),先解出方程
a
x
+
b
y
=
g
c
d
(
a
,
b
)
ax+by=gcd(a,b)
ax+by=gcd(a,b)的解
x
1
x_1
x1,
y
1
y_1
y1,然后两边乘以
m
/
g
c
d
(
a
,
b
)
m/gcd(a,b)
m/gcd(a,b),不就能求出来我们想要的了吗?
a
m
x
1
g
c
d
(
a
,
b
)
+
b
m
y
1
g
c
d
(
a
,
b
)
=
m
a\frac{mx_1}{gcd(a,b)}+b\frac{my_1}{gcd(a,b)}=m
agcd(a,b)mx1+bgcd(a,b)my1=m所以,我们进一步研究问题:解出方程
a
x
+
b
y
=
g
c
d
(
a
,
b
)
ax+by=gcd(a,b)
ax+by=gcd(a,b)的解。
那么我们需要求出三个东西,
x
x
x,
y
y
y,
g
c
d
(
a
,
b
)
gcd(a,b)
gcd(a,b),这就是扩展欧几里得算法的根本研究问题。(By the way,欧几里得算法就是求解
g
c
d
(
a
,
b
)
gcd(a,b)
gcd(a,b),方法就是我们熟知的辗转相除法。)
当
b
=
0
b=0
b=0,有特解
x
=
1
,
y
=
0
x=1,y = 0
x=1,y=0,这是迭代的初始条件。
当
b
≠
0
b\neq0
b=0,假设
x
1
x_1
x1,
y
1
y_1
y1是
a
x
1
+
b
y
1
=
g
c
d
(
a
,
b
)
ax_1+by_1=gcd(a,b)
ax1+by1=gcd(a,b)的解,根据欧几里得算法:
g
c
d
(
a
,
b
)
=
g
c
d
(
b
,
a
m
o
d
b
)
gcd(a,b) = gcd(b,a\ mod\ b)
gcd(a,b)=gcd(b,a mod b),下一步我们构造
b
x
2
+
(
a
m
o
d
b
)
y
2
=
g
c
d
(
b
,
a
m
o
d
b
)
bx_2+(a\ mod\ b)y_2 = gcd(b, a\ mod\ b)
bx2+(a mod b)y2=gcd(b,a mod b),
那
a
m
o
d
b
a\ mod\ b
a mod b怎么表示?不就是
a
/
b
a/b
a/b的余数嘛,所以你细品:a%b = a - (a//b)*b,(
a
÷
b
=
q
…
…
r
a \div b = q……r
a÷b=q……r,则
a
=
b
∗
q
+
r
a = b*q + r
a=b∗q+r,其中q就是a整除b的结果a//b), 带入到上式则有
b
x
2
+
(
a
m
o
d
b
)
y
2
=
b
x
2
+
(
a
−
(
a
/
/
b
)
b
)
y
2
=
g
c
d
(
b
,
a
m
o
d
b
)
bx_2+(a\ mod\ b)y_2 = bx_2+(a - (a//b)b)y_2 = gcd(b,a\ mod\ b)
bx2+(a mod b)y2=bx2+(a−(a//b)b)y2=gcd(b,a mod b)
好乱,整理一下:
{
a
x
1
+
b
y
1
=
g
c
d
(
a
,
b
)
b
x
2
+
(
a
−
(
a
/
/
b
)
b
)
y
2
=
g
c
d
(
b
,
a
m
o
d
b
)
g
c
d
(
a
,
b
)
=
g
c
d
(
b
,
a
m
o
d
b
)
\left\{ \begin{aligned} ax_1 + by_1 &= gcd(a,b) \\ bx_2+(a - (a//b)b)y_2 &= gcd(b,a\ mod\ b) \\ gcd(a,b) &= gcd(b,a\ mod\ b) \end{aligned} \right.
⎩
⎨
⎧ax1+by1bx2+(a−(a//b)b)y2gcd(a,b)=gcd(a,b)=gcd(b,a mod b)=gcd(b,a mod b)这样我们就能找到
(
x
1
,
y
1
)
(x_1,y_1)
(x1,y1)和
(
x
2
,
y
2
)
(x_2,y_2)
(x2,y2)的关系了:
a
x
1
+
b
y
1
=
a
y
2
+
b
(
x
2
−
(
a
/
/
b
)
y
2
)
ax_1+by_1 = ay_2 + b(x_2 - (a//b)y_2)
ax1+by1=ay2+b(x2−(a//b)y2),所以有
{
x
1
=
y
2
y
1
=
x
2
−
(
a
/
/
b
)
y
2
\left\{ \begin{aligned} x_1 &= y_2\\ y_1 &= x_2 - (a//b)y_2 \end{aligned} \right.
{x1y1=y2=x2−(a//b)y2这就是递归的关系式。
有了初始值与递归关系,就可以写代码来求解了。
def exgcd(a,b):
if b==0:
return a,1,0
gcd,x2,y2 = exgcd(b,a%b)
x1,y1 = y2,x2-(a//b)*y2
return gcd,x1,y1
别忘了我们求的是 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b),而我们要求的是 a x + b y = m ax+by=m ax+by=m,那么对于函数输出,还要再乘以 m / g c d m/gcd m/gcd
def Func(a,b,m):
gcd,x1,y1 = exgcd(a,b)
if m % gcd != 0:return None,None,None
x0, y0 = x * m // gcd, y * m // gcd
return gcd, x0, y0
汇总:
def exgcd(a,b):
if b==0:
return a,1,0
gcd,x2,y2 = exgcd(b,a%b)
x1,y1 = y2,x2-(a//b)*y2
return gcd,x1,y1
def Func(a,b,m):
gcd,x1,y1 = exgcd(a,b)
if m % gcd != 0:return None,None,None
x0, y0 = x * m // gcd, y * m // gcd
return gcd, x0, y0
MOD = 1e9+7
a = int(input())
_, inv_a, _ = Func(a, MOD, 1)
print(inv_a)
三、费马小定理
费马小定理:
如果 p p p是素数,且 a a a是一个不能被 p p p整除的整数,那么: a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv1({\mathrm{mod}}p) ap−1≡1(modp)
这给了我们一个求逆元的好方法:
a
a
p
−
2
≡
1
(
m
o
d
p
)
aa^{p-2}\equiv1(mod\ p)
aap−2≡1(mod p)注意前提:模数为素数,且
a
a
a不能被
p
p
p整除。
这时我们只需要算一下
a
p
−
2
a^{p-2}
ap−2就行了,很方便吧。可以用快速幂来求。
def KSM(a, b, mod):
if b == 0: return 1
if b == 1: return a % mod
res = KSM(a, b//2, mod)
res = res * res % mod
if b % 2 == 1:
res = res * a % mod
return res
def INV(a):
return KSM(a, MOD-2, MOD)
MOD = 1e9+7
a = int(input())
print(INV(a))
四、总结
介绍了两种方法求解逆元:扩展欧几里得算法和费马小定理算法,前者实用性广,而后者有一个前提,即模数必须为素数,不过一般用费马小定理的情况要多一些。