學習:數學----gcd及擴展gcd

 

gcd及擴展gcd能夠用來求兩個數的最大公因數,擴展gcd甚至能夠用來求一次不定方程ax+by=c的解ios

 

展轉相除法與gcd


 

假設有兩個數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 }
gcd

 

 

 

裴蜀定理


 

  ax + by = m
  有解當且僅當m是gcd(a,b)的倍數。裴蜀等式有解時必然有無窮多個整數解,每組解x、y都稱爲裴蜀數,可用展轉相除法求得。

 

重要推論:get

  a,b互質的充要條件是存在整數x,y使ax+by=1.

特別的:

  方程 ax + by = 1 有解當且僅當整數a和b互素。


 

 

 

擴展gcd


 

前言

  擴展gcd是在普通的gcd上作了一些升級,擴展gcd不只能夠求出a和b最大公約數,還能夠求出ax+by=gcd(a,b)的一組解,進而能夠求出ax+by=gcd(a,b)以及ax+by=c的解集

 

 

求ax+by=gcd(a,b)的一組解(擴展gcd

經過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;

 

 

求ax+by=c的一組解

  經過擴展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及ax+by=gcd(a,b)的解集表示

  假設咱們得出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

 

 

求ax+by=c的非負整數解(important

  非負整數解: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

相關文章
相關標籤/搜索