歐幾里得算法:算法
1.定義:gcd的意思是最大公約數,一般用擴展歐幾里得算法求ide
原理:gcd(a, b)=gcd(b, a%b)spa
2.證實:code
令d=gcd(a, b) => a=m*d,b=n*dblog
則m*d=t*n*d+a%b => a%b=d*(m-t*n)遞歸
gcd(b, a%b)=gcd(n*d, (m-t*n)*d)class
令gcd(n, m-t*n)=e => n=x*e,m-t*n=y*e變量
則m-x*e*n=y*e => m=e*(x*n+y)原理
由gcd(n, m)=1知gcd(e*(x*n+y), e*x)=1擴展
故e=1
故gcd(n*d, (m-t*n)*d)=d即gcd(b, a%b)=gcd(a, b)
3.邊界:
當b=0時return a
能夠視爲gcd(a, 0)=a,任何數都能整除0
也能夠視爲gcd(a, b)=b,這裏的a和b是上一層的,知足a%b=0
4.特殊狀況:
當a<b時,a%b=a,因此在下一層gcd(b, a%b)中至關於把a與b交換
5.代碼:
1 int gcd(int a,int b){ return b ? gcd(b,a%b) : a;}
擴展歐幾里得算法:
1.丟番圖方程:
有一個或者幾個變量的整係數方程,它們的求解僅僅在整數範圍內進行。
擴展歐幾里得算法研究的是形如 a*x+b*y=c 的丟番圖方程的解
2.裴蜀定理:
對於正整數a和b,令gcd(a, b)=d,則對於任意整數x和y,都有d|(a*x+b*y)
證實:
令a=n*d,b=m*d,則a*x+b*y=d*n*x+d*m*y
顯然d|(d*n*x+d*m*y)
3.引理:
丟番圖方程 a*x+b*y=c 有解當且僅當d|c
對於任意整數x和y,a*x+b*y的最小正值爲gcd(a, b)
證實:
①必要性:
由裴蜀定理,不存在整數x和y,使得d不整除(a*x+b*y)
②充分性:
要證a*x+b*y=c有解,只需a*x+b*y=d有解
令對於任意整數x和y,a*x+b*y能獲得的最小正值爲s
由裴蜀定理,d|s,則d<=s
令q=⌊a/s⌋,p=a%s
則p=a-q*(a*x+b*y)=a*(1-q*x)-q*b*y=a*(1-q*x)+b*(-q*y)
由p=a%s知0<=p<s
又s爲a*x+b*y能獲得的最小正值
故p=0,即s|a
同理,s|b,即s|d,故s<=d
綜上,s=d
即對於任意整數x和y,a*x+b*y能獲得的最小正值爲d
故存在整數x和y,使a*x+b*y=d
即存在整數x和y,使a*x+b*y=c
4.擴展歐幾里得算法:
一般將求解a*x+b*y=c轉化爲求解a*x+b*y=gcd(a, b),得解後乘上c/gcd(a, b)便可
令
a*x1+b*y1=gcd(a, b)
b*x2+(a%b)*y2=gcd(b, a%b)
由gcd(a,b)=gcd(b,a%b)知
a*x1+b*y1=b*x2+(a%b)*y2
=b*x2+(a-b*⌊a/b⌋)*y2=a*y2+b*(x2-⌊a/b⌋*y2)
故x1=y2,y1=(x2-⌊a/b⌋*y2)
如此遞歸直至邊界狀況
5.邊界:
當b=0時,gcd(a, b)=a(任何數都能整除0)
a*x+b*y=a*x=gcd(a, b)*x
若使a*x+b*y=gcd(a, b),只需x=1,y能夠爲任何值,一般設爲0,減小溢出的風險
y的多值對應方程的多解
6.通解:
對於對於第一個解x0和y0,其餘解能夠表示爲x0+(b/d)*k和y0-(a/d)*k
推導:
令a*(x+m)+b*(x-n)=d
=> a*m=b*n => m/n=b/a
因gcd(a, b)=d,m和n均爲整數
故m和n的最小值分別爲b/d和a/d
若要求其中一個解爲正整數,可在獲得負解後用通解轉化爲正數
7.代碼:
1 void exgcd(int a,int b,int &x,int &y){ 2 if(!b){ 3 x=1,y=0; 4 return ; 5 } 6 exgcd(b,a%b,x,y); 7 int z=x; 8 x=y,y=(z-a/b*y); 9 }
8.易錯點:
算法中存在乘法,有溢出的風險,應見機開long long
例題:
洛谷4549 裴蜀定理
洛谷1516 青蛙的約會
洛谷3951 小凱的疑惑
洛谷1082 同餘方程