一、輾轉相除法
inline int GCD(int x,int y) { int r=x%y; while(r) x=y,y=r,r=x%y; return y; }
原理證明
- 因為a=b+c,於是b,c的公約數也必然是a的約數,假設(b,c)=e,
- ((b,c)=e表示e為b和c的最大公約數)那么有elb+c,即ela,
- 根據"d是b,c的公約數"知道dle,,
- 又因為e也是a,b的公約數,eld,綜上有e=d
- 可見(a,b)=(b,c)=d
二、二進制算法
可將x,y分為六種情況進行討論:
- 若x=0或y=0:返回y或x;
- 若x<y:交換x,y;
- 若x為偶數且y為偶數:gcd(x/2,y/2);
- 若x為偶數且y為奇數:gcd(x/2,y);
- 若x為奇數且y為偶數:gcd(x,y/2);
- 若x為奇數且y為奇數:gcd(x-y,y);
inline int gcd(int x,int y) { int n=0,m=0; while(!(x&1)) ++n,x>>=1; while(!(y&1)) ++m,y>>=1; n=min(n,m); while(1) { if(x<y) swap(x,y); if(!(x-=y)) return y<<n; while(!(x&1)) x>>=1; } }
三、最小公倍數
幾個數的最小公倍數為幾個數的乘積除以這幾個數的最大公約數
四、擴展歐幾里得
擴展歐幾里德算法是用來在已知a, b求解一組x,y,使它們滿足貝祖等式(裴蜀定理,博客整除這一章有介紹): ax+by = gcd(a, b) =d(解一定存在,根據數論中的相關定理)。擴展歐幾里德常用在求解模線性方程及方程組中。
公式表述1
- gcd(a,b)=gcd(b,a mod b)
- 證明:a可以表示成a = kb + r,則r = a mod b
- 假設d是a,b的一個公約數,則有
- d|a, d|b,而r = a - kb,因此d|r
- 因此d是(b,a mod b)的公約數
- 假設d 是(b,a mod b)的公約數,則
- d | b , d |r ,但是a = kb +r
- 因此d也是(a,b)的公約數
- 因此(a,b)和(b,a mod b)的公約數是一樣的,其最大公約數也必然相等,得證
公式表述2
- 首先根據裴蜀定理得,解一定存在
- 其次因為gcd(a,b)=gcd(b,a%b),所以p*a+q*b=gcd(b,a%b)=p*b+q*a%b=p*b+q*(a-a/b*b)=q*a+(p-a/b*q)*b,這樣它就把a與b的線性組合簡化為b與a%b的線性組合了
- 假根據前面的結論,a,b都在減小,當b減小到0時,得出p=1,其遞歸會最初的p,q就行了
//其中x,y為滿足x*a+y*b=gcd(a,b)的解,ret為gcd(a,b); int exgcd(int a,int b,int &x,int &y) { int ret,temp; if(!b) { x=1,y=0; return a; } ret=exgcd(b,a%b,x,y); temp=x,x=y,y=temp-a/b*y; return ret; }
五、求解線性方程
定理一:對於方程a*x+b*y=c,該方程等價於a*x≡c(mod) b,有整數解的充分必要條件是:c%GCD(a,b)=0。
定理二:若GCD(a,b)=1,且x0,y0為a*x+b*y=c的一組解,則方程的任意解可以表達為 x=x0+b*t,y=y0-a*t,對於任意整數t皆成立
根據定理一,我們可以求出方程a*x0+b*y0=gcd(a,b)的一組解,x0,y0,兩邊同時除以GCD(a,b)再乘以c就得到原方程的一個解
根據定理二,我們就可以求出一組特解(最小整數解),t=b/gcd(a,b),x=(x%t+t)%t;