首页 > 代码库 > 扩展欧几里德算法及其应用
扩展欧几里德算法及其应用
接着欧几里德算法往后写,扩展欧几里德算法常常用来解不定方程及一些相关的应用,用到的思想就是欧几里德算法的思想:通过在结果不改变的情况下不断取余而逐步缩小数据规模,两个数会不断变小,直到减小到一个数是另一个数的倍数的时候,就很容易求出他们的最小公倍数了。下面我们来说说扩展欧几里德的思想:
我们要求出 a * x + b * y = c 的所有解,其中 a,b,c 是已经告诉你的常数,而 x,y 是待求的未知量,我们要求出所有的 x,y ,其实只要求出一组 x0,y0就行了,剩下的解可以由 x=x0+k*(b*gcd(a,b)/a) , y=y0+k*(a*gcd(a,b)/b)求出,其中 k 为任意整数。
而怎么求出一组 a * x + b * y = c 的解呢?首先判断是否有解的条件是 c%gcd(a,b) 是否等于 0。也就是说 c一定要是 a,b 的最大公约数的整数倍才有解,否则就没有整数解,这个只要自习想一想就可以理解,所以,我们只要求出 a * x + b * y = gcd(a,b) 的一组解最后再乘以 c/gcd(a,b) 就好了。接下来我们说怎么求 a * x + b * y = gcd(a,b)的解。
到了重头戏,求 a * x + b * y = gcd(a,b)的解,按照本文开头提到的想法,不断缩小数据规模,首先,在前面证明欧几里得算法的时候我们证明了 gcd(a,b)=gcd(a,a%b),现在我们提出另一个一个方程 b * x1 +a%b * y1 = gcd( b,a%b) ,这里的 a,b,x,y 还是上一个方程的 a,b,x,y ,但是可以看出,这个方程的系数比上一个方程小了,所以只要我们得到这个方程和原方程是解的关系,我们就可以利用这个方程化简数据规模,从而递归地求出方程的解。于是联立两个方程我们得到了 : x= y1; y=x1-a/b*y1;所以我们只要按照这个方法把x1,y1当成新的x,y再次递归下去就可以求出解。当然我们还要说一下递归的终止条件就是 有一个系数为0,这样就可以求出一组目前方程的确定的 x,y了(不是最终解),当然因为a始终大于b所以肯定是b先为0,这样就得到了退出递归的条件。最终得到的x,y就是a * x + b * y = gcd(a,b)的一组解了。
代码实现,C++,递归实现,这个是一开始写的,比较容易理解,但是常数大了一些:
1 void Exgcd(int a,int b,int &x,int &y) 2 { 3 if(b==0) 4 { 5 x=1; 6 y=0; 7 return ; 8 } 9 Exgcd(b,a%b,x,y);10 int t=x;11 x=y;12 y=t-a/b*y;13 return ;14 }
后来在网上看到了另一种写法,这种写法比较好写,常数也小,推荐:
1 void Exgcd(int a,int b,int&x,int&y) 2 {3 if(!b) { x=1,y=0; return ;}4 Exgcd(b,a%b,y,x);5 y-=a/b*x;6 }
另外还有非递归的版本,因为写起来太麻烦,也没有递归版本效率高,所以就不写了-_-
因为上面只是一个裸的函数,是在确定有解的情况下调用的(事实上不管有没有解调用函数后都会返回一组解),而且返回的解也不是最终解,所以我们还可以加一个判断函数来判断是否有解,并将返回的解转化为最终解:
1 int gcd(int a,int b) {return b? gcd(b,a%b):a;} 2 bool check_exgcd(int a,int b,int c,int &x,int &y) 3 { 4 int k=gcd(a,b); 5 if(c%k) 6 return false; 7 Exgcd(a,b,x,y); 8 k=c/k; 9 x*=k; y*=k;10 return true;11 }
扩展欧几里德算法及其应用