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個數的最大公約數和最小公倍數
見下篇