根据教师课件,写成本文。未经允许,禁止转载。
\(\gcd\),即最大公约数,指的是几个整数中公有的约数中最大的一个。
问题
已知 \(a,b\),求 \(\gcd(a,b)\)。
方法一:枚举法
从 \(\min(a,b)\) 到 \(1\) 枚举,找到第一个 \(x\) 符合题意,就退出循环。
时间复杂度 \(\mathcal O(\min(a,b))\)。
方法二:分解质因数
令 \(a=p_1^{x_1}p_2^{x_2}\dots p_n^{x_n}\),\(b=p_1^{y_1}p_2^{y_2}\dots p_n^{y_n}\),其中\(x_i,y_i\ge0\) 且不同时为 0。
则 \(\gcd(a,b)=p_1^{\min(x_1,y_1)}p_2^{\min(x_2,y_2)}\dots p_3^{\min(x_3,y_3)}\)。
时间复杂度 \(\mathcal O(\sqrt{\min(a,b)})\)。
void Gcd()
{
for(int x=2;x*x<=min(a,b);x++)
{
while (a % x==0 && b % x==0) {a/=x;b/=x;ans*=x;}
while (a % x==0)a/=x;
while (b % x==0)b/=x;
}
if (a % b==0)ans*=b;
else if (b % a==0)ans*=a;
printf("%d",ans);
}
方法三:辗转相除(欧几里得算法)
定理:\(\gcd(a,b)=\gcd(b,a\%b)\)。
证明:
设 \(\gcd(a,b)=p\),则有 \(a=a'*p,b=b'*p,\gcd(a',b')=1\)。
\(a\%b=a-\lfloor\frac{a}{b}\rfloor *b=a'*p-\lfloor\frac{a}{b}\rfloor*b'*p=p*(a'-\lfloor\frac{a}{b}\rfloor*b')\)。
\(\gcd(b,a\%b)=\gcd(b'*p,p*(a'-\lfloor\frac{a}{b}\rfloor*b'))=p*\gcd(b',a'-\lfloor\frac{a}{b}\rfloor*b')\)。
现证明 \(\gcd(b',a'-\lfloor\frac{a}{b}\rfloor*b')=1\),使用反证法。假设 \(gcd(b',a'-\lfloor\frac{a}{b}\rfloor*b')=t(t>1)\)。
\(b'=b''*t\),\(a'-\lfloor\frac{a}{b}\rfloor*b'=c'*t\)。
\(a'=c'*t+\lfloor\frac{a}{b}\rfloor*b'=c'*t+\lfloor\frac{a}{b}\rfloor*b''*t=t*(c'+\lfloor\frac{a}{b}\rfloor*b'')\)。
则 \(\gcd(a',b')\ge p\),与 \(\gcd(a',b')=1\) 矛盾,因此 \(\gcd(b',a'-\lfloor\frac{a}{b}\rfloor*b')=1\)。
\(\gcd(b,a\%b)=p=\gcd(a,b)\)。
时间复杂度 \(\mathcal O(\log(\max(a,b)))\)。
分析:
-
设 \(a>b\)。
若 \(a>2b\),则 \(b\le \frac{a}{2}\),规模减小一半。反之 \(a<2b\),则 \(a\%b<\frac{a}{2}\)。
因此时间复杂度是 \(\log\) 级别。
-
斐波那契分析:传送门。
int Gcd(int a,int b)
{
if (b==0) return a;
else return Gcd(b,a % b);
}
方法四:二进制法
当 \(a<b\) 时, \(\gcd(a,b)=\gcd(b,a)\)。
当 \(a=b\) 时 \(\gcd(a,b)=a\)。
当 \(a,b\) 同为偶数时,\(\gcd(a,b)=2*\gcd(\frac{a}{2},\frac{b}{2})\)。
当 \(a\) 为偶数,\(b\) 为奇数时,\(\gcd(a,b)=\gcd(\frac{a}{2},b)\)。
当 \(a\) 为奇数,\(b\) 为偶数时,\(\gcd(a,b)=\gcd(a,\frac{b}{2})\)。
当 \(a,b\) 为奇数时,\(\gcd(a,b)=\gcd(a-b,b)\)。
注:此法适合高精度求最大公约数。
int Gcd(int m,int n)
{
if (m==n) return m;
if (m<n) return Gcd(n,m);
if (m & 1==0) return (n & 1==0)? 2*Gcd(m/2,n/2):Gcd(m/2,n);
return (n & 1==0)? Gcd(m,n/2): Gcd(n,m-n);
}