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
",gcd(a,b));
12     //    printf("%lld
",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
",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
",a,x);
23 //        printf("a:%lld->x:%lld
b:%lld->y:%lld
",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
",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 }
原文地址:https://www.cnblogs.com/cenariusxz/p/4323872.html