背景1
- 给定两个数xxx与yyy。
- 求出这两个数的最大公因数。
暴力1
- 直接从1到min(x,y)min(x,y)min(x,y)枚举,找出最大的能分别整除它们两个的数即可。
- 时间复杂度:O(min(x,y))O(min(x,y))O(min(x,y))。
暴力2
- 求出xxx的所有约数,再求出yyy的所有约数。
- 从中查找出公共的数,在所有公共的数中找出最大值即可。
- 时间复杂度O(max(x,y))O(\sqrt{max(x,y)})O(max(x,y))。
引入——更相减损术
- 更相减损术是出自《九章算术》的一种求最大公约数的算法,它原本是为约分而设计的,但它适用于任何需要求最大公约数的场合。
- 原文:可半者半之,不可半者,副置分母、子之数,以少减多,更相减损,求其等也。以等数约之。
- 说人话:
- 对于两个数,假如两个数都是偶数,先分别对每一个数除2,直到至少有一个数变成奇数为止。
- 若这两个数相同,那么最大公约数就是这个数乘上2n2^n2n,nnn是刚才除2的次数。
- 否则,设这两个数分别为x,yx,yx,y,不妨设x<yx<yx<y,则新的x′=y−xx'=y-xx′=y−x,y′=xy'=xy′=x。再重新判断是否相等。
- 举例子:
- 对于606060和100100100,求出它们的最大公约数。
- 首先不断除2,最终得到151515和252525。
- 发现15≠2515 \neq 2515=25,所以151515变成25−15=1025-15=1025−15=10,252525变成151515。
- 发现10≠1510 \neq 1510=15,所以101010变成15−10=515-10=515−10=5,151515变成101010。
- 发现5≠105 \neq 105=10,所以101010变成10−5=510-5=510−5=5,555变成555。
- 这时5=55 = 55=5,所以最大公约数就是5∗22=205*2^2=205∗22=20。
- 其实我们发现,之前不用除2也一样可以求出最大公约数。
- 正确性的证明
- 假若aaa是x,yx,yx,y的公因数,x<yx<yx<y。则aaa一定也是y−x,xy-x,xy−x,x的公因数。
- 这时十分显然的,设y=a∗y0,x=a∗x0y=a*y0,x=a*x0y=a∗y0,x=a∗x0,则y−x=a∗(y0−x0)y-x=a*(y0-x0)y−x=a∗(y0−x0),aaa也是其因数。
- 反过来也是一样的,若aaa是y−x,xy-x,xy−x,x的公因数,x<yx<yx<y。则aaa一定也是x,yx,yx,y的公因数。
- 所以y−x,xy-x,xy−x,x的公因数与x,yx,yx,y的公因数是等价的。
- 对于两个相同的数,它们的最大公因数显然是它们本身,有这个边界条件往上推即可得到原本两个数的最大公因数。
- 但是,假若xxx特别小,yyy特别大,时间复杂度就会退化到O(y)O(y)O(y),没能完全优化时间复杂度。
- 不过,它给我们提供了一个非常好的思想:
欧几里得算法
- 我们发现,重复的减法其实相当于取模的过程,所以我们直接取模就好了。
- 有x,y(x<y)x,y(x<y)x,y(x<y)推到ymod x,xy\mod x,xymodx,x。
- 发现这两组数的最大公因数也是等价的。
- 但注意此时的边界条件变为其中一个数为0,则最大公因数为另一个数。
- 时间复杂度:
- 若x>y2x>\frac{y}{2}x>2y,则ymod x<y2y\mod x<\frac{y}{2}ymodx<2y
- 若x<=y2x<=\frac{y}{2}x<=2y,则ymod x<x<=y2y\mod x<x<=\frac{y}{2}ymodx<x<=2y。
- 即每一次,较大的那个数都会缩减至少一半。
- 所以极限的时间复杂度是O(log2(max(x,y)))O(log_2(max(x,y)))O(log2(max(x,y)))的。
代码实现
int gcd(int x,int y){
if(x==0) return y;
else return gcd(y%x,x);
}