最大公约数是个很常用的概念,例如9和6的最大公约数是3,记作 gcd(9, 6) = 3 ,最小公倍数则为 6×9 mod gcd(6, 9) 。
我们知道,含有两个未知数的二元一次方程可以表示成平面直角坐标系内的一条直线,当拥有两条相交直线我们通过代入消元可以得到唯一解,而只有一条直线时,则有无数个 (x, y) 坐标满足等式。
而针对这样一个方程 ax+by = m ,贝祖定理告诉我们,当该方程有整数解时,当且仅当 m 是 a 和 b 的最大公约数的倍数,可以表示成 ax+by = gcd(a, b)*k (K∈Z) ,例如 9x+6y = 12 有整数解。特别地,方程 ax+by = 1 有整数解当且仅当 a 和 b 互质,即 gcd(a, b) = 1,这个定理在 RSA 加密算法中需要用到。
我们首先看两个数的最大公约数的求法,常用的方法是辗转相减法(又称更相减损术)和辗转相除法(又称欧几里得算法)。
以C语言代码的形式展示两种算法。
1 #include <stdio.h> 2 3 int main() { 4 int a, b,r ; 5 scanf("%d%d", &a, &b); 6 7 // 辗转相减法 8 // while(a != b) { 9 // a>b ? a-=b : b-=a; 10 // } 11 12 // 辗转相除法 13 r = 1; 14 while(r) { 15 r = a % b; 16 a = b; 17 b = r; 18 } 19 20 printf("%d", a); 21 return 0; 22 }
辗转相减法简单且容易记,每次以大数减小数,当两数减到相等时即为最大公约数。但效率不及辗转相除法,例如求 2000000000 与 1 的最大公约数,辗转相减法需要让 2000000000 不断自减一,直到等于 1 ,得到最大公约数 1 ,总共循环 2000000000-1 轮;辗转相除法则只需要一轮就可完成计算。
以下给出求若干正整数的最大公约数的C语言代码。
1 #include <stdio.h> 2 3 #define MAX_LENGTH 3000 4 5 int GCD(int *num, int n); 6 7 /* 8 8 9 26 39 13 65 52 104 78 91 10 */ 11 int main() { 12 int i, j, n, num[MAX_LENGTH], gcd; 13 14 // 1. 数据输入 15 scanf("%d", &n); 16 for(i=0; i<n; i++) { 17 scanf("%d", &num[i]); 18 } 19 20 // 2. 获取该批数据的最大公约数 21 gcd = GCD(num, n); 22 printf("%d", gcd); 23 24 return 0; 25 } 26 27 int GCD(int *num, int n) { 28 int i, j, r, gcd=num[0]; 29 30 for(i=1; i<n; i++) { 31 // 辗转相减法 32 // while(gcd != num[i]) { 33 // gcd>num[i] ? gcd-=num[i] : num[i]-=gcd; 34 // } 35 36 // 辗转相除法 37 r = 1; 38 while(r) { 39 r = gcd % num[i]; 40 gcd = num[i]; 41 num[i] = r; 42 } 43 } 44 return gcd; 45 }若干正整数的最大公约数