目录
引言
欧几里得算法(gcd)
代码模板:
扩展欧几里得(exgcd)
代码模板:
通解形式为:
最小正整数解:
什么是扩展欧几里得,听起来好高深,别急先从欧几里得下手;
辗转相除法:用a除以b(这里是a>b,当然,在程序编程中,求两个数的最大公约数,可以不限a和b的大小,a得到结果q和余数r,再用除数b除以余数r 再得到一个余数,再用除数除以余数,…如此循环,直到余数为0,那么此时的除数就是最大公因数
辗转相除法能够成立基于以下定理:
要想证明这个定理,需要知道两个引理:
这两个引理的是什么意思呢,拿第一个来说;
假设一个数d是a的因子,也就是a=m*d,同时也是b的因子,b=n*d,那么a%d = a-w*b = (m-w*n)*d;
由此可得,a的因子集合、b的因子集合和c的因子集合是相同的;
然后利用上述定理当余数为0的时候,除数就是最大公约数;
可是为什么会出现余数为0的情况呢,为什么这个时候除数是最大公约数?????
按照辗转相除法的方法如此循环下去,
因为每次的余数r肯定小于除数n
那么相当于每次的被除数(变为上次的除数)变小,
除数(变为上次的余数)也变小,而公因数的集合一直不变
也就是被除数和除数越来越小,而二者所包含的公共公因数集合又不
变,这样下去,除数总有一次会变为最大的公因数。
int gcd(int a, int b)
{
return b ? gcd(b, a % b) : a;
}
或者使用c++函数 __gcd();
顾名思义extend—gcd,就是在欧几里得算法的过程基础上进行修改;
扩展欧几里得用途:
首先除了了解过欧几里得算法,还要知道裴属定理:
即如果a、b是整数,那么一定存在整数x、y使得ax+by=gcd(a,b)。
换句话说,如果ax+by=m有解,那么m一定是gcd(a,b)的若干倍。(可以来判断一个这样的式子有没有解)
难道仅仅局限于知道是否有解么?当然好奇的人类是想知道在有解的情况下,这个解是多少,也就是x和y的值是多少;
扩展欧几里得就能实现(根据模板讲解吧):
// 求x, y,使得ax + by = gcd(a, b)
int exgcd(int a, int b, int &x, int &y)
{
if (!b)
{
x = 1; y = 0;
return a; //到达递归边界开始向上一层返回
}
int d = exgcd(b, a % b, x, y);
int temp=y; //推出这一层的x,y
y=x-(a/b)*y;
x=temp;
return d;
}
首先当递归到达边界时,余数b==0,除数a就是最大公约数,此时可以得出来方程的一组解
x=1,y=0; ——> a*1+b*0=gcd(a,b);
注意在递归中永远都是先得到上一层的结果状态
假设栈中当前层得到的解是x1,y1;那么 就要求下一层的结果状态了;代入方程得:
b*x1 + (a%b)*y1 = gcd(a,b)
—》b*x1+(a-(a/b)*b)*y1 = gcd(a,b)
—》a*y1+ b*(x1-(a/b)*y1) = gcd(a,b)
显而易见由上一层的解 x1,y1,可以推出下一层x,y的状态
x=y1 y= x1-(a/b)*y1
于是相邻的两个层之间的状态关系就出来了,ey~
由上述模板可以求出方程 ax+by=gcd(a,b) 的任意一组解x0,y0;那么方程ax+by=k的通解的形式是什么样的呢?
( ax+by=k 中的k一定是gcd(a,b)的倍数,方程才会有解)
具体推导:扩展欧几里得求通解、最小正整数解
因为由 ax+by=gcd(a,b) 变成 ax+by=k 方程扩大了 k/gcd(a,b)倍,所以通过扩欧求出来的x0,y0也扩大了相同的倍数
x0=x0* (k/gcd(a,b)) y0=y0* (k/gcd(a,b))