gcd及扩展gcd能够用来求两个数的最大公因数,扩展gcd甚至能够用来求一次不定方程ax+by=c的解ios
假设有两个数a与b,如今要求a与b的最大公因数,咱们能够设
ide
a=b*q+p函数
若是a是与b的最大公约数是gcd(a,b),那么b与p的最大公约数也是gcd(a,b)优化
即spa
gcd(a,b)=gcd(b,p)=gcd(b,a%b),而后咱们令a=b,b=a%b,而后再进行以上步骤.net
以此类推,a与b的值会愈来愈小,直到某一时刻a变成了b的倍数,使得a%b=0,可是后来赋值使得a=b,b=a%b,因此等到b=0的时候,a的值就是原来a与b的最大公约数了。设计
证实在循环过程当中必定存在某一时刻,使得a为b的倍数:code
因为当a为正数(负数)时,a%b的值必定是正数(负数),也就是说,当b的值不断减少,最多也就减少到1(-1),而此时a必定是b的倍数。固然,不必定只有当b减少到1(-1)时,才会出现a%b=0,只要当b等于原来a和b最大公约数的时候,就必定会发生a%b=0blog
1 long long getGcd(long long _a,long long _b){ 2 if(_b==0) return _a; 3 else return getGcd(_b,_a%_b); 4 }
重要推论:get
a,b互质的充要条件是存在整数x,y使ax+by=1.
特别的:
方程 ax + by = 1 有解当且仅当整数a和b互素。
扩展gcd是在普通的gcd上作了一些升级,扩展gcd不只能够求出a和b最大公约数,还能够求出ax+by=gcd(a,b)的一组解,进而能够求出ax+by=gcd(a,b)以及ax+by=c的解集
经过gcd能够知道
gcd(a,b)=gcd(b,a%b)
又由于
ax+by=gcd(a,b)
由普通gcd,能够经过a=b,b=a%b代换得
bx+a%b*y=gcd(b,a%b)
所以
ax+by=bx+a%b*y=bx+(a-[a/b]*b)*y=bx+ay-[a/b]*b*y=ay+(x-[a/b]*b*y)
能够获得x与y的更新式
x'=y,y'=x-[a/b]*b
注意一点,若是当b=0时,那么ax+by=gcd(a,b)=a,此时xy的更新式为
x'=1,y'=0;
经过扩展gcd,能够获得ax'+by'=gcd(a,b)的解x'与y',若是ax+by=c存在整数解,根据裴蜀定理可知必定知足条件c是gcd(a,b)的倍数
所以,将等式左右两边乘c/gcd(a,b),获得:
ax'*c/gcd(a,b)+by'*gcd(a,b)=ax+by=c
因而,获得了ax+by=c的解为:x=x'*c/gcd(a,b),y=y'*c/gcd(a,b),其中x'和y'是经过扩展gcd求出的一组解
假设咱们得出ax+by=c一组解x与y,若是x每减去一个t(t为常数),那么y至少必须加上一个(a'*t/b'(其中a'=a/gcd(a,b),b'=b/gcd(a,b))),才能使等式两边成立,即
a(x-t)+b(y+a'*t/b')=ax+by=c
因为咱们须要变换后的x和y仍然是整数,因为t已是整数,即x-t也必定是整数,如今只需考虑a'*t/b'是否为整数,因为gcd(a',b')=1,即a'必定不是b'的倍数,故必须令t=t*b'(即t必须是b'的倍数),才使得a'*t/b'为整数,即解集为:
x=x-t*b/gcd(a,b),y=y+t*a/gcd(a,b),t为任意整数
特别的,当gcd(a,b)=1,解集为:x=x-t*b,y=y+t*a
非负整数解:x与y都为非负数,且x与y都与0比较接近的解
ps:令gcd(a,b)=gcd,如下计算以前应该先把a=a/gcd,b=b/gcd,由于解集中x是加减b/gcd,同理y,这样能够防止获得的解不是最小解。
考虑三种状况:
(1).当a,b>0,c>0
这种状况可能存在x与y都为非负整数的解,且当x为x解集中最小的非负整数时,若是此时对应的y为非负整数,那么就存在一组最小非负整数解;若是此时y为负数,那么就必定不存在最小非负整数解。
此时
x=(x%b+b)%b,y=(c-a*gcd*x)/(b*gcd)
可是此时x的确的相对于y来讲更接近0,可是y可能并非很接近0,好比解50x+y=100,用这种方法解出来的解为x=0,y=100,相对于x=2,y=0来说并非最优解,此时让y更接近0才得到更好的答案,所以能够优化解,此时:
当a>=b时,y=(y%a+a)%a,x=(c-b*gcd*y)/(a*gcd);
当a<b时,x=(x%b+b)%b,y=(c-a*gcd*x)/(gcd*b);
这样以来,就能够经过a与b的大小,来判断哪一个未知数接近0能够得到更完美的解
(2).当a,b>0,c<0
这种状况必定不会出现最小非负整数解,可是为了让解更加接近0,由于当c>0和当c<0是一种对称状况,若是存在一组x与y,是得当a,b>0,c>0时的最小非负整数解,那么-x与-y就必定是c<0时的最接近0的解。
此时
当a>=b时,y=-(y%a+a)%a,x=(c-b*gcd*y)/(a*gcd);
当a<b时,x=-(x%b+b)%b,y=(c-a*gcd*x)/(b*gcd);
(3).当a>0,b<0 或 a<0,b>0
此时方程能够当作ax-b'y=c(a*b'>0),这时必定存在d与e,使得d-e=c,即ax-b'y=d-e,能够变形为e+ax=d+b'y
这个式子能够看作:
sum1=e+ax,sum2=d+b'y,sum1等于e加上x个a,sum2等于d加上y个b',当sum1与sum2第一次相同(因为a*b'>0,当a与b同小于0时,第一次相同的时sum1=sum=sum,sum必定小于e和d,同理a与b'大于0)时,x与y的值为多少?
当d>e => c>0时,当y>0时,x必定大于0,此时让y取解集中靠近0非负解,获得的x与y必定是最小非负解
此时:x=x%b<0?x%b+(b<0?-b:b):x%b,y=(c-a*gcd*x)/(b*gcd);
当d<e => c<0时,当x>0时,y必定大于0,此时让x取解集中靠近0非负解,获得的x与y必定是最小非负解
此时:y=y%a<0?y%a+(a<0?-a:a):y%a,x=(c-b*gcd*y)/(a*gcd);
(固然,这个状况的求最小解的方法公式也能够获得其余状况的最小解)
求:
1.a与b的最大公因数
2.求ax+by=c的某一个解
3.求ax+by=c的最小正整数解(若是不存在则求靠近0,0的解)
#include <iostream> using namespace std; /*1*/long long getGcd(long long _a,long long _b){ if(_b==0) return _a; else return getGcd(_b,_a%_b); } /*2*/long long getExgcd(long long _a,long long _b,long long &_x,long long &_y){ if(_b==0){ _x=1,_y=0; return _a; } long long _gcd=getExgcd(_b,_a%_b,_x,_y); long long _rx=_x; _x=_y; _y=_rx-_a/_b*_y; return _gcd; } /*3*/long long getAnyCalc(long long _a,long long _b,long long _c,long long &_x,long long &_y){ long long _gcd=getExgcd(_a,_b,_x,_y); if(_c%_gcd) return -1; long long _transit=_c/_gcd; _x*=_transit;_y*=_transit; return _gcd; } /*4*/long long getUpCalc(long long _a,long long _b,long long _c,long long &_x,long long &_y){ long long _gcd=getExgcd(_a,_b,_x,_y); if(_c%_gcd) return -1; long long _transit=_c/_gcd; _x*=_transit;_y*=_transit; long long __a=_a,__b=_b; _a/=_gcd;_b/=_gcd; _x=_x%_b<0?_x%_b+(_b<0?-_b:_b):_x%_b; _y=_y%_a<0?_y%_a+(_a<0?-_a:_a):_y%_a; if(_c<0) _y=(_c-__a*_x)/__b; else _x=(_c-__b*_y)/__a; return _gcd; } int main(){ getGcd(long long a,long long b)//1 getAnyCalc(long long a,long long b,long long c,long long x,long long y)//2,3 getUpCalc(long long a,long long b,long long c,long long x,long long y)//2,4 }
使用说明:函数getGcd(long long a,long long b)求a与b的最大公约数(须要1号函数)
函数getAnyCalc(long long a,long long b,long long c,long long x,long long y)求ax+by=c的某一个解(不存在解返回-1,存在返回gcd(a,b),解x与y传的是引用)(须要2,3号函数)
函数getUpCalc(long long a,long long b,long long c,long long x,long long y)求ax+by=c的最小非负整数解(不存在解返回-1,存在解且不存在最小非负整数解则获得的解为靠近(0,0)的解且返回的是gcd(a,b))(须要2,4号函数)
1.江西财经大学第二届程序设计竞赛同步赛----H-大时钟:https://blog.csdn.net/weixin_43702895/article/details/89422929