欧几里得算法
欧几里得算法,又称辗转相除法。用于计算两个整数
a
,
b
a,b
a,b 的最大公约数。
基本算法思路(来自离散数学)为:
设
a
=
b
q
+
r
a=bq+r
a=bq+r,其中
a
,
b
,
q
,
r
a,b,q,r
a,b,q,r都是整数。则
gcd
(
a
,
b
)
=
gcd
(
b
,
r
)
\text{gcd}(a,b)=\text{gcd}(b,r)
gcd(a,b)=gcd(b,r),即
gcd
(
a
,
b
)
=
gcd
(
b
,
a
%
b
)
\text{gcd}(a,b)=\text{gcd}(b,a\%b)
gcd(a,b)=gcd(b,a%b)。
Theorem 1
令 a , b , c a,b,c a,b,c 都是整数,则:
- 若 a ∣ b a|b a∣b 和 a ∣ c a|c a∣c,则 a ∣ ( b + c ) a|(b+c) a∣(b+c);
- 若 a ∣ b a|b a∣b,那么对于所有整数 c c c 都有 a ∣ b c a|bc a∣bc;
- 若 a ∣ b , b ∣ c a|b,\ b|c a∣b, b∣c,则 a ∣ c a|c a∣c。
证明:假定
a
∣
b
a|b
a∣b 和
a
∣
c
a|c
a∣c,从整除性定义可知有整数
s
,
t
s,t
s,t,使得
b
=
a
s
,
c
=
a
t
b=as,\ c=at
b=as, c=at。
因此,
b
+
c
=
a
s
+
a
t
=
a
(
s
+
t
)
b+c=as+at=a(s+t)
b+c=as+at=a(s+t)
于是
a
a
a 整出
b
+
c
b+c
b+c,这样,我们就证明了定理 1 的第(1)部分。
Lemma 1
如果 a , b , c a,b,c a,b,c 是整数,使得 a ∣ b a|b a∣b 和 a ∣ c a|c a∣c,那么只要 m , n m,n m,n 是整数,就有 a ∣ ( m b + n c ) a|(mb+nc) a∣(mb+nc)。
证明:根据定义 1 的第(2)部分,只要 m , n m,n m,n是整数,就有 a ∣ m b a|mb a∣mb 和 a ∣ n c a|nc a∣nc。根据定理 1 中的(1),有 a ∣ ( m b + n c ) a|(mb+nc) a∣(mb+nc)。
辗转相除法
证明:如果能证明
a
,
b
a,b
a,b 的最大公约数和
b
,
r
b,r
b,r 的公约数相同,也就证明了
gcd
(
a
,
b
)
=
gcd
(
b
,
r
)
\text{gcd}(a,b)=\text{gcd}(b,r)
gcd(a,b)=gcd(b,r),因为这两对整数必定有相同的最大公约数。
现在假定
d
d
d 整除
b
b
b 和
r
r
r,于是
d
d
d 也整除
a
−
b
q
=
r
a-bq=r
a−bq=r(根据离散数学2.4定理1),因此
a
a
a 和
b
b
b 的任何公约数也是
b
b
b 和
r
r
r 的公约数。
类似地,假定
d
d
d 整除
b
b
b 和
r
r
r,于是
d
d
d 也整除
b
q
+
r
=
a
bq+r=a
bq+r=a,因此
b
b
b 和
r
r
r 的任何公约数也是
a
a
a 和
b
b
b 的公约数。
于是
gcd
(
a
,
b
)
=
gcd
(
b
,
r
)
\text{gcd}(a,b)=\text{gcd}(b,r)
gcd(a,b)=gcd(b,r)。
假定
a
a
a 和
b
b
b 为正整数,
a
≥
b
a \ge b
a≥b。令
r
0
=
a
r_0=a
r0=a 和
r
1
=
b
r_1=b
r1=b。若辗转应用整除算法,得
r
0
=
r
1
q
1
+
r
2
0
≤
r
2
<
r
1
r
1
=
r
2
q
2
+
r
3
0
≤
r
3
<
r
2
⋮
r
n
−
2
=
r
n
−
1
q
n
−
1
+
r
n
0
≤
r
n
<
r
n
−
1
r
n
−
1
=
r
n
q
n
r_0=r_1q_1+r_2\ \ \ \ 0 \le r_2 < r_1\\ r_1=r_2q_2+r_3\ \ \ \ 0 \le r_3 < r_2\\ \vdots\\ r_{n-2}=r_{n-1}q_{n-1}+r_n\ \ \ \ 0 \le r_n < r_{n-1}\\ r_{n-1}=r_nq_n\ \ \ \
r0=r1q1+r2 0≤r2<r1r1=r2q2+r3 0≤r3<r2⋮rn−2=rn−1qn−1+rn 0≤rn<rn−1rn−1=rnqn
最终在辗转相除序列中会出现余数为
0
0
0,因为在余数序列
a
=
r
0
>
r
1
>
r
2
>
⋯
≥
0
a=r_0>r_1>r_2>\cdots \ge 0
a=r0>r1>r2>⋯≥0 中至多包含
a
a
a 项,从引理
1
1
1 知道
gcd
(
a
,
b
)
=
gcd
(
r
0
,
r
1
)
=
gcd
(
r
1
,
r
2
)
=
⋯
=
gcd
(
r
n
−
2
,
r
n
−
1
)
=
gcd
(
r
n
−
1
,
r
n
)
=
gcd
(
r
n
,
0
)
=
r
n
\text{gcd}(a,b)=\text{gcd}(r_0,r_1)=\text{gcd}(r_1,r_2)=\cdots \\ =\text{gcd}(r_{n-2},r_{n-1})=\text{gcd}(r_{n-1},r_n)=\text{gcd}(r_n,0)=r_n
gcd(a,b)=gcd(r0,r1)=gcd(r1,r2)=⋯=gcd(rn−2,rn−1)=gcd(rn−1,rn)=gcd(rn,0)=rn
因此,最大公约数是除法序列中最后一个非零余数。
应用
现在,我们知道了
a
,
b
a,b
a,b 的最大公约是
d
d
d,那么,我们一定能够找到这样的
x
,
y
x,y
x,y,使得:
a
x
+
b
y
=
d
ax+by=d
ax+by=d,这是一个不定方程,也就是一种丢番图方程,该方程有任意组解。
但是只要我们找到一组特殊的解,记为
x
0
,
y
0
x_0,y_0
x0,y0,那么,我们就可以用这组解表示出整个不定方程的通解,对应的通解为:
{
x
=
x
0
+
(
b
/
d
)
∗
k
y
=
y
0
−
(
a
/
d
)
∗
k
k
∈
[
−
∞
,
+
∞
]
\begin{cases} x=x_0+(b/d)*k\\ y=y_0-(a/d)*k \end{cases}\ k \in [-\infty, +\infty]
{x=x0+(b/d)∗ky=y0−(a/d)∗k k∈[−∞,+∞]
令 A = a d , B = b d A=\frac{a}{d},\ B=\frac{b}{d} A=da, B=db,那么 A , B A,B A,B 一定是互质的,这样最小系数不就是这两个。
于是,我们就从欧几里得算法过度到扩展欧几里得算法。
扩展欧几里得算法
扩欧就是求
a
×
x
+
b
×
y
=
gcd
(
a
,
b
)
a \times x+b \times y=\text{gcd}(a,b)
a×x+b×y=gcd(a,b) 这个方程的解,其实也就是同余方程
a
x
≡
gcd
(
a
,
b
)
(
m
o
d
b
)
ax \equiv \text{gcd}(a,b)\ (\bmod\ b)
ax≡gcd(a,b) (mod b) 的解。
这里,我们主要是想计算出
x
x
x 的值,
y
y
y 是一个辅助解。
简单理解
当 b = 0 b=0 b=0
gcd
(
a
,
b
)
=
gcd
(
a
,
0
)
=
a
\text{gcd}(a,b)=\text{gcd}(a,0)=a
gcd(a,b)=gcd(a,0)=a
∴
a
×
x
+
b
×
y
=
gcd
(
a
,
b
)
→
a
×
x
=
a
\therefore a \times x+b\times y=\text{gcd}(a,b) \to a \times x=a
∴a×x+b×y=gcd(a,b)→a×x=a
我们取一组最小的正整数解,即
x
=
1
,
y
=
0
x=1,\ y=0
x=1, y=0。
当 b ≠ 0 b \neq 0 b=0
根据最大公约数性质
gcd
(
a
,
b
)
=
gcd
(
b
,
a
m
o
d
b
)
\text{gcd}(a,b)=\text{gcd}(b, a \bmod b)
gcd(a,b)=gcd(b,amodb)。
这样我们构造出
b
x
1
+
(
a
m
o
d
b
)
y
1
=
gcd
(
b
,
(
a
m
o
d
b
)
)
bx_1+(a \bmod b)y_1 =\text{gcd}(b,(a \bmod b))
bx1+(amodb)y1=gcd(b,(amodb)),可得
b
x
1
+
(
a
m
o
d
b
)
y
1
=
a
x
+
b
y
bx_1+(a \bmod b)y_1=ax+by
bx1+(amodb)y1=ax+by
这里我们可以将
a
m
o
d
b
a \bmod b
amodb 变成
a
−
b
∗
(
a
/
b
)
a-b*(a/b)
a−b∗(a/b),注意这里的
/
/
/ 是整除的意思,带入上式,可得
b
x
1
+
(
a
−
b
∗
(
a
/
b
)
)
y
1
=
a
x
+
b
y
bx_1+(a-b*(a/b))y_1=ax+by
bx1+(a−b∗(a/b))y1=ax+by
b
x
1
+
a
y
1
−
b
∗
(
a
/
b
)
y
1
=
a
x
+
b
y
bx_1+ay_1-b*(a/b)y_1=ax+by
bx1+ay1−b∗(a/b)y1=ax+by
a
y
1
+
b
(
x
1
−
(
a
/
b
)
y
1
)
=
a
x
+
b
y
ay_1+b(x_1-(a/b)y_1)=ax+by
ay1+b(x1−(a/b)y1)=ax+by
这样,我们就求出了一组解,即为:
x
=
y
1
,
y
=
x
1
−
(
a
/
b
)
y
2
x=y_1,\ y=x_1-(a/b)y_2
x=y1, y=x1−(a/b)y2。
我们就这样继续卷啊。最终将
b
b
b 卷为
0
0
0。
但是,我们求出的解
x
x
x,这个
x
x
x 不一定是最小正整数解。显然
x
x
x 加上或者减去
b
b
b 是没有任何影响的,因此
x
=
(
x
%
b
+
b
)
%
b
x=(x\% b+b) \% b
x=(x%b+b)%b 就是满足同余方程的最小正整数解。
模板代码
//求解ax+by=gcd(a,b)
//返回值为 gcd(a,b)
LL exgcd(LL a, LL b, LL &x, LL &y) {
//出口
if (b==0) {
x=1;y=0;
return a;
}
LL d=exgcd(b, a%b, y, x);
y=y-a/b*x;
return d;
}
裴蜀定理
裴蜀定理,又称贝祖定理(Bézout’s lemma)。是一个关于最大公约数的定理。
设
a
,
b
a,\ b
a, b 是不全为零的整数,则存在整数
x
,
y
x,\ y
x, y, 使得
a
x
+
b
y
=
c
ax+by=c
ax+by=c。
也就是有解的充要条件是
g
c
d
(
a
,
b
)
∣
c
gcd(a,b)|c
gcd(a,b)∣c。
学渣,我们就不证明这个了。
OI运用
和扩欧相比,裴蜀定理增加了对方程是否有解的判断。即
g
c
d
(
a
,
b
)
∣
c
gcd(a,b)|c
gcd(a,b)∣c。
我们在 OI 中,一般都是使用
g
c
d
gcd
gcd 算法来搞。
同余方程
概念
方程定义为 a x ≡ b ( m o d m ) ax \equiv b\ (\bmod\ m) ax≡b (mod m),也就是 a x % m ≡ b % m ax \% m \equiv b \% m ax%m≡b%m。
同余方程变扩展欧几里得
∵
a
x
≡
b
(
m
o
d
m
)
\because ax \equiv b\ (\bmod\ m)
∵ax≡b (mod m)
∴
m
∣
(
a
x
−
b
)
\therefore m|(ax-b)
∴m∣(ax−b)
∴
m
×
k
=
a
×
x
−
b
∀
k
∈
[
−
∞
,
+
∞
]
\therefore m \times k = a \times x-b\ \ \forall k \in [-\infty,+\infty]
∴m×k=a×x−b ∀k∈[−∞,+∞]
∴
a
×
x
−
m
×
k
=
b
\therefore a \times x - m \times k=b
∴a×x−m×k=b
∴
a
×
x
+
m
×
y
=
b
\therefore a \times x + m \times y=b
∴a×x+m×y=b
用中文来说因为
a
x
ax
ax 和
b
b
b 对
m
m
m 是同余,也就意味着
a
x
−
b
ax-b
ax−b 是
m
m
m 的整数倍,也就是可以整除。
根据裴蜀定理,我们知道
a
x
+
m
y
=
b
ax+my=b
ax+my=b 有解的充要条件为
gcd
(
a
,
m
)
∣
b
\text{gcd}(a,m)|b
gcd(a,m)∣b。
该方程的通解形式为
x
=
b
d
x
0
+
m
d
k
,
d
=
gcd
(
a
,
m
)
x=\frac{b}{d}x_0+\frac{m}{d}k,\ \ \ d=\text{gcd}(a,m)
x=dbx0+dmk, d=gcd(a,m)。
下面是推导过程。利用扩欧,我们可以计算出一组解,记为
x
0
,
y
0
x_0, y_0
x0,y0。
∴
a
x
0
+
m
y
0
=
d
(
1
)
\therefore ax_0+my_0=d\ \ \ (1)
∴ax0+my0=d (1)
∵
a
x
+
m
y
=
d
(
2
)
\because ax+my=d\ \ \ (2)
∵ax+my=d (2)
∴
a
x
0
+
m
y
0
=
a
x
+
m
y
→
a
x
0
−
a
x
=
m
y
−
m
y
0
→
a
(
x
0
−
x
)
=
m
(
y
−
y
0
)
\therefore ax_0+my_0=ax+my \rightarrow ax_0-ax=my-my_0 \rightarrow a(x_0-x)=m(y-y_0)
∴ax0+my0=ax+my→ax0−ax=my−my0→a(x0−x)=m(y−y0)
为了求最小解,方程两边都处于
d
d
d,得到
a
d
(
x
0
−
x
)
=
m
d
(
y
−
y
0
)
\frac{a}{d}(x_0-x)=\frac{m}{d}(y-y_0)
da(x0−x)=dm(y−y0)
∵
gcd
(
a
d
,
m
d
)
≡
1
\because \text{gcd}(\frac{a}{d},\frac{m}{d}) \equiv 1
∵gcd(da,dm)≡1
∴
m
d
∣
(
x
0
−
x
)
\therefore \frac{m}{d}|(x_0-x)
∴dm∣(x0−x),也就是
x
0
−
x
x_0-x
x0−x 一定是
m
d
\frac{m}{d}
dm 的整数倍。
∴
x
0
−
x
=
k
m
d
\therefore x_0-x = k\frac{m}{d}
∴x0−x=kdm
∴
x
=
x
0
+
k
m
d
\therefore x=x_0+k\frac{m}{d}
∴x=x0+kdm
一般 OI 中,都是要求一个最小的正整数解。
如果
x
≥
0
x\ge 0
x≥0,
x
%
m
d
x \% \frac{m}{d}
x%dm 为一个介于
[
0
,
m
d
)
[0, \frac{m}{d})
[0,dm) 的最小正整数解。
如果
x
<
0
x<0
x<0,因为 C++ 的取模结果
x
%
m
d
x \% \frac{m}{d}
x%dm 为负数,
(
x
%
m
d
+
m
d
)
%
m
d
(x \% \frac{m}{d}+\frac{m}{d})\% \frac{m}{d}
(x%dm+dm)%dm 为一个介于
[
0
,
m
d
)
[0, \frac{m}{d})
[0,dm) 的最小正整数解。
中国剩余定理
{
x
≡
b
1
(
m
o
d
a
1
)
x
≡
b
2
(
m
o
d
a
2
)
.
.
.
x
≡
b
k
(
m
o
d
a
k
)
\begin{cases} x \equiv b_1\ (\bmod \ a_1)\\ x \equiv b_2\ (\bmod \ a_2)\\ ...\\ x \equiv b_k\ (\bmod \ a_k)\\ \end{cases}
⎩⎪⎪⎪⎨⎪⎪⎪⎧x≡b1 (mod a1)x≡b2 (mod a2)...x≡bk (mod ak)
其中所有的
a
1
,
a
2
,
⋯
,
a
n
a_1,a_2,\cdots,a_n
a1,a2,⋯,an 都是互质,这里的
b
i
b_i
bi 都小于
a
i
a_i
ai。
符号定义
a
i
a_i
ai 表示扩展中国剩余定理的模数。
b
i
b_i
bi 表示扩展中国剩余定理的余数。
如上公式所示。
简单解释
这样,我们可以构造一个数
A
=
a
1
∗
a
2
∗
⋯
∗
a
n
A=a_1*a_2*\cdots *a_n
A=a1∗a2∗⋯∗an,那么显然当我们求出
x
x
x 之后,加上或者减去
A
A
A 都是成立的。
下面考虑对于每一个同余方程的处理,
对于同余式
x
≡
b
k
(
m
o
d
a
k
)
x \equiv b_k\ (\bmod\ a_k)
x≡bk (mod ak),我们可以构造一个
A
k
=
A
/
a
k
A_k=A/a_k
Ak=A/ak,显然
gcd
(
A
k
,
a
k
)
=
1
\text{gcd}(A_k,a_k)=1
gcd(Ak,ak)=1,根据裴蜀定理可得,存在整数
i
,
j
i,j
i,j 使得
i
A
k
+
j
a
k
=
1
iA_k+ja_k=1
iAk+jak=1,即
i
A
k
≡
1
(
m
o
d
a
k
)
iA_k \equiv 1\ (\bmod\ a_k)
iAk≡1 (mod ak)。
因为
i
A
k
m
o
d
b
k
=
1
iA_k \bmod b_k=1
iAkmodbk=1,那么有
b
i
∗
i
A
k
m
o
d
a
k
=
b
i
b_i*iA_k \bmod a_k=b_i
bi∗iAkmodak=bi,这样
i
i
i 可以通过扩欧求得。所以该同余方程的一个解式
x
=
b
i
∗
i
∗
A
k
x=b_i*i*A_k
x=bi∗i∗Ak。
这样,再回到整个方程组,我们继续构造
x
=
b
1
i
1
A
1
+
b
2
i
2
A
2
+
⋯
+
b
n
i
n
A
n
x=b_1i_1A_1+b_2i_2A_2+\cdots + b_ni_nA_n
x=b1i1A1+b2i2A2+⋯+bninAn,那么这个数就是同余方程组的一个解。
因为对于第
k
k
k 个同余方程,
A
1
⋯
n
A_{1\cdots n}
A1⋯n 中除了
A
k
A_k
Ak 之外所有的数都是
a
k
a_k
ak 的倍数,这样同余方程式成立的。
LL china(LL n) {
LL N=1;
for (LL i=1; i<=n; i++) {
N*=a[i];
}
LL ans=0;
for (LL i=1; i<=n; i++) {
LL a=N/a[i], b=a[i], x, y;
exgcd(a,b,x,y);
ans+=a*((x%b+b)%b)*b[i];
}
return (ans%N+N)%N;
}
扩展中国剩余定理
扩展中国剩余定理,不需要中国剩余定理中的对模数要两两互质的要求。其实中国剩余定理就是
n
n
n 个同余方程。
{
x
≡
b
1
(
m
o
d
a
1
)
x
≡
b
2
(
m
o
d
a
2
)
.
.
.
x
≡
b
k
(
m
o
d
a
k
)
\begin{cases} x \equiv b_1\ (\bmod \ a_1)\\ x \equiv b_2\ (\bmod \ a_2)\\ ...\\ x \equiv b_k\ (\bmod \ a_k)\\ \end{cases}
⎩⎪⎪⎪⎨⎪⎪⎪⎧x≡b1 (mod a1)x≡b2 (mod a2)...x≡bk (mod ak)
个人理解
考虑已经求出来了前
k
−
1
k−1
k−1 个同余方程的解,得到的解为 ans ,设 lcm 为前
k
−
1
k−1
k−1 个方程中所有的
a
i
a_i
ai 的最小公倍数,则前
k
−
1
k−1
k−1 个方程的解为 x=ans+i∗lcm ,而我们只需要确定一个
i
i
i 使得
a
n
s
+
i
∗
l
c
m
≡
b
k
(
m
o
d
a
k
)
ans+i∗lcm \equiv b_k\ (\bmod\ a_k)
ans+i∗lcm≡bk (mod ak),然后更新一下 lcm 就好了。
a
n
s
+
1
∗
l
c
m
=
b
k
(
m
o
d
a
k
)
ans+1*lcm=b_k\ (\bmod\ a_k)
ans+1∗lcm=bk (mod ak)
i
∗
l
c
m
≡
b
k
−
a
n
s
(
m
o
d
a
k
)
i*lcm \equiv b_k-ans\ (\bmod\ a_k)
i∗lcm≡bk−ans (mod ak)
由于
l
c
m
lcm
lcm 和
a
k
a_k
ak 不一定互质,设
gcd
=
gcd
(
lcm
,
a
k
)
,
c
=
(
b
k
−
a
n
s
)
m
o
d
a
k
\text{gcd}=\text{gcd}(\text{lcm}, a_k),\ c=(b_k-ans) \bmod\ a_k
gcd=gcd(lcm,ak), c=(bk−ans)mod ak,这样由上式可得
i
∗
l
c
m
+
h
∗
a
k
=
c
i*lcm+h*a_k=c
i∗lcm+h∗ak=c
我们继续变形
i
/
g
c
d
∗
l
c
m
+
h
∗
a
k
/
g
c
d
=
c
/
g
c
d
i/gcd*lcm+h*a_k/gcd=c/gcd
i/gcd∗lcm+h∗ak/gcd=c/gcd
i
/
g
c
d
∗
l
c
m
≡
c
/
g
c
d
(
m
o
d
a
k
/
g
c
d
)
i/gcd*lcm \equiv c/gcd\ (\bmod\ a_k/gcd)
i/gcd∗lcm≡c/gcd (mod ak/gcd)
此时,
gcd
(
l
c
m
,
b
k
/
g
c
d
)
=
1
\text{gcd}(lcm, b_k/gcd)=1
gcd(lcm,bk/gcd)=1,对于上面的方程,我们可以求出一个
j
j
j 满足
j
∗
l
c
m
≡
1
(
m
o
d
a
k
/
g
c
d
)
j*lcm \equiv 1\ (\bmod\ a_k/gcd)
j∗lcm≡1 (mod ak/gcd),然后再乘以
c
/
g
c
d
c/gcd
c/gcd 倍数即可。
模板代码
LL a[N];//模数
LL b[N];//余数
LL mul(LL x,LL y,LL p) {
//快速乘, (x*y)%p
LL res=0;
while (y) {
if (y%2) {
res=(res+x)%p;
}
x=(x+x)%p;
y/=2;
}
return res%p;
}
LL excrt(LL n) {
//第一个方程
LL m=a[1], ans=b[1];
for (LL i=2; i<=n; i++) {
//c就是前面bk-ans
LL c=(b[i]-ans%a[i]+a[i])%a[i];
LL x,y;
LL d=exgcd(m, a[i], x, y);
if ((b[i]-ans)%d) {
return -1;//无解返回 -1
}
x=mul(x,c/d,a[i]/d);
ans+=x*m;//更新ans
m*=(a[i]/d);
ans=(ans%m+m)%m;
}
return (ans%m+m)%m;
}