gcd(欧几里得算法辗转相除法):
gcd ( a , b )= d ;
即 d = gcd ( a , b ) = gcd ( b , a mod b );以此式进行递归即可。
之前一直愚蠢地以为辗转相除法输进去时 a 要大于 b ,现在发现事实上如果 a 小于 b,那第一次就会先交换 a 与 b。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
在原基础上改成循环之后的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;
将此结论代入递归函数既得。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
拓展欧几里得求逆元:
当 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 的逆元;
递归方法计算:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
循环方法计算:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
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 }