3最大公约数

最大公约数

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

一组整数的公约数,是指同时是这组数中每一个数的约数的数。\(\pm 1\) 是任意一组整数的公约数。

一组整数的最大公约数,是指所有公约数里面最大的一个。

对不全为0的整数 \(a,b\),将其最大公约数记为 \(gcd(a,b)\),不引起歧义时可简写为 \((a,b)\)

对不全为 0的整数\(a_1,\dots,a_n\),将其最大公约数记为 \(gcd(a_1,\dots,a_n)\),不引起歧义时可简写为 \((a_1,\dots,a_n)\)

欧几里得算法

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

不妨设\(a\gt b\)

讨论$ a = b*q+r\(,其中\)r\gt b$。

则有 \(gcd(a,b)=gcd(b,a \quad mod \quad b)\)

证明:

\(a = bk + c\),显然$ c = a\quad mod\quad b$。

\(d|a,d|b\),则有

\[\begin{cases} c = a - bk\\ \frac{c}{d}=\frac{a}{d} - \frac{b}{d}k \end{cases} \tag{1} \]

由右边的式子可知\(\frac{c}{d}\) 为整数,即d|c,所以对于a,b的公约数,也会是b,a mod b的公约数。

反过来证明:

设 d|b,d|(a mod b),可以像以前一样得到一下式子

\[\frac{a mod b}{d} = \frac{a}{d} - \frac{b}{d}k\\ \frac{a mod b}{d} + \frac{b}{d}k= \frac{a}{d} \]

显然左边式子是整数,所以\(\frac{a}{d}\),为整数 d|a,所以b,a mod b 的公约数也是a,b的公约数。

常见的gcd的写法:

(1) 递归写法

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

(2) 非递归写法

int gcd(int a, int b)
{
    while(b!=0)
    {
        int temp = a;
        a = b;
        b = temp % b;
    }
    reutrn a;
}

(3)对于C++17版本

# include<numeric>
std::gcd
std::lcm

在输入为两个长为n的二进制整数时,欧几里得算法的时间复杂度为O(n)。(换句话说,在默认a,b同阶的情况下,时间复杂度为\(O(log max(a,b))\)

优化:

(1) 更相减损术

针对大整数,使用加减替代乘除求出最大公约数。

\(a \ge b\),若a = b,则gcd(a,b) = a = b。

否则,\(\forall d|a,d|b\),可以证明\(d|a-b\)

因此,a和b的所有公因数都是a - b 和 b 的公因数,gcd(a,b) = gcd(a-b,b)。

(2) Stein算法的优化

考虑另一个优化,若\(2|a,2|b\)\(gcd(a,b)=2gcd(\frac{a}{2},\frac{b}{2})\)

否则,若\(2\mid a,2\nmid b\)\(gcd(a,b)=gcd(\frac{a}{2},b)\)

优化后的算法时间复杂度为\(O(\log{n})\)

证明:

若$ 2\mid a \(或\) 2\mid b$,每次递归至少会将 a,b 之一减半。

否则,\(2\mid a-b\),回到了上一种情况。

算法最多递归$ O(\log n) $次。

int gcd(int a,int b)
{
    if(a==0) return b;
    if(b==0) return b;
    // countr_zero 表示二进制末位0的个数,也即公因数2的次数
    int atimes = contr_zero(a);
    int btimes = contr_zero(b);
    int mintimes = min(atimes,btimes);
    a >>= atimes;
    while(1)
    {
        b >> = bitmes;
        // 确保 a<=b
        if(a > b) swap(a,b);
    	b -= a;
        if(b==0) break;
        btimes = contr_zero(b);
    }
    reuturn a << mintimes;
}

int countr_zero(Big a) {
  int ans = 0;
  while(!(a & 1)) {
    a >>= 1;
    ++ans;
  }
  return ans;
}

最小公倍数

两个个数

\(a = p_1^{k_{a_1}}p_2^{k_{a_2}} \cdots p_s^{k_{a_s}},\)
\(b = p_1^{k_{b_1}}p_2^{k_{b_2}} \cdots p_s^{k_{b_s}}\)

我们发现,对于 a 和 b 的情况,二者的最大公约数等于

\(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^{\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})}\)

由于$ k_a + k_b = \max(k_a, k_b) + \min(k_a, k_b)$

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

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

扩展欧几里得算法

常用与求\(ax + by = gcd(a,b)\)的一组可行解。

过程:

\[ax_1+by_1=\gcd(a,b)\\ bx_2+(a\bmod b)y_2=\gcd(b,a\bmod b) \]

由欧几里得定理可知:\(\gcd(a,b)=\gcd(b,a\bmod b)\)

所以 \(ax_1+by_1=bx_2+(a\bmod b)y_2\)

又因为

\(a\bmod b=a-(\lfloor\frac{a}{b}\rfloor\times b)\)

所以

\[ax_1+by_1=bx_2+(a-(\lfloor\frac{a}{b}\rfloor\times 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) \]

因为 \(a=a,b=b\),所以

\(x_1=y_2,y_1=x_2-\lfloor\frac{a}{b}\rfloor y_2\)

将 $x_2,y_2 $不断代入递归求解直至 \(\gcd\)(最大公约数,下同)为 0 递归 \(x=1,y=0\) 回去求解。

int exgcd(int a,int b,int  &x,int &y)
{
	int(!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;
}
posted @ 2025-07-18 20:10  星空丶star  阅读(70)  评论(0)    收藏  举报