欧几里得算法及其扩展学习笔记

欧几里得算法

在欧几里得著的《几何原本》里面,有用线段的划分来讲解这个数学方法的,这里我们从代数而不是几何上来讲,并且侧重于算法OI竞赛。

欧几里得算法(gcdgcd),又称辗转相除法,可以用来快速计算两个整数的最大公约数,并有许多扩展应用。


下面我们来看公式:
gcd(n,m)=gcd(m,n mod m)gcd(n,m)=gcd(m,n m mod m)

我们可以简单的证明一下这个公式的正确性:
我们令g=gcd(n,m)g=gcd(n,m),那么显然gn,gmg|n,g|maba|b表示aabb的因子)。又因为n mod m=nm×nmn m mod m=n-m imes lfloorfrac{n}{m} floor,那么我们可以将n,mn,m写为n=k1g,m=k2gn=k_1g,m=k_2g,那么n mod m=nm×nm=k1gk2gk1gk2g=g(k1k2k1gk2g)n m mod m=n-m imes lfloorfrac{n}{m} floor=k_1g-k_2glfloorfrac{k_1g}{k_2g} floor=gleft(k_1-k_2leftlfloorfrac{k_1g}{k_2g} ight floor ight),而(k1k2k1gk2g)left(k_1-k_2leftlfloorfrac{k_1g}{k_2g} ight floor ight)肯定为整数,所以g(n mod m)g|(n m mod m),那么显然ggcd(m,n mod m)g|gcd(m,n m mod m),(因为ggn,mn,m的因子)。接下来我们令g^=gcd(m,n mod m)hat g=gcd(m,n m mod m),那么显然有g^ghat g|g,又因为前面我们可以得知gg^g|hat g,所以就有g=g^g=hat g,那么gcd(n,m)=gcd(m,n mod m)gcd(n,m)=gcd(m,n m mod m)

所以代码就简单啦!
递归边界为m=0m=0时,因为模数不能为0,所以此时就可以直接返回nn

int gcd(int n,int m){
	if(!m) return n;
	else return gcd(m,n%m); 
}//也可以用自带的__gcd(n,m);

扩展欧几里得算法

  • 前置

  • 裴蜀定理(贝祖定理)

内容:对于一个系数为整数(a,b,ca,b,c为整数)的二元一次方程ax+by=cax+by=c,若其存在整数解,当且仅当gcd(a,b)cgcd(a,b)|c
用处:判断一个上述的二元一次方程是否有整数解。

简单的证明:
我们令g=gcd(a,b)g=gcd(a,b),同样的我们可以将a,ba,b写成a=k1g,b=k2ga=k_1g,b=k_2g,那么显然ax+by=g(k1x+k2y)ax+by=g(k_1x+k_2y),所以g(ax+by)g|(ax+by)

所以当gcd(a,b)cgcd(a,b)|c时必然有整数解,下面我们将在扩展欧几里得算法讲解给出证明,及其整数解的求法。


正题

  • Exgcd

ax+by=cax+by=ccgcd(a,b)c|gcd(a,b)前提下是否一定有整数解呢?

因为有了前提,所以我们可以将原式写成ax+by=kgcd(a,b)ax+by=kcdot gcd(a,b),由于kk为整数,那么如果ax+by=gcd(a,b)ax+by=gcd(a,b)有整数解,那么原式一定有整数解(倍数关系),那么只需证明并求出ax+by=gcd(a,b)ax+by=gcd(a,b)的一组特殊解(x1,y1)(x_1,y_1),然后所有的解都可以表示出来(原来的解就是现在解的kk倍)。

所以现在我们只需证明ax+by=gcd(a,b)ax+by=gcd(a,b)有解即可。

通过gcdgcd的递推式可知,我们如果的到如下式子的一组解:
bx+(a mod b)y=gcd(b,a mod b)bx+(a m mod b)y=gcd(b,a m mod b)
令解为(x1,y1)(x_1,y_1),那么就有如下推导:
ax+by=bx1+(a mod b)y1ax+by=bx_1+(a m mod b)y_1
=bx1+(aab×b)y1=bx_1+(a-leftlfloorfrac{a}{b} ight floor imes b)y_1
=ay1+(x1aby1)b=ay_1+(x_1-leftlfloorfrac{a}{b} ight floor y_1)b
一个小引理当ax+by=az+bkax+by=az+bk,必然x,yx,y有一组解为x=z,y=kx=z,y=k
ax+by=ay1+(x1aby1)bax+by=ay_1+(x_1-leftlfloorfrac{a}{b} ight floor y_1)b
所以有一组解为x=y1,y=x1aby1x=y_1,y=x_1-leftlfloorfrac{a}{b} ight floor y_1,而这个解显然一定会满足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;}
}	

递归边界显然就是当ax+by=gcd(a,b)ax+by=gcd(a,b)的时候,b=0b=0,显然gcd(a,b)gcd(a,b)不合法,所以我们就令gcd(a,0)=agcd(a,0)=a,那么原来方程就变为ax=aax=a,显然x=1,y=0x=1,y=0

辗转相减的用途

既然有更快的辗转相除,那么辗转相减有什么用呢?
我们知道gcd(a,b)=gcd(a,ba)gcd(a,b)=gcd(a,b-a),那么容易推广而知gcd(a1,a2,a3, ,an)=gcd(a1,a2a1,a3a2, ,anan1)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}),那么对于一个序列aa的区间gcdgcd,我们可以通过差分的方式快速维护。
题目:bzoj 5028 小Z的加油店

应用

  • 求乘法逆元

因为我们可以发现,逆元式子ax1( mod m)axequiv 1( m mod m)xx为逆元,当gcd(a,m)=1gcd(a,m)=1,它可以写成ax=1+bmacdot x=1+bcdot m,也就是ax+(b)m=1acdot x+(-b)cdot m=1,因为gcd(a,m)1gcd(a,m)|1,所以我们用扩展欧几里得算法求出这个方程的一组解,其中的xx便是它的逆元。友链-逆元求法

  • 求方程的一组解(基本操作)

  • 辗转相除法神奇用法:求以下式子
    i=1ni×pqsum_{i=1}^{n}leftlfloorfrac{i imes p}{q} ight floor
    具体为转换成三角形求内部整点个数。

  • 题目1

  • 题目2

推荐这个讲解的友链文章IN


原文地址:https://www.cnblogs.com/VictoryCzt/p/10053430.html