欧几里得算法
在欧几里得著的《几何原本》里面,有用线段的划分来讲解这个数学方法的,这里我们从代数而不是几何上来讲,并且侧重于算法OI竞赛。
欧几里得算法(gcd),又称辗转相除法,可以用来快速计算两个整数的最大公约数,并有许多扩展应用。
下面我们来看公式:
gcd(n,m)=gcd(m,n mod m)
我们可以简单的证明一下这个公式的正确性:
我们令g=gcd(n,m),那么显然g∣n,g∣m(a∣b表示a为b的因子)。又因为n mod m=n−m×⌊mn⌋,那么我们可以将n,m写为n=k1g,m=k2g,那么n mod m=n−m×⌊mn⌋=k1g−k2g⌊k2gk1g⌋=g(k1−k2⌊k2gk1g⌋),而(k1−k2⌊k2gk1g⌋)肯定为整数,所以g∣(n mod m),那么显然g∣gcd(m,n mod m),(因为g为n,m的因子)。接下来我们令g^=gcd(m,n mod m),那么显然有g^∣g,又因为前面我们可以得知g∣g^,所以就有g=g^,那么gcd(n,m)=gcd(m,n mod m)。
所以代码就简单啦!
递归边界为m=0时,因为模数不能为0,所以此时就可以直接返回n。
int gcd(int n,int m){
if(!m) return n;
else return gcd(m,n%m);
}
扩展欧几里得算法
内容:对于一个系数为整数(a,b,c为整数)的二元一次方程ax+by=c,若其存在整数解,当且仅当gcd(a,b)∣c。
用处:判断一个上述的二元一次方程是否有整数解。
简单的证明:
我们令g=gcd(a,b),同样的我们可以将a,b写成a=k1g,b=k2g,那么显然ax+by=g(k1x+k2y),所以g∣(ax+by)。
所以当gcd(a,b)∣c时必然有整数解,下面我们将在扩展欧几里得算法讲解给出证明,及其整数解的求法。
正题
ax+by=c在c∣gcd(a,b)前提下是否一定有整数解呢?
因为有了前提,所以我们可以将原式写成ax+by=k⋅gcd(a,b),由于k为整数,那么如果ax+by=gcd(a,b)有整数解,那么原式一定有整数解(倍数关系),那么只需证明并求出ax+by=gcd(a,b)的一组特殊解(x1,y1),然后所有的解都可以表示出来(原来的解就是现在解的k倍)。
所以现在我们只需证明ax+by=gcd(a,b)有解即可。
通过gcd的递推式可知,我们如果的到如下式子的一组解:
bx+(a mod b)y=gcd(b,a mod b)
令解为(x1,y1),那么就有如下推导:
ax+by=bx1+(a mod b)y1
=bx1+(a−⌊ba⌋×b)y1
=ay1+(x1−⌊ba⌋y1)b
一个小引理当ax+by=az+bk,必然x,y有一组解为x=z,y=k。
ax+by=ay1+(x1−⌊ba⌋y1)b
所以有一组解为x=y1,y=x1−⌊ba⌋y1,而这个解显然一定会满足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;}
}
递归边界显然就是当ax+by=gcd(a,b)的时候,b=0,显然gcd(a,b)不合法,所以我们就令gcd(a,0)=a,那么原来方程就变为ax=a,显然x=1,y=0。
辗转相减的用途
既然有更快的辗转相除,那么辗转相减有什么用呢?
我们知道gcd(a,b)=gcd(a,b−a),那么容易推广而知gcd(a1,a2,a3,⋯,an)=gcd(a1,a2−a1,a3−a2,⋯,an−an−1),那么对于一个序列a的区间gcd,我们可以通过差分的方式快速维护。
题目:bzoj 5028 小Z的加油店
应用
因为我们可以发现,逆元式子ax≡1( mod m),x为逆元,当gcd(a,m)=1,它可以写成a⋅x=1+b⋅m,也就是a⋅x+(−b)⋅m=1,因为gcd(a,m)∣1,所以我们用扩展欧几里得算法求出这个方程的一组解,其中的x便是它的逆元。友链-逆元求法
推荐这个讲解的友链文章IN