1.概念
最大公约数(Greatest Common Divisor:GCD)指某几个整数共有约数中最大的一个。最小公倍数(least common multiple:lcm)是某几个整数公有的倍数中最小的一个正整数。
2.相互之间关系及证明
两个整数的最小公倍数与最大公约数之间有如下的关系:最小公倍数 = 两数之积 / 最大公约数
证明1:
设a,b两个整数,假设最大公约数为gcd,最小公倍数为lcm。
根据公约数的定义有k1,k2使得 a = k1 * gcd;b = k2 * gcd;——(1)
其中 k1,k2互质即两数除1之外没有其他公约数(gcd(k1, k2) = 1)
证:假设有其他公约数,那么a,b的最大公约数就不是gcd了,而是gcd*另外的这个公约数,与假设矛盾。
所以观察(1)式,要能同时被a,b整除的数必须有 k1,k2的乘积(gcd(k1, k2) = 1),而又要求最小所以有lcm = k1 * k2 * gcd = a * b / gcd;
证法2:
根据公被数的定义有t1,t2使得 lcm = a * t1 ;lcm = b * t2 ;将(1)式代入则有
k1 * gcd * t1 = lcm = k2 * gcd * t2——(2)
并且k1,k2互质,t1,t2互质(证明类似k1,k2.若不互质,则有公约数,那么lcm就不是最小公倍数,而是lcm/这个公约数),所以(由(2)可得k1/k2=t2/t1;相等有两种情况,一是k1=c*t2,k2=c*t1,c!=1;二是k1=c*t2,k2=c*t1,c=1;因为k1,k2互质,t1,t2互质就是说这两个分式都办法约掉 除1之外的其他数,那么相等只有一种情况),k1 = t2, k2 = t1;
代入lcm = k1 * gcd * t1 = k1 * k2 * gcd = a * b / gcd ;
所以,最小公倍数 = 两数之积 / 最大公约数
3.求法及证明
最大公约数求解
最容易想到的方法就是遍历2到min(a,b)之间的整数,求出所有能够被a,b都整除的数,然后取最大。若未找到则返回1。但这种方法对于两个都是很大的数要浪费很多时间。
自然我们就要寻找两个数公约数的一些性质,然后利用这些性质来减少运算量。
一:欧几里德算法
又名辗转相除法,用于计算两个整数a,b的最大公约数。其计算原理依赖于下面的定理即(a,b)和(b,a mod b)的公约数是一样的:
定理:gcd(a,b) = gcd(b,a mod b)
证明:设r = a mod b;则a可以表示成a = kb + r;其中mod就是求余,r为余数,k为商。
假设d是a,b的一个公约数,则a,b能被d整除表示为d|a, d|b,而r = a - kb,因此 d|r
因此d是(b,a mod b)的公约数
假设d 是(b,a mod b)的公约数,则d | b , d |r ,但是a = kb +r
因此d也是(a,b)的公约数
因此(a,b)和(b,a mod b)的公约数是一样的,其最大公约数也必然相等,得证。
二:更相减损法
其实质也类似于辗转相除法,不过把mod换成是减,gcd(a,b) = gcd(b,a - b)即(a,b)和(b,a - b)的公约数一样的(证明类似)和gcd(a,a)=a。
其流程是,先判断a,b是否为偶数,若是则除2(可省略),然后将大的减小的用来更新大的,一直不断更新相减。到大的和小的相等的时候停止并返回值,这个值就是最大公约数。
如求(91 49)的最大公约数:
91 49
42 49
42 7
35 7
28 7
21 7
14 7
7 7
而7和7的最大公约数就是7,(7,7)=7,所以(91,49)=(42,7)=(7,7)=7。利用gcd(a,b) = gcd(b,a - b)更新,利用gcd(a,a)=a得最后的值。
三:Stein算法
通过对两个数的奇偶性判断对更相减损法做一些适当的扩展就能够得到Stein算法。
主要利用到的原理有
1.gcd(ka,kb) = k gcd(a,b),当k=2时,我们就能够先计算gcd(a,b)然后将结果乘以2就得到gcd(ka,kb)。
2.gcd(2a,b)=gcd(a,b)。这个容易理解,如果一个数能约a,必然能约2a。
3.利用更相减损法的结论。
有了上述规律就可以给出Stein算法如下:
如果A=0,B是最大公约数,算法结束
如果B=0,A是最大公约数,算法结束
设置A1 = A、B1=B和C1 = 1
循环判断递归
如果An和Bn都是偶数,则An+1 =An /2,Bn+1 =Bn /2,Cn+1 =Cn *2(注意,乘2只要把整数左移一位即可,除2只要把整数右移一位即可)
如果An是偶数,Bn不是偶数,则An+1 =An /2,Bn+1 =Bn ,Cn+1 =Cn
如果Bn是偶数,An不是偶数,则Bn+1 =Bn /2,An+1 =An ,Cn+1 =Cn
如果An和Bn都不是偶数,则An+1 =|An -Bn|,Bn+1 =min(An,Bn),Cn+1 =Cn(利用更相减损法)
算法背景:
欧几里德算法是计算两个数最大公约数的传统算法,他无论从理论还是从效率上都是很好的。但是他有一个致命的缺陷,这个缺陷只有在大素数时才会显现出来。考虑现在的硬件平台,一般整数最多也就是64位,对于这样的整数,计算两个数之间的模是很简单的。对于字长为32位的平台,计算两个不超过32位的整数的模,只需要一个指令周期,而计算64位以下的整数模,也不过几个周期而已。但是对于更大的素数,这样的计算过程就不得不由用户来设计,为了计算两个超过64位的整数的模,用户也许不得不采用类似于多位数除法手算过程中的试商法,这个过程不但复杂,而且消耗了很多CPU时间。对于现代密码算法,要求计算128位以上的素数的情况比比皆是,设计这样的程序迫切希望能够抛弃除法和取模。Stein算法由J. Stein 1961年提出,这个方法也是计算两个数的最大公约数。和欧几里德算法 算法不同的是,Stein算法只有整数的移位和加减法,这对于程序设计者是一个福音。
4.c++程序
一:欧几里德算法
int gcd(int a, int b) { if(b == 0) return a; return gcd(b, a % b); }
int gcd(int a, int b) { while(b != 0) { int temp= b; b = a % b; a = temp; } return a; }
二:更相减损法
int gcd(int a,int b) { while(a!=b) { if(a>b) a-=b; else b-=a; } return a; }
三:Stein算法
int Gcd(int a, int b) { if(a == 0) return b; if(b == 0) return a; if(a % 2 == 0 && b % 2 == 0) return 2 * gcd(a >> 1, b >> 1); else if(a % 2 == 0) return gcd(a >> 1, b); else if(b % 2 == 0) return gcd(a, b >> 1); else return gcd(abs(a - b), Min(a, b)); }
最小公倍数求法:
根据最小公倍数和最大公约数的关系求
int lcm(int a,int b) //最小公倍数 { return a*b/gcd(a,b); }
5.扩展欧几里得算法及求N个数的最大公约数和最小公倍数
见下篇