最大公约数
最大公约数即为 Greatest Common Divisor,常缩写为 gcd。
一组数的公约数,是指同时是这组数中每一个数的约数的数。而最大公约数,则是指所有公约数里面最大的一个。
那么如何求最大公约数呢?我们先考虑两个数的情况。
欧几里德算法
如果我们已知两个数
a 和
b ,如何求出二者的最大公约数呢?
不妨设
a>b
我们发现如果
b 是
a 的约数,那么
b 就是二者的最大公约数。
下面讨论不能整除的情况,即
a=b×q+r ,其中
r<b 。
我们通过证明可以得到
gcd(a,b)=gcd(b,amodb) ,过程如下:
设
a=bk+c ,显然有
c=amodb 。设
d∣a d∣b ,则
c=a−bk
dc=da−dbk 由右边的式子可知
dc 为整数,即
d∣c 所以对于
a,b 的公约数,它也会是
amodb 的公约数。
反过来也需要证明
设
d∣b d∣(amodb) ,我们还是可以像之前一样得到以下式子
damodb=da−dbk
damodb+dbk=da 因为左边式子显然为整数,所以
da 也为整数,即
d∣a ,所以
b,amodb 的公约数也是
a,b 的公约数。
既然两式公约数都是相同的,那么最大公约数也会相同。
所以得到式子
gcd(a,b)=gcd(b,amodb)
既然得到了
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 和
b 满足
gcd(a,b)=1 ,我们称
a 和
b 互质。
多个数的最大公约数
那怎么求多个数的最大公约数呢?显然答案一定是每个数的约数,那么也一定是每相邻两个数的约数。我们采用归纳法,可以证明,每次取出两个数求出答案后再放回去,不会对所需要的答案造成影响。
最小公倍数
接下来我们介绍如何求解最小公倍数(Least Common Multiple, LCM)。
两个数的
首先我们介绍这样一个定理——算术基本定理:
每一个正整数都可以表示成若干整数的乘积,这种分解方式在忽略排列次序的条件下是唯一的。
用数学公式来表示就是
x=p1k1p2k2⋯psks
设
a=p1ka1p2ka2⋯pskas ,
b=p1kb1p2kb2⋯pskbs
我们发现,对于
a 和
b 的情况,二者的最大公约数等于
p1min(ka1,kb1)p2min(ka2,kb2)⋯psmin(kas,kbs)
最小公倍数等于
p1max(ka1,kb1)p2max(ka2,kb2)⋯psmax(kas,kbs)
由于
ka+kb=max(ka,kb)+min(ka,kb)
所以得到结论是
gcd(a,b)×lcm(a,b)=a×b
要求两个数的最小公倍数,先求出最大公约数即可。
多个数的
可以发现,当我们求出两个数的
gcd 时,求最小公倍数是
O(1) 的复杂度。那么对于多个数,我们其实没有必要求一个共同的最大公约数再去处理,最直接的方法就是,当我们算出两个数的
gcd ,或许在求多个数的
gcd 时候,我们将它放入序列对后面的数继续求解,那么,我们转换一下,直接将最小公倍数放入序列即可。
扩展欧几里得定理
扩展欧几里德定理(Extended Euclidean algorithm, EXGCD),常用于求
ax+by=gcd(a,b) 的一组可行解。
证明
设
ax1+by1=gcd(a,b)
bx2+(amodb)y2=gcd(b,amodb)
由欧几里得定理可知:
gcd(a,b)=gcd(b,amodb)
所以
ax1+by1=bx2+(amodb)y2
又因为
amodb=a−(⌊ba⌋×b)
所以
ax1+by1=bx2+(a−(⌊ba⌋×b))y2
ax1+by1=ay2+bx2−⌊ba⌋×by2=ay2+b(x2−⌊ba⌋y2)
因为
a=a,b=b ,所以
x1=y2,y1=x2−⌊ba⌋y2
将
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 即可。