数论——欧几里得算法 GCD和LCM

GCD

什么是GCD,GCD就是最大公约数,对于一些最大公约数问题,我们通常是利用循环来找到最大的公约数,可这样效率往往并不高,对于给定的两个整数m和n,时间复杂度可能高达O(min(n,m)),这是我们不愿看到的的,那么有什么方法更快解决呢?当然有,就是欧几里得算法,也就是辗转相除法。它的步骤十分简短,我们先来看看它是怎么做到的呢?
在这里插入图片描述
我们看一个示例:
假如需要求 1997 和 615 两个正整数的最大公约数,用欧几里德算法,是这样进行的:

1997 / 615 = 3 (余 152)
615 / 152 = 4(余7)
152 / 7 = 21(余5)
7 / 5 = 1 (余2)
5 / 2 = 2 (余1)
2 / 1 = 2 (余0)

至此,最大公约数为1
以除数和余数反复做除法运算,当余数为 0 时,取当前算式除数为最大公约数,所以就得出了 1997 和 615 的最大公约数 1


那么,为什么是这样的呢?我们来看证明。

GCD证明

其计算原理依赖于下面的定理:
定理:两个整数的最大公约数等于其中较小的那个数和两数相除余数的最大公约数。最大公约数(Greatest Common Divisor)缩写为GCD
gcd(a,b) = gcd(b,a mod b) (不妨设a>b 且r=a mod b ,r不为0)
假设c = gcd(a,b),则存在m,n,使a = mc, b = nc;
令r = a mod b,即存在k,使r = a-kb = mc - knc = (m-kn)c;
故gcd(b,a mod b) = gcd(b,r) = gcd(nc,(m-kn)c) = gcd(n,m-kn)c;
则c为b与a mod b的公约数;
假设d = gcd(n,m-kn), 则存在x,y, 使n = xd, m-kn = yd; 故m = yd+kn = yd+kxd = (y+kx)d;
故有a = mc = (y+kx)dc, b = nc = xdc; 可得 gcd(a,b) = gcd((y+kx)dc,xdc) = dc;
由于gcd(a,b) = c, 故d = 1;
即gcd(n,m-kn) = 1, 故可得gcd(b,a mod b) = c;
故得证gcd(a,b) = gcd(b,a mod b).


如果我们了解完了证明,对于代码实现应该也很简单了,我们可以利用递归或者非递归实现此操作,结束条件就是余数为0.

GCD非递归实现

long long gcd(long long n,long long m){
	long long temp;
	while(n%m!=0){
		temp=n%m;//取余数做除数。
		n=m;
		m=temp;
	}
	return m;
}

GCD递归实现

long long gcd(long long n,long long m){
	return n%m?gcd(m,n%m):m;//仔细了解这一行代码。
}

LCM

LCM又是什么呢?就是最小公倍数,对于给定的a和b,我们要求a和b的最小公倍数通常也是枚举,这样十分麻烦,时间复杂度也很大。我们同样也有快捷的方法,我们想想,最小公倍数是什么?就是对于除0以外最小的公倍数。我们如果要得到a和b的公倍数,直接让c=a*b即可,那么我们如果想要让c最小,我们可以求出a和b的最大公约数,也就是说我在a*b的过程中我多乘了他们的一份最大公约数,我们用c除以gcd(a,b)得到的c就是我们所求的最小公倍数

那么显而易见,我们由上已知了GCD的求法,那么LCM就迎刃而解了。

LCM代码实现

long long lcm(long long n,long long m){
	return n*m/gcd(n,m);
}

猜你喜欢

转载自blog.csdn.net/hzf0701/article/details/107788295