最大公约数(gcd)算法

在这里插入图片描述
求最大公约数有两种算法, 欧几里得算法和更相减损法, 这里只讨论欧几里得算法
算法核心gcd⁡(a,b)=gcd⁡(b,amod  b)\gcd(a, b) = \gcd(b, a \mod b)gcd(a,b)=gcd(b,amodb)

算法证明

引理: d  ∣  ad \; | \; ada并且d  ∣  bd \; | \; bdb, 那么x,y,∈Zx, y, \in Zx,y,Z情况下, d  ∣  ax+byd \; | \; ax + bydax+by
证明:
由 d∣a,可设 a=d⋅m(m∈Z)由 d∣b,可设 b=d⋅n(n∈Z)那么:ax+by=(d⋅m)x+(d⋅n)y=d(mx+ny)因为 m,n,x,y∈Z,所以 mx+ny∈Z因此 d∣(ax+by) \begin{aligned} &\text{由 } d \mid a \text{,可设 } a = d \cdot m \quad (m \in \mathbb{Z}) \\ &\text{由 } d \mid b \text{,可设 } b = d \cdot n \quad (n \in \mathbb{Z}) \\ &\text{那么:} \\ &ax + by = (d \cdot m)x + (d \cdot n)y = d(mx + ny) \\ &\text{因为 } m, n, x, y \in \mathbb{Z} \text{,所以 } mx + ny \in \mathbb{Z} \\ &\text{因此 } d \mid (ax + by) \end{aligned}  da,可设 a=dm(mZ) db,可设 b=dn(nZ)那么:ax+by=(dm)x+(dn)y=d(mx+ny)因为 m,n,x,yZ,所以 mx+nyZ因此 d(ax+by)
证毕

根据上述引理, 证明欧几里得算法, 假设u=gcd⁡(a,b)u = \gcd(a, b)u=gcd(a,b), v=gcd⁡(b,amod  b)v = \gcd(b, a \mod b)v=gcd(b,amodb), 证明u=vu = vu=v只需要证明 uuu能推出vvv并且vvv能推出uuu

首先证明uuu推出vvv
证明:

假设最大公约数是ddd, 因为d  ∣  bd \; | \; bdb, 因此等式右边d  ∣  bd \; | \; bdb就是正确的
还需要证明d  ∣  (amod  b)d \; | \; (a \mod b)d(amodb)
amod  ba \mod bamodb展开
amod  b=a−⌊ab⌋⋅b a \mod b = a - \left \lfloor \frac{a}{b} \right \rfloor \cdot bamodb=abab
因为引理说明了d  ∣  ax+byd \; | \; ax + bydax+by, 将xxx设为111, yyy设为⌊ab⌋\left \lfloor \frac{a}{b} \right \rfloorba
得到d  ∣  a−⌊ab⌋⋅bd \; | \; a - \left \lfloor \frac{a}{b} \right \rfloor \cdot bdabab
也就是说d  ∣  (amod  b)d \; | \; (a \mod b)d(amodb)
uuu能推出vvv

再证明vvv推出uuu
证明:

设最大公约数是ddd, 由vvv可知, d  ∣  bd \; | \; bdb, 因此等式左边d  ∣  bd \; | \; bdb就是正确的
还需要证明d  ∣  ad \; | \; ada
由引理可知d  ∣  ax+byd \; | \; ax + bydax+by
vvv展开
d  ∣  bx+(amod  b)yd \; | \; bx + (a \mod b)ydbx+(amodb)y
继续展开
d  ∣  bx+(a−⌊ab⌋⋅b)yd \; | \; bx + (a - \left \lfloor \frac{a}{b} \right \rfloor \cdot b)ydbx+(abab)y
x=⌊ab⌋x = \left \lfloor \frac{a}{b} \right \rfloorx=ba, y=1y = 1y=1, 得到
d  ∣  b⋅(x−⌊ab⌋)+a d \; | \; b\cdot (x - \left \lfloor \frac{a}{b} \right \rfloor) + adb(xba)+a
化简得到
d  ∣  ad \; | \; ada
vvv能推出uuu

因为uuu能推出vvv并且vvv能推出uuu, 所以u=vu = vu=v, 证毕
欧几里得算法时间复杂度O(log⁡k)O(\log k)O(logk)

代码实现

#include <bits/stdc++.h>

using namespace std;

int gcd(int a, int b) {
   
    return b ? gcd(b, a % b) : a;
}

int main() {
   
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    int n;
    cin >> n;
    while (n--) {
   
        int a, b;
        cin >> a >> b;
        cout << gcd(a, b) << '\n';
    }

    return 0;
}