欧几里得算法
在欧几里得著的《几何原本》里面,有用线段的划分来讲解这个数学方法的,这里我们从代数而不是几何上来讲,并且侧重于算法OI竞赛。
欧几里得算法( g c d gcd gcd),又称辗转相除法,可以用来快速计算两个整数的最大公约数,并有许多扩展应用。
下面我们来看公式:
g
c
d
(
n
,
m
)
=
g
c
d
(
m
,
n
m
o
d
m
)
gcd(n,m)=gcd(m,n\ \rm mod\ m)
gcd(n,m)=gcd(m,n mod m)
我们可以简单的证明一下这个公式的正确性:
我们令
g
=
g
c
d
(
n
,
m
)
g=gcd(n,m)
g=gcd(n,m),那么显然
g
∣
n
,
g
∣
m
g|n,g|m
g∣n,g∣m(
a
∣
b
a|b
a∣b表示
a
a
a为
b
b
b的因子)。又因为
n
m
o
d
m
=
n
−
m
×
⌊
n
m
⌋
n\ \rm mod\ m=n-m\times \lfloor\frac{n}{m}\rfloor
n mod m=n−m×⌊mn⌋,那么我们可以将
n
,
m
n,m
n,m写为
n
=
k
1
g
,
m
=
k
2
g
n=k_1g,m=k_2g
n=k1g,m=k2g,那么
n
m
o
d
m
=
n
−
m
×
⌊
n
m
⌋
=
k
1
g
−
k
2
g
⌊
k
1
g
k
2
g
⌋
=
g
(
k
1
−
k
2
⌊
k
1
g
k
2
g
⌋
)
n\ \rm mod\ m=n-m\times \lfloor\frac{n}{m}\rfloor=k_1g-k_2g\lfloor\frac{k_1g}{k_2g}\rfloor=g\left(k_1-k_2\left\lfloor\frac{k_1g}{k_2g}\right\rfloor\right)
n mod m=n−m×⌊mn⌋=k1g−k2g⌊k2gk1g⌋=g(k1−k2⌊k2gk1g⌋),而
(
k
1
−
k
2
⌊
k
1
g
k
2
g
⌋
)
\left(k_1-k_2\left\lfloor\frac{k_1g}{k_2g}\right\rfloor\right)
(k1−k2⌊k2gk1g⌋)肯定为整数,所以
g
∣
(
n
m
o
d
m
)
g|(n\ \rm mod\ m)
g∣(n mod m),那么显然
g
∣
g
c
d
(
m
,
n
m
o
d
m
)
g|gcd(m,n\ \rm mod\ m)
g∣gcd(m,n mod m),(因为
g
g
g为
n
,
m
n,m
n,m的因子)。接下来我们令
g
^
=
g
c
d
(
m
,
n
m
o
d
m
)
\hat g=gcd(m,n\ \rm mod\ m)
g^=gcd(m,n mod m),那么显然有
g
^
∣
g
\hat g|g
g^∣g,又因为前面我们可以得知
g
∣
g
^
g|\hat g
g∣g^,所以就有
g
=
g
^
g=\hat g
g=g^,那么
g
c
d
(
n
,
m
)
=
g
c
d
(
m
,
n
m
o
d
m
)
gcd(n,m)=gcd(m,n\ \rm mod\ m)
gcd(n,m)=gcd(m,n mod m)。
所以代码就简单啦!
递归边界为
m
=
0
m=0
m=0时,因为模数不能为0,所以此时就可以直接返回
n
n
n。
int gcd(int n,int m){
if(!m) return n;
else return gcd(m,n%m);
}//也可以用自带的__gcd(n,m);
扩展欧几里得算法
-
前置
-
裴蜀定理(贝祖定理)
内容:对于一个系数为整数( a , b , c a,b,c a,b,c为整数)的二元一次方程 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。
用处:判断一个上述的二元一次方程是否有整数解。
简单的证明:
我们令
g
=
g
c
d
(
a
,
b
)
g=gcd(a,b)
g=gcd(a,b),同样的我们可以将
a
,
b
a,b
a,b写成
a
=
k
1
g
,
b
=
k
2
g
a=k_1g,b=k_2g
a=k1g,b=k2g,那么显然
a
x
+
b
y
=
g
(
k
1
x
+
k
2
y
)
ax+by=g(k_1x+k_2y)
ax+by=g(k1x+k2y),所以
g
∣
(
a
x
+
b
y
)
g|(ax+by)
g∣(ax+by)。
所以当 g c d ( a , b ) ∣ c gcd(a,b)|c gcd(a,b)∣c时必然有整数解,下面我们将在扩展欧几里得算法讲解给出证明,及其整数解的求法。
正题
- Exgcd
a x + b y = c ax+by=c ax+by=c在 c ∣ g c d ( a , b ) c|gcd(a,b) c∣gcd(a,b)前提下是否一定有整数解呢?
因为有了前提,所以我们可以将原式写成 a x + b y = k ⋅ g c d ( a , b ) ax+by=k\cdot gcd(a,b) ax+by=k⋅gcd(a,b),由于 k k k为整数,那么如果 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)有整数解,那么原式一定有整数解(倍数关系),那么只需证明并求出 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)的一组特殊解 ( x 1 , y 1 ) (x_1,y_1) (x1,y1),然后所有的解都可以表示出来(原来的解就是现在解的 k k k倍)。
所以现在我们只需证明 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)有解即可。
通过
g
c
d
gcd
gcd的递推式可知,我们如果的到如下式子的一组解:
b
x
+
(
a
m
o
d
b
)
y
=
g
c
d
(
b
,
a
m
o
d
b
)
bx+(a\ \rm mod\ b)y=gcd(b,a\ \rm mod\ b)
bx+(a mod b)y=gcd(b,a mod b)
令解为
(
x
1
,
y
1
)
(x_1,y_1)
(x1,y1),那么就有如下推导:
a
x
+
b
y
=
b
x
1
+
(
a
m
o
d
b
)
y
1
ax+by=bx_1+(a\ \rm mod\ b)y_1
ax+by=bx1+(a mod b)y1
=
b
x
1
+
(
a
−
⌊
a
b
⌋
×
b
)
y
1
=bx_1+(a-\left\lfloor\frac{a}{b}\right\rfloor\times b)y_1
=bx1+(a−⌊ba⌋×b)y1
=
a
y
1
+
(
x
1
−
⌊
a
b
⌋
y
1
)
b
=ay_1+(x_1-\left\lfloor\frac{a}{b}\right\rfloor y_1)b
=ay1+(x1−⌊ba⌋y1)b
一个小引理当
a
x
+
b
y
=
a
z
+
b
k
ax+by=az+bk
ax+by=az+bk,必然
x
,
y
x,y
x,y有一组解为
x
=
z
,
y
=
k
x=z,y=k
x=z,y=k。
a
x
+
b
y
=
a
y
1
+
(
x
1
−
⌊
a
b
⌋
y
1
)
b
ax+by=ay_1+(x_1-\left\lfloor\frac{a}{b}\right\rfloor y_1)b
ax+by=ay1+(x1−⌊ba⌋y1)b
所以有一组解为
x
=
y
1
,
y
=
x
1
−
⌊
a
b
⌋
y
1
x=y_1,y=x_1-\left\lfloor\frac{a}{b}\right\rfloor y_1
x=y1,y=x1−⌊ba⌋y1,而这个解显然一定会满足
a
x
+
b
y
=
g
c
d
(
a
,
b
)
ax+by=gcd(a,b)
ax+by=gcd(a,b)。所以我们不仅证明了它一定有解,还构造出了解的样子和求法。
代码就是这样的:
int exgcd(int a,int b,int &x,int &y){
if(!b){x=1;y=0;return a;}
else {int now=exgcd(b,a%b,y,x);y-=x*(a/b);return now;}
}
递归边界显然就是当 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)的时候, b = 0 b=0 b=0,显然 g c d ( a , b ) gcd(a,b) gcd(a,b)不合法,所以我们就令 g c d ( a , 0 ) = a gcd(a,0)=a gcd(a,0)=a,那么原来方程就变为 a x = a ax=a ax=a,显然 x = 1 , y = 0 x=1,y=0 x=1,y=0。
辗转相减的用途
既然有更快的辗转相除,那么辗转相减有什么用呢?
我们知道
g
c
d
(
a
,
b
)
=
g
c
d
(
a
,
b
−
a
)
gcd(a,b)=gcd(a,b-a)
gcd(a,b)=gcd(a,b−a),那么容易推广而知
g
c
d
(
a
1
,
a
2
,
a
3
,
⋯
 
,
a
n
)
=
g
c
d
(
a
1
,
a
2
−
a
1
,
a
3
−
a
2
,
⋯
 
,
a
n
−
a
n
−
1
)
gcd(a_1,a_2,a_3,\cdots ,a_n)=gcd(a_1,a_2-a_1,a_3-a_2,\cdots,a_n-a_{n-1})
gcd(a1,a2,a3,⋯,an)=gcd(a1,a2−a1,a3−a2,⋯,an−an−1),那么对于一个序列
a
a
a的区间
g
c
d
gcd
gcd,我们可以通过差分的方式快速维护。
题目:bzoj 5028 小Z的加油店
应用
- 求乘法逆元
因为我们可以发现,逆元式子 a x ≡ 1 ( m o d m ) ax\equiv 1(\ \rm mod\ m) ax≡1( mod m), x x x为逆元,当 g c d ( a , m ) = 1 gcd(a,m)=1 gcd(a,m)=1,它可以写成 a ⋅ x = 1 + b ⋅ m a\cdot x=1+b\cdot m a⋅x=1+b⋅m,也就是 a ⋅ x + ( − b ) ⋅ m = 1 a\cdot x+(-b)\cdot m=1 a⋅x+(−b)⋅m=1,因为 g c d ( a , m ) ∣ 1 gcd(a,m)|1 gcd(a,m)∣1,所以我们用扩展欧几里得算法求出这个方程的一组解,其中的 x x x便是它的逆元。友链-逆元求法
- 求方程的一组解(基本操作)
-
辗转相除法神奇用法:求以下式子
∑ i = 1 n ⌊ i × p q ⌋ \sum_{i=1}^{n}\left\lfloor\frac{i\times p}{q}\right\rfloor i=1∑n⌊qi×p⌋
具体为转换成三角形求内部整点个数。
推荐这个讲解的友链文章IN