【Math】最大公约数(gcd)

最大公约数

最大公约数即为 Greatest Common Divisor,常缩写为 gcd。

一组数的公约数,是指同时是这组数中每一个数的约数的数。而最大公约数,则是指所有公约数里面最大的一个。

那么如何求最大公约数呢?我们先考虑两个数的情况。

欧几里德算法

如果我们已知两个数 a a a b b b ,如何求出二者的最大公约数呢?

不妨设 a > b a > b a>b

我们发现如果 b b b a a a 的约数,那么 b b b 就是二者的最大公约数。
下面讨论不能整除的情况,即 a = b × q + r a = b \times q + r a=b×q+r ,其中 r < b r < b r<b

我们通过证明可以得到 gcd ⁡ ( a , b ) = gcd ⁡ ( b , a   m o d   b ) \gcd(a,b)=\gcd(b,a \bmod b) gcd(a,b)=gcd(b,amodb) ,过程如下:


a = b k + c a=bk+c a=bk+c ,显然有 c = a   m o d   b c=a \bmod b c=amodb 。设 d ∣ a     d ∣ b d|a\ \ \ d|b da   db ,则 c = a − b k c=a-bk c=abk c d = a d − b d k \frac{c}{d}=\frac{a}{d}-\frac{b}{d}k dc=dadbk 由右边的式子可知 c d \frac{c}{d} dc 为整数,即 d ∣ c d|c dc 所以对于 a , b a,b a,b 的公约数,它也会是 a   m o d   b a \bmod b amodb 的公约数。

反过来也需要证明

d ∣ b     d ∣ ( a   m o d   b ) d|b\ \ \ d|(a \bmod b) db   d(amodb) ,我们还是可以像之前一样得到以下式子 a   m o d   b d = a d − b d k \frac{a\bmod b}{d}=\frac{a}{d}-\frac{b}{d}k damodb=dadbk a   m o d   b d + b d k = a d \frac{a\bmod b}{d}+\frac{b}{d}k=\frac{a}{d} damodb+dbk=da 因为左边式子显然为整数,所以 a d \frac{a}{d} da 也为整数,即 d ∣ a d|a da ,所以 b , a   m o d   b b,a\bmod b b,amodb 的公约数也是 a , b a,b a,b 的公约数。

既然两式公约数都是相同的,那么最大公约数也会相同。

所以得到式子 gcd ⁡ ( a , b ) = gcd ⁡ ( b , a   m o d   b ) \gcd(a,b)=\gcd(b,a\bmod b) gcd(a,b)=gcd(b,amodb)

既然得到了 gcd ⁡ ( a , b ) = gcd ⁡ ( b , r ) \gcd(a, b) = \gcd(b, r) gcd(a,b)=gcd(b,r) ,这里两个数的大小是不会增大的,那么我们也就得到了关于两个数的最大公约数的一个递归求法。

inline int gcd(int a, int b) {
	if(b == 0) return a;
	return gcd(b, a % b);
}

更多写法:https://blog.csdn.net/Ljnoit/article/details/99319849
递归至 b==0 (即上一步的 a%b==0 ) 的情况再返回值即可。

上述算法被称作欧几里德算法(Euclidean algorithm)。

如果两个数 a a a b b b 满足 gcd ⁡ ( a , b ) = 1 \gcd(a, b) = 1 gcd(a,b)=1 ,我们称 a a a b b b 互质。

多个数的最大公约数

那怎么求多个数的最大公约数呢?显然答案一定是每个数的约数,那么也一定是每相邻两个数的约数。我们采用归纳法,可以证明,每次取出两个数求出答案后再放回去,不会对所需要的答案造成影响。

最小公倍数

接下来我们介绍如何求解最小公倍数(Least Common Multiple, LCM)。

两个数的

首先我们介绍这样一个定理——算术基本定理:

每一个正整数都可以表示成若干整数的乘积,这种分解方式在忽略排列次序的条件下是唯一的。

用数学公式来表示就是 x = p 1 k 1 p 2 k 2 ⋯ p s k s x = p_1^{k_1}p_2^{k_2} \cdots p_s^{k_s} x=p1k1p2k2psks

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=p1ka1p2ka2pskas , 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=p1kb1p2kb2pskbs

我们发现,对于 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})} p1min(ka1,kb1)p2min(ka2,kb2)psmin(kas,kbs)

最小公倍数等于

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})} p1max(ka1,kb1)p2max(ka2,kb2)psmax(kas,kbs)

由于 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) ka+kb=max(ka,kb)+min(ka,kb)

所以得到结论是 gcd ⁡ ( a , b ) × lcm ⁡ ( a , b ) = a × b \gcd(a, b) \times \operatorname{lcm}(a, b) = a \times b gcd(a,b)×lcm(a,b)=a×b

要求两个数的最小公倍数,先求出最大公约数即可。

多个数的

可以发现,当我们求出两个数的 g c d gcd gcd 时,求最小公倍数是 O ( 1 ) O(1) O(1) 的复杂度。那么对于多个数,我们其实没有必要求一个共同的最大公约数再去处理,最直接的方法就是,当我们算出两个数的 g c d gcd gcd ,或许在求多个数的 g c d gcd gcd 时候,我们将它放入序列对后面的数继续求解,那么,我们转换一下,直接将最小公倍数放入序列即可。

扩展欧几里得定理

扩展欧几里德定理(Extended Euclidean algorithm, EXGCD),常用于求 a x + b y = gcd ⁡ ( a , b ) ax+by=\gcd(a,b) ax+by=gcd(a,b) 的一组可行解。

证明

a x 1 + b y 1 = gcd ⁡ ( a , b ) ax_1+by_1=\gcd(a,b) ax1+by1=gcd(a,b)

b x 2 + ( a   m o d   b ) y 2 = gcd ⁡ ( b , a   m o d   b ) bx_2+(a\bmod b)y_2=\gcd(b,a\bmod b) bx2+(amodb)y2=gcd(b,amodb)

由欧几里得定理可知: gcd ⁡ ( a , b ) = gcd ⁡ ( b , a   m o d   b ) \gcd(a,b)=\gcd(b,a\bmod b) gcd(a,b)=gcd(b,amodb)

所以 a x 1 + b y 1 = b x 2 + ( a   m o d   b ) y 2 ax_1+by_1=bx_2+(a\bmod b)y_2 ax1+by1=bx2+(amodb)y2

又因为 a   m o d   b = a − ( ⌊ a b ⌋ × b ) a\bmod b=a-(\lfloor\frac{a}{b}\rfloor\times b) amodb=a(ba×b)

所以 a x 1 + b y 1 = b x 2 + ( a − ( ⌊ a b ⌋ × b ) ) y 2 ax_1+by_1=bx_2+(a-(\lfloor\frac{a}{b}\rfloor\times b))y_2 ax1+by1=bx2+(a(ba×b))y2

a x 1 + b y 1 = a y 2 + b x 2 − ⌊ a b ⌋ × b y 2 = a y 2 + b ( x 2 − ⌊ a b ⌋ y 2 ) ax_1+by_1=ay_2+bx_2-\lfloor\frac{a}{b}\rfloor\times by_2=ay_2+b(x_2-\lfloor\frac{a}{b}\rfloor y_2) ax1+by1=ay2+bx2ba×by2=ay2+b(x2bay2)

因为 a = a , b = b a=a,b=b a=a,b=b ,所以 x 1 = y 2 , y 1 = x 2 − ⌊ a b ⌋ y 2 x_1=y_2,y_1=x_2-\lfloor\frac{a}{b}\rfloor y_2 x1=y2,y1=x2bay2

x 2 , y 2 x_2,y_2 x2,y2 不断代入递归求解直至 GCD(最大公约数,下同)为 0 递归 x=1,y=0 回去求解。

inline int exgcd(int a, int b, int &x, int &y) {
	if(!b) {
		x=1;
		y=0;
		return a;
	}
	int d=exgcd(b, a % b, x, y);
	int t=x;
	x=y;
	y=t-(a/b)*y;
	return d;
}

函数返回的值为 GCD,在这个过程中计算 x , y x,y x,y 即可。

  • 19
    点赞
  • 98
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值