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\),则有
由右边的式子可知\(\frac{c}{d}\) 为整数,即d|c,所以对于a,b的公约数,也会是b,a mod b的公约数。
反过来证明:
设 d|b,d|(a mod b),可以像以前一样得到一下式子
显然左边式子是整数,所以\(\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)\)的一组可行解。
过程:
设
由欧几里得定理可知:\(\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)\)
所以
因为 \(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;
}

浙公网安备 33010602011771号