在信息学竞赛(OI)里,最大公约数(Greatest Common Divisor,简称 gcd)指的是两个或多个整数共有约数里最大的那个。例如,12 和 18 的公约数有 1、2、3、6,其中最大公约数是 6。在 C++ 里,计算两个数的最大公约数是一个常见操作,gcd 算法能够高效地算出两个整数的最大公约数。其功能不仅局限于单纯的数学计算,在很多算法和问题的求解中,最大公约数都扮演着关键角色。
计算最大公约数最常用的算法是欧几里得算法(辗转相除法),其原理基于以下定理:
对于任意两个正整数 a 和 b(a > b),它们的最大公约数等于 b 和 a % b(a 除以 b 的余数)的最大公约数,即 gcd(a, b) = gcd(b, a % b)。
证明
设 a = b k + c a=bk+c a = bk + c ,显然有 c = a m o d b c=a \bmod b c = a mod b 。设 d ∣ a , d ∣ b d \mid a,~d \mid b d ∣ a , d ∣ b ,则 c = a − b k , c d = a d − b d k c=a-bk, \frac{c}{d}=\frac{a}{d}-\frac{b}{d}k c = a − bk , d c = d a − d b k 。
由右边的式子可知 c d \frac{c}{d} d c 为整数,即 d ∣ c d \mid c d ∣ c ,所以对于 a , b a,b a , b 的公约数,它也会是 b , a m o d b b,a \bmod b b , a mod b 的公约数。
反过来也需要证明:
设 d ∣ b , d ∣ ( a m o d b ) d \mid b,~d\mid (a \bmod b) d ∣ b , d ∣ ( a mod b ) ,我们还是可以像之前一样得到以下式子 a m o d b d = a d − b d k , a m o d b d + b d k = a d \frac{a\bmod b}{d}=\frac{a}{d}-\frac{b}{d}k,~\frac{a\bmod b}{d}+\frac{b}{d}k=\frac{a}{d} d a mod b = d a − d b k , d a mod b + d b k = d a 。
因为左边式子显然为整数,所以 a d \frac{a}{d} d a 也为整数,即 d ∣ a d \mid a d ∣ a ,所以 b , a m o d b b,a\bmod b b , a mod b 的公约数也是 a , b a,b a , b 的公约数。
既然两式公约数都是相同的,那么最大公约数也会相同。
所以得到式子 gcd ( a , b ) = gcd ( b , a m o d b ) \gcd(a,b)=\gcd(b,a\bmod b) g cd( a , b ) = g cd( b , a mod b )
当 b 为 0 时,a 和 0 的最大公约数就是 a,这就是算法的终止条件。
给定两个正整数 a 和 b,求出它们的最大公约数。
输入 :
一行,包含两个正整数 a 和 b,用空格分隔。
示例输入 :
输出 :
输出一个整数,表示 a 和 b 的最大公约数。
示例输出 :
示例代码
#include <iostream>
using namespace std ;
// 欧几里得算法计算最大公约数
int gcd ( int a , int b ) {
return b == 0 ? a : gcd (b, a % b);
}
int main () {
int a, b;
// 读入两个正整数
cin >> a >> b;
// 计算最大公约数
int res = gcd (a, b);
// 输出结果
cout << res << endl;
return 0 ;
}
欧几里得算法的时间复杂度是 O ( log ( min ( a , b ) ) ) O(\log(\min(a, b))) O ( log ( min ( a , b ))) 。这是因为在每一次递归调用中,两个数中的较大数至少会减少一半。可以通过数学推导证明,在最坏情况下,每次递归后,问题规模会以指数级的速度缩小,所以总的递归次数是对数级别的。
由于使用了递归实现,递归调用会使用系统栈空间,空间复杂度是 O ( log ( min ( a , b ) ) ) O(\log(\min(a, b))) O ( log ( min ( a , b ))) ,主要取决于递归的深度。
一组整数的公倍数,是指同时是这组数中每一个数的倍数的数。0 是任意一组整数的公倍数。
一组整数的最小公倍数,是指所有正的公倍数里面,最小的一个数。
对整数 a , b a,b a , b ,将其最小公倍数记为 lcm ( a , b ) \operatorname{lcm}(a,b) lcm ( a , b ) ,不引起歧义时可简写为 [ a , b ] [a,b] [ a , b ] 。
对整数 a 1 , … , a n a_1,\dots,a_n a 1 , … , a n ,将其最小公倍数记为 lcm ( a 1 , … , a n ) \operatorname{lcm}(a_1,\dots,a_n) lcm ( a 1 , … , a n ) ,不引起歧义时可简写为 [ a 1 , … , a n ] [a_1,\dots,a_n] [ a 1 , … , a n ] 。
设 a = p 1 k a 1 p 2 k a 2 ⋯ p s k a s a = p_1^{k_{a_1}}p_2^{k_{a_2}} \cdots p_s^{k_{a_s}} a = p 1 k a 1 p 2 k a 2 ⋯ p s k a s ,b = p 1 k b 1 p 2 k b 2 ⋯ p s k b s b = p_1^{k_{b_1}}p_2^{k_{b_2}} \cdots p_s^{k_{b_s}} b = p 1 k b 1 p 2 k b 2 ⋯ p s k b s
我们发现,对于 a a a 和 b b b 的情况,二者的最大公约数等于
p 1 min ( k a 1 , k b 1 ) p 2 min ( k a 2 , k b 2 ) ⋯ p s min ( k a s , k b s ) p_1^{\min(k_{a_1}, k_{b_1})}p_2^{\min(k_{a_2}, k_{b_2})} \cdots p_s^{\min(k_{a_s}, k_{b_s})} p 1 m i n ( k a 1 , k b 1 ) p 2 m i n ( k a 2 , k b 2 ) ⋯ p s m i n ( k a s , k b s )
最小公倍数等于
p 1 max ( k a 1 , k b 1 ) p 2 max ( k a 2 , k b 2 ) ⋯ p s max ( k a s , k b s ) p_1^{\max(k_{a_1}, k_{b_1})}p_2^{\max(k_{a_2}, k_{b_2})} \cdots p_s^{\max(k_{a_s}, k_{b_s})} p 1 m a x ( k a 1 , k b 1 ) p 2 m a x ( k a 2 , k b 2 ) ⋯ p s m a x ( k a s , k b s )
由于 k a + k b = max ( k a , k b ) + min ( k a , k b ) k_a + k_b = \max(k_a, k_b) + \min(k_a, k_b) k a + k b = max ( k a , k b ) + min ( k a , k b )
所以得到结论是 gcd ( a , b ) × lcm ( a , b ) = a × b \gcd(a, b) \times \operatorname{lcm}(a, b) = a \times b g cd( a , b ) × lcm ( a , b ) = a × b
要求两个数的最小公倍数,先求出最大公约数即可。