一行代码求出两个数的最大公约数与最小公倍数
欧几里得算法求最大公约数
如果设两个数为a,b,他们的最大公约数是gcd(a,b),那么可以得出gcd(a,b)= gcd(b,a mod b),于是有下面求两个数最大公约数的欧几里得算法
int gcd (int a,int b){
return b ? gcd(b,a % b): a;//a和0的最大公约数是a 因为0可以整除任何数
}
非常精简,就一句话,必背算法之一。下面来证明为什么gcd(a,b)= gcd(b,a mod b)。
首先按照常识,我们可以知道一个数d 能整除 a,也能整除b ===> d能整除x *a + y * b(x,y为整数,类似线性组合).
而且
因为对于a,b的所有公约数,都能整除a,b所以他们都能整除a mod b,所以得出(1)任何一个a,b的公约数都是b, a mod b的公约数。
同理对于b, a mod b的任意一个公约数k,都能整除b和a mod b ,而且
那么k 肯定能整除
因为可以把这个式子看作是a mod b 与b的线性组合。所以对于b,a mod b的所有公约数都能整除 a,b.所以得出(2)任意b,a mod b的公约数都是a ,b的公约数
由(1)(2)得 a ,b得公约数与b, a mod b的公约数相同 ===> 所以他们的最大公约数肯定相同。即gcd(a,b) = gcd(b,a mod b)。
所有用递归就可以写出这个算法,
注意:当b = 0的时候,a 和0的最大公约数是 a。
求最小公倍数
如果是两个数a,b,最大公约数为c,只要将ab两数相乘,再除以c即可。因为两数相乘时,公约数部分乘了两次,所以要除以公约数一次,所以两个数a,b,最大公约数为c,最小公倍数是ab/c。
所以求最小公倍数的算法如下:
int lcm(int a,int b){
return a * b / gcd(a,b);
}
也是十分精简的算法。
扩展欧几里得算法
裴蜀定理:对于任意正整数a,b一定存在非零整数x,y使得
ax + by = gcd(a,b)
现在有一道题 要求:
给定n对正整数ai,bi,对于每对数,求出一组xi,yi,使其满足ai∗xi+bi∗yi=gcd(ai,bi)。
可以使用扩展欧几里得算法来在运行欧几里得算法的同时,顺便把x,y求出来。
结合欧几里得算法与裴蜀定理可以得:
所以这层x不用变,只要将y更新为 y - (a /b) * x即可
#include <iostream>
using namespace std;
//执行欧几里得算法的同时顺便把x,y给求出来,所以用引用进行传参
int exgcd(int a,int b,int &x,int &y) {
if(!b) {
x = 1, y = 0;//边界情况 就是b = 0的时候,明显公约数是a,而且就是x = 1 y =0 刚好就是1* a + 0 * b =a ;
return a;
}
int d = exgcd(b,a % b,y,x);
y -= (a / b) * x;//这层x不用变,只要将y更新为 y - (a /b) * x即可
return d;
}
int main(){
int n ;
cin >> n;
while(n --) {
int a,b,x,y;
scanf("%d%d",&a,&b);
exgcd(a,b,x,y);
printf("%d %d\n",x,y);
}
return 0;
}