gcd以及ex_gcd的总结

gcd以及ex_gcd的总结

转载连接: http://www.xuebuyuan.com/552599.html



ex_gcd()---表示扩展欧几里得算法

gcd()---表示最大公约数,常用方法是欧几里得算法

定义1:a和b是两个不全为0的整数,称a与b的公因子中最大的为a和b的最大公约数,用gcd(a,b)来表示。

定义2:a和b是两个非0的整数,称a与b的公倍数中最小的为a和b的最小公倍数,用lcm(a,b)来表示。

最小公倍数与最大公约数之间的性质:

    1、若a|m,b|m,那么lcm(a,b)|m

    2、若d|a,d|b,则d|gcd(a,b)

    3、lcm(a,b)=a/gcd(a,b)*b

   4、设m,a,b是正整数,则lcm(ma,mb)=m*gcd(a,b)

   5、若m是非0整数,a1,a2……an的公倍数,则lcm(a1,a2……an)|m

   6、整数a和b素因子分解成a=p1^(r1)*p2^(r2)……pn^(rn),b=p1^(s1)*p2^(s2)……pn^(sn),在这儿·pi是不同的素数

          gcd(a,b)=p1^min(r1,s1)*p2^min(r2,s2)*……pn^min(rn,sn)

          lcm(a,b)=p1^max(r1,s1)*p2^max(r2,s2)*……pn^max(rn,sn)

gcd的求解思路----辗转相除法

递归求解

[cpp]  view plain  copy
 print ?
  1. int gcd(int m,int n)  
  2. {  
  3.     if(n==0)  
  4.     return m;  
  5.     else  
  6.     return gcd(n,m%n);  
  7. }  

迭代求解

[cpp]  view plain  copy
 print ?
  1. int gcd(int m,int n)  
  2. {  
  3.     int temp;  
  4.     if(m<n)  
  5.     {  
  6.         swap(m,n);  
  7.     }  
  8.     while((temp=m%n)!=0)  
  9.     {  
  10.         m=n;  
  11.         n=temp;  
  12.     }  
  13.     return n;  
  14. }  

HDU1222

题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=1222

题意:假设步长是m 总长度是N, 这里首先我们计算出他们的周期,也就是最小公倍数,当过了这个周期,狼搜索的点肯定重复了!

因为狼每走一次肯定是搜索了一个点,搜索完n个点的时候,走的长度正好是这个周期,说明他没有重复的搜索了N个点!这就转化到gcd

[cpp]  view plain  copy
 print ?
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<cmath>  
  4. #include<cstring>  
  5. #include<algorithm>  
  6. using namespace std;  
  7. /* 
  8. int gcd(int m,int n) 
  9. { 
  10.     int temp; 
  11.     if(m<n) 
  12.     { 
  13.         swap(m,n); 
  14.     } 
  15.     while((temp=m%n)!=0) 
  16.     { 
  17.         m=n; 
  18.         n=temp; 
  19.     } 
  20.     return n; 
  21. } 
  22. */  
  23. int gcd(int m,int n)  
  24. {  
  25.     if(n==0)  
  26.     return m;  
  27.     else  
  28.     return gcd(n,m%n);  
  29. }  
  30. int main()  
  31. {  
  32.     int m,n;  
  33.     int T;  
  34.     scanf("%d",&T);  
  35.     while(T--)  
  36.     {  
  37.         scanf("%d%d",&m,&n);  
  38.         if(gcd(m,n)==1)  
  39.         printf("NO\n");  
  40.         else  
  41.         printf("YES\n");  
  42.     }  
  43.     return 0;  
  44. }  

HDU1108

题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=1108

题意:很简单,就是求最小公倍数,利用性质3就可以

[cpp]  view plain  copy
 print ?
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<cstring>  
  4. #include<cmath>  
  5. using namespace std;  
  6.   
  7. int gcd(int a,int b)  
  8. {  
  9.     if(b==0)  
  10.     return a;  
  11.     else  
  12.     return gcd(b,a%b);  
  13. }  
  14.   
  15. int main()  
  16. {  
  17.     int a,b;  
  18.     int k;  
  19.     while(cin>>a>>b)  
  20.     {  
  21.         k=a*b/gcd(a,b);  
  22.         cout<<k<<endl;  
  23.     }  
  24.     return 0;  
  25. }  

扩展欧几里得算法:

设 a>b。
1,显然当 b=0,gcd(a,b)=a。此时 x=1,y=0;
2,ab!=0 时
设 ax1+by1=gcd(a,b);
bx2+(a mod b)y2=gcd(b,a mod b);
根据朴素的 欧几里德原理有 gcd(a,b)=gcd(b,a mod b);
则:ax1+by1=bx2+(a mod b)y2;
即:ax1+by1=bx2+(a-[a/b]*b)y2=ay2+bx2-(a/b)*by2;
根据恒等定理得:x1=y2; y1=x2-[a/b]*y2;
这样我们就得到了求解 x1,y1 的方法:x1,y1 的值基于 x2,y2.

在通过循环就可以写出代码

[cpp]  view plain  copy
 print ?
  1. long long ex_gcd(long long a,long long b,long long &x,long long &y)  
  2. {  
  3.     if(b==0)  
  4.     {  
  5.         x=1;  
  6.         y=0;  
  7.         return a;  
  8.     }  
  9.     long long r=ex_gcd(b,a%b,x,y);  
  10.     long long t=x;  
  11.     x=y;  
  12.     y=t-a/b*y;  
  13.     return r;  
  14. }  

HDU4180

题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=4180

题意: 给出分数(a/b)求另一个分数(c/d){d < b),且满足fabs(a/b - c/d)最小。 ab互质时 最小时满足 bc+1 = ad || bc = ad+1,正符合扩展欧几里得。求出两个d后比较。 

[cpp]  view plain  copy
 print ?
  1. #include<iostream>  
  2. #include<cstdio>  
  3. using namespace std;  
  4. int ex_gcd(int a,int b,int &x,int &y)  
  5. {  
  6.     if(b==0)  
  7.     {  
  8.         x=1;  
  9.         y=0;  
  10.         return a;  
  11.     }  
  12.     int r=ex_gcd(b,a%b,x,y);  
  13.     int t=x;  
  14.     x=y;  
  15.     y=t-a/b*y;  
  16.     return r;  
  17. }  
  18. int main()  
  19. {  
  20.     int t;  
  21.     cin>>t;  
  22.     int a,b;  
  23.     int x,y;  
  24.     while(t--)  
  25.     {  
  26.         scanf("%d/%d",&a,&b);  
  27.         int gcd=ex_gcd(a,b,x,y);  
  28.         if(gcd!=1)  
  29.         {  
  30.             printf("%d/%d\n",a/gcd,b/gcd);  
  31.             continue;//这儿必须要写啊 不然会wa的  
  32.         }  
  33.         if(a==1)  
  34.         {  
  35.             printf("%d/%d\n",a,b-1);  
  36.             continue;  
  37.         }  
  38.         int c1=(-y+a)%a;  
  39.         int c2=(y+a)%a;  
  40.         int d1=(x+b)%b;  
  41.         int d2=(-x+b)%b;  
  42.         if(d1<d2)  
  43.             printf("%d/%d\n",c2,d2);  
  44.         else  
  45.             printf("%d/%d\n",c1,d1);  
  46.     }  
  47.     return 0;  
  48. }  

POJ1061

题目连接:http://poj.org/problem?id=1061

题意:两只青蛙跳了t步,A的坐标为x+m*t,B的坐标为y+n*t,他们相遇的条件为:x+m*t-y-n*t=p*L,即得到:(n-m)*t+L*p=(x-y)

这时求最小的t就是解一次同余方程(n-m)*t+L*p=(x-y)的最小整数解

[cpp]  view plain  copy
 print ?
  1. #include<cstdio>  
  2. #include<algorithm>  
  3. #include<iostream>  
  4. #include<cmath>  
  5. using namespace std;  
  6.   
  7. long long gcd(long long a,long long b)  
  8. {  
  9.     if(!b)return a;  
  10.     else return gcd(b,a%b);  
  11. }  
  12.   
  13. void ex_gcd(long long a,long long b,long long &x,long long &y)  
  14. {  
  15.     if(b==0)  
  16.     {  
  17.         x=1;  
  18.         y=0;  
  19.         return ;  
  20.     }  
  21.     ex_gcd(b,a%b,x,y);  
  22.     long long t=x;  
  23.     x=y;  
  24.     y=t-a/b*y;  
  25. }  
  26.   
  27. int main()  
  28. {  
  29.     long long x,y,m,n,l;  
  30.     long long k1,k2;  
  31.     long long t;  
  32.     long long a,b,c,g;  
  33.     while(cin>>x>>y>>m>>n>>l)  
  34.     {  
  35.         a=n-m;  
  36.         b=l;  
  37.         c=x-y;  
  38.         g=gcd(a,b);  
  39.         if(c%g)  
  40.         {  
  41.             cout<<"Impossible"<<endl;  
  42.             continue;  
  43.         }  
  44.         a=a/g;  
  45.         b=b/g;  
  46.         c=c/g;  
  47.         ex_gcd(a,b,k1,k2);  
  48.         t=c*k1/b;  
  49.         k1=c*k1-b*t;  
  50.         if(k1<0)  
  51.         k1+=b;  
  52.         cout<<k1<<endl;  
  53.     }  
  54.     return 0;  
  55. }  

扩展欧几里得算法应用比较广泛,很多算法都用到了它,比如说同余问题等等

欧几里得算法比较适合于__int64范围内数,但是对于很大的数就不适合了,在这儿介绍一种适合大数的求最大公约数的算法,它的名字就是Stein算法,它是对欧几里得算法的一种改进,适合超过64位的整数,它只有移位和加法操作,它的算法思想就是 

                                     gcd(a,a)=a,  gcd(k*a,k*b)=k*gcd(a,b)

                                     当k与b互素时,gcd(k*a,b)=gcd(a,b);

算法步骤:

1、如果a=b,那么a或者b是最大公约数,算法结束

2、如果a=0,b是最大公约数,算法结束

3、如果b=0,a是最大公约数,算法结束

4、设置a[1]=a,b[1]=b和c[1]=1

5、如果a[n]和b[n]都是偶数,则a[n+1]=a[n]/2,b[n+1]=b[n]/2,c[n+1]=c[n]*2

6、如果a[n]是偶数,b[n]不是,则a[n+1]=a[n]/2,b[n+1]=b[n],c[n+1]=c[n]

7、如果b[n]是偶数,a[n]不是,则a[n+1]=a[n],b[n+1]=b[n]/2,c[n+1]=c[n]

8、如果a[n]和b[n]都不是偶数,则a[n+1]=abs(a[n]-b[n])/2,b[n+1]=min(a[n],b[n]),c[n+1]=c[n]

9、n++,转到1

[cpp]  view plain  copy
 print ?
  1. #include<iostream>  
  2. #include<cmath>  
  3. #include<cstdio>  
  4. #include<algorithm>  
  5. using namespace std;  
  6.   
  7. __int64 Stein(__int64 a,__int64 b)  
  8. {  
  9.     if(a<b)swap(a,b);  
  10.     if(b==0)return a;  
  11.     if((a%2==0)&&(b%2==0))return 2*Stein(a/2,b/2);  
  12.     if(a%2==0)return Stein(a/2,b);  
  13.     if(b%2==0)return Stein(a,b/2);  
  14.     return Stein((a-b)/2,b);  
  15. }  
  16.   
  17. int main()  
  18. {  
  19.     __int64 a,b;  
  20.     while(cin>>a>>b)  
  21.     {  
  22.         cout<<Stein(a,b)<<endl;  
  23.     }  
  24.     return 0;  
  25. }  


ex_gcd()---表示扩展欧几里得算法

gcd()---表示最大公约数,常用方法是欧几里得算法

定义1:a和b是两个不全为0的整数,称a与b的公因子中最大的为a和b的最大公约数,用gcd(a,b)来表示。

定义2:a和b是两个非0的整数,称a与b的公倍数中最小的为a和b的最小公倍数,用lcm(a,b)来表示。

最小公倍数与最大公约数之间的性质:

    1、若a|m,b|m,那么lcm(a,b)|m

    2、若d|a,d|b,则d|gcd(a,b)

    3、lcm(a,b)=a/gcd(a,b)*b

   4、设m,a,b是正整数,则lcm(ma,mb)=m*gcd(a,b)

   5、若m是非0整数,a1,a2……an的公倍数,则lcm(a1,a2……an)|m

   6、整数a和b素因子分解成a=p1^(r1)*p2^(r2)……pn^(rn),b=p1^(s1)*p2^(s2)……pn^(sn),在这儿·pi是不同的素数

          gcd(a,b)=p1^min(r1,s1)*p2^min(r2,s2)*……pn^min(rn,sn)

          lcm(a,b)=p1^max(r1,s1)*p2^max(r2,s2)*……pn^max(rn,sn)

gcd的求解思路----辗转相除法

递归求解

[cpp]  view plain  copy
 print ?
  1. int gcd(int m,int n)  
  2. {  
  3.     if(n==0)  
  4.     return m;  
  5.     else  
  6.     return gcd(n,m%n);  
  7. }  

迭代求解

[cpp]  view plain  copy
 print ?
  1. int gcd(int m,int n)  
  2. {  
  3.     int temp;  
  4.     if(m<n)  
  5.     {  
  6.         swap(m,n);  
  7.     }  
  8.     while((temp=m%n)!=0)  
  9.     {  
  10.         m=n;  
  11.         n=temp;  
  12.     }  
  13.     return n;  
  14. }  

HDU1222

题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=1222

题意:假设步长是m 总长度是N, 这里首先我们计算出他们的周期,也就是最小公倍数,当过了这个周期,狼搜索的点肯定重复了!

因为狼每走一次肯定是搜索了一个点,搜索完n个点的时候,走的长度正好是这个周期,说明他没有重复的搜索了N个点!这就转化到gcd

[cpp]  view plain  copy
 print ?
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<cmath>  
  4. #include<cstring>  
  5. #include<algorithm>  
  6. using namespace std;  
  7. /* 
  8. int gcd(int m,int n) 
  9. { 
  10.     int temp; 
  11.     if(m<n) 
  12.     { 
  13.         swap(m,n); 
  14.     } 
  15.     while((temp=m%n)!=0) 
  16.     { 
  17.         m=n; 
  18.         n=temp; 
  19.     } 
  20.     return n; 
  21. } 
  22. */  
  23. int gcd(int m,int n)  
  24. {  
  25.     if(n==0)  
  26.     return m;  
  27.     else  
  28.     return gcd(n,m%n);  
  29. }  
  30. int main()  
  31. {  
  32.     int m,n;  
  33.     int T;  
  34.     scanf("%d",&T);  
  35.     while(T--)  
  36.     {  
  37.         scanf("%d%d",&m,&n);  
  38.         if(gcd(m,n)==1)  
  39.         printf("NO\n");  
  40.         else  
  41.         printf("YES\n");  
  42.     }  
  43.     return 0;  
  44. }  

HDU1108

题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=1108

题意:很简单,就是求最小公倍数,利用性质3就可以

[cpp]  view plain  copy
 print ?
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<cstring>  
  4. #include<cmath>  
  5. using namespace std;  
  6.   
  7. int gcd(int a,int b)  
  8. {  
  9.     if(b==0)  
  10.     return a;  
  11.     else  
  12.     return gcd(b,a%b);  
  13. }  
  14.   
  15. int main()  
  16. {  
  17.     int a,b;  
  18.     int k;  
  19.     while(cin>>a>>b)  
  20.     {  
  21.         k=a*b/gcd(a,b);  
  22.         cout<<k<<endl;  
  23.     }  
  24.     return 0;  
  25. }  

扩展欧几里得算法:

设 a>b。
1,显然当 b=0,gcd(a,b)=a。此时 x=1,y=0;
2,ab!=0 时
设 ax1+by1=gcd(a,b);
bx2+(a mod b)y2=gcd(b,a mod b);
根据朴素的 欧几里德原理有 gcd(a,b)=gcd(b,a mod b);
则:ax1+by1=bx2+(a mod b)y2;
即:ax1+by1=bx2+(a-[a/b]*b)y2=ay2+bx2-(a/b)*by2;
根据恒等定理得:x1=y2; y1=x2-[a/b]*y2;
这样我们就得到了求解 x1,y1 的方法:x1,y1 的值基于 x2,y2.

在通过循环就可以写出代码

[cpp]  view plain  copy
 print ?
  1. long long ex_gcd(long long a,long long b,long long &x,long long &y)  
  2. {  
  3.     if(b==0)  
  4.     {  
  5.         x=1;  
  6.         y=0;  
  7.         return a;  
  8.     }  
  9.     long long r=ex_gcd(b,a%b,x,y);  
  10.     long long t=x;  
  11.     x=y;  
  12.     y=t-a/b*y;  
  13.     return r;  
  14. }  

HDU4180

题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=4180

题意: 给出分数(a/b)求另一个分数(c/d){d < b),且满足fabs(a/b - c/d)最小。 ab互质时 最小时满足 bc+1 = ad || bc = ad+1,正符合扩展欧几里得。求出两个d后比较。 

[cpp]  view plain  copy
 print ?
  1. #include<iostream>  
  2. #include<cstdio>  
  3. using namespace std;  
  4. int ex_gcd(int a,int b,int &x,int &y)  
  5. {  
  6.     if(b==0)  
  7.     {  
  8.         x=1;  
  9.         y=0;  
  10.         return a;  
  11.     }  
  12.     int r=ex_gcd(b,a%b,x,y);  
  13.     int t=x;  
  14.     x=y;  
  15.     y=t-a/b*y;  
  16.     return r;  
  17. }  
  18. int main()  
  19. {  
  20.     int t;  
  21.     cin>>t;  
  22.     int a,b;  
  23.     int x,y;  
  24.     while(t--)  
  25.     {  
  26.         scanf("%d/%d",&a,&b);  
  27.         int gcd=ex_gcd(a,b,x,y);  
  28.         if(gcd!=1)  
  29.         {  
  30.             printf("%d/%d\n",a/gcd,b/gcd);  
  31.             continue;//这儿必须要写啊 不然会wa的  
  32.         }  
  33.         if(a==1)  
  34.         {  
  35.             printf("%d/%d\n",a,b-1);  
  36.             continue;  
  37.         }  
  38.         int c1=(-y+a)%a;  
  39.         int c2=(y+a)%a;  
  40.         int d1=(x+b)%b;  
  41.         int d2=(-x+b)%b;  
  42.         if(d1<d2)  
  43.             printf("%d/%d\n",c2,d2);  
  44.         else  
  45.             printf("%d/%d\n",c1,d1);  
  46.     }  
  47.     return 0;  
  48. }  

POJ1061

题目连接:http://poj.org/problem?id=1061

题意:两只青蛙跳了t步,A的坐标为x+m*t,B的坐标为y+n*t,他们相遇的条件为:x+m*t-y-n*t=p*L,即得到:(n-m)*t+L*p=(x-y)

这时求最小的t就是解一次同余方程(n-m)*t+L*p=(x-y)的最小整数解

[cpp]  view plain  copy
 print ?
  1. #include<cstdio>  
  2. #include<algorithm>  
  3. #include<iostream>  
  4. #include<cmath>  
  5. using namespace std;  
  6.   
  7. long long gcd(long long a,long long b)  
  8. {  
  9.     if(!b)return a;  
  10.     else return gcd(b,a%b);  
  11. }  
  12.   
  13. void ex_gcd(long long a,long long b,long long &x,long long &y)  
  14. {  
  15.     if(b==0)  
  16.     {  
  17.         x=1;  
  18.         y=0;  
  19.         return ;  
  20.     }  
  21.     ex_gcd(b,a%b,x,y);  
  22.     long long t=x;  
  23.     x=y;  
  24.     y=t-a/b*y;  
  25. }  
  26.   
  27. int main()  
  28. {  
  29.     long long x,y,m,n,l;  
  30.     long long k1,k2;  
  31.     long long t;  
  32.     long long a,b,c,g;  
  33.     while(cin>>x>>y>>m>>n>>l)  
  34.     {  
  35.         a=n-m;  
  36.         b=l;  
  37.         c=x-y;  
  38.         g=gcd(a,b);  
  39.         if(c%g)  
  40.         {  
  41.             cout<<"Impossible"<<endl;  
  42.             continue;  
  43.         }  
  44.         a=a/g;  
  45.         b=b/g;  
  46.         c=c/g;  
  47.         ex_gcd(a,b,k1,k2);  
  48.         t=c*k1/b;  
  49.         k1=c*k1-b*t;  
  50.         if(k1<0)  
  51.         k1+=b;  
  52.         cout<<k1<<endl;  
  53.     }  
  54.     return 0;  
  55. }  

扩展欧几里得算法应用比较广泛,很多算法都用到了它,比如说同余问题等等

欧几里得算法比较适合于__int64范围内数,但是对于很大的数就不适合了,在这儿介绍一种适合大数的求最大公约数的算法,它的名字就是Stein算法,它是对欧几里得算法的一种改进,适合超过64位的整数,它只有移位和加法操作,它的算法思想就是 

                                     gcd(a,a)=a,  gcd(k*a,k*b)=k*gcd(a,b)

                                     当k与b互素时,gcd(k*a,b)=gcd(a,b);

算法步骤:

1、如果a=b,那么a或者b是最大公约数,算法结束

2、如果a=0,b是最大公约数,算法结束

3、如果b=0,a是最大公约数,算法结束

4、设置a[1]=a,b[1]=b和c[1]=1

5、如果a[n]和b[n]都是偶数,则a[n+1]=a[n]/2,b[n+1]=b[n]/2,c[n+1]=c[n]*2

6、如果a[n]是偶数,b[n]不是,则a[n+1]=a[n]/2,b[n+1]=b[n],c[n+1]=c[n]

7、如果b[n]是偶数,a[n]不是,则a[n+1]=a[n],b[n+1]=b[n]/2,c[n+1]=c[n]

8、如果a[n]和b[n]都不是偶数,则a[n+1]=abs(a[n]-b[n])/2,b[n+1]=min(a[n],b[n]),c[n+1]=c[n]

9、n++,转到1

[cpp]  view plain  copy
 print ?
  1. #include<iostream>  
  2. #include<cmath>  
  3. #include<cstdio>  
  4. #include<algorithm>  
  5. using namespace std;  
  6.   
  7. __int64 Stein(__int64 a,__int64 b)  
  8. {  
  9.     if(a<b)swap(a,b);  
  10.     if(b==0)return a;  
  11.     if((a%2==0)&&(b%2==0))return 2*Stein(a/2,b/2);  
  12.     if(a%2==0)return Stein(a/2,b);  
  13.     if(b%2==0)return Stein(a,b/2);  
  14.     return Stein((a-b)/2,b);  
  15. }  
  16.   
  17. int main()  
  18. {  
  19.     __int64 a,b;  
  20.     while(cin>>a>>b)  
  21.     {  
  22.         cout<<Stein(a,b)<<endl;  
  23.     }  
  24.     return 0;  
  25. }  

猜你喜欢

转载自blog.csdn.net/crystaljy/article/details/77481259
gcd