gcd模板(歐幾里得與擴展歐幾里得、拓展歐幾里得求逆元)


gcd(歐幾里得算法輾轉相除法):

gcd ( a , b )= d ;

即 d = gcd ( a , b ) = gcd ( b , a mod b );以此式進行遞歸即可。

之前一直愚蠢地以為輾轉相除法輸進去時 a 要大於 b ,現在發現事實上如果 a 小於 b,那第一次就會先交換 a 與 b。

 1 #include<stdio.h>
 2 #define ll long long 
 3 
 4 ll gcd(ll a,ll b){  5     return b==0?a:gcd(b,a%b);  6 }  7 
 8 int main(){  9  ll a,b; 10     while(scanf("%lld%lld",&a,&b)!=EOF){ 11         printf("%lld\n",gcd(a,b)); 12     // printf("%lld\n",a>b?gcd(a,b):gcd(b,a));
13  } 14     return 0; 15 }
View Code

在原基礎上改成循環之后的GCD:

 

1 ll gcd(ll a,ll b){
2     for(;a>0&&b>0;a>b?a%=b:b%=a);
3     return a+b;
4 }

這個代碼是針對非負數范圍的,但除此之外我還糾結了很久,在非負數的范圍內(long long內)與普通遞歸的gcd對拍並沒有發現問題,一直做題的時候也沒有發現有什么問題,但是刷到一題UVA10325,經測試數據中沒有給0或負數,但是用這個WA用遞歸版的AC,並不知道為什么。

所以……還是庫函數/遞歸保平安吧

 

拓展歐幾里得:

當 gcd ( a , b )= d 時,求絕對值和最小的 x , y 使得 x * a + y * b = d ;

d = gcd ( a , b ) = gcd ( b , a mod b );

設:

x1 * a + y1 * b = d ;        ①

x2 * b + y2 * ( a mod b ) = d ;   ②

因為 a mod b = a - ( a / b )* b;  ③(除法為整除)

將③代入①整理得:

y2 * a + ( x2 - ( a / b ) * y2 ) * b = d; ④

由①和④整理得:

x1 = y2 ;

y1 = x2 - ( a / b ) * y2;

將此結論代入遞歸函數既得。

 1 #include<stdio.h>
 2 #define ll long long
 3 
 4 void gcd(ll a,ll b,ll& d,ll& x,ll& y){  5     if(!b){d=a;x=1;y=0;}  6     else {gcd(b,a%b,d,y,x);y-=x*(a/b);}  7 }  8 
 9 int main(){ 10  ll a,b,d,x,y; 11     while(scanf("%lld%lld",&a,&b)!=EOF){ 12  gcd(a,b,d,x,y); 13         printf("%lld*%lld+%lld*%lld=%lld\n",a,x,b,y,d); 14  } 15     return 0; 16 }
View Code

 

拓展歐幾里得求逆元:

當 a 與 b 互素時有 gcd ( a , b ) = 1 ;

即得: a * x + b * y = 1;

a * x ≡ 1 ( mod b );

由於 a 與 b 互素,同余式兩邊可以同除 a ,得:

1 * x ≡ 1 / a (mod b);

因此 x 是 a mod b 的逆元;

 

遞歸方法計算:

 1 #include<stdio.h>
 2 #define ll long long
 3 
 4 ll gcd(ll a,ll b,ll &d,ll& x,ll& y){
 5     if(!b){
 6         d=a;
 7         x=1;
 8         y=0;
 9         return x;
10     }
11     else{
12         gcd(b,a%b,d,y,x);
13         y-=x*(a/b);
14     }
15     return x;
16 }
17 
18 int main(){
19     ll a,b,d,x,y;
20     while(scanf("%lld%lld",&a,&b)!=EOF){
21         x=gcd(a,b,d,x,y);
22         printf("a:%lld->x:%lld\n",a,x);
23 //        printf("a:%lld->x:%lld\nb:%lld->y:%lld\n",a,x,b,y);
24     }
25     return 0;
26 }
View Code

 

循環方法計算:

 1 #include<stdio.h>
 2 
 3 int main(){
 4     int a,b;
 5     while(scanf("%d%d",&a,&b)!=EOF){
 6         int x=1,y=0,t;
 7 
 8         {
 9             if(a!=1&&b!=1){
10                 int b0=b,q;
11                 while(a>1){
12                     q=a/b0;
13                     t=b0;b0=a%b0;a=t;
14                     t=y;y=x-q*y;x=t;
15                 }
16                 if(x<0)x+=b;
17             }
18         }
19 
20         printf("a:%d->x:%d\n",a,x);
21     }
22     return 0;
23 }
View Code

 

 1 ll gcd(ll a,ll b){
 2     if(a!=1&&b!=1){
 3         int b0=b,q,t,x=1,y=0;
 4         while(a>1){
 5             q=a/b0;
 6             t=b0;b0=a%b0;a=t;
 7             t=y;y=x-q*y;x=t;
 8         }
 9         if(x<0)x+=b;
10     }
11     return x;
12 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM