目錄
GCD與LCM
GCD
歐幾里得
遞歸寫法
形式1:
int gcd(int a,int b)
{
if(b==0) return a;
return gcd(b,a%b);
}
形式2:
int gcd(int x,int y){return y==0?x:GCD(x%y)}
非遞歸寫法
int gcd(int a,int b)
{
while(b)
{
a=a%b;
swap(a,b);
}
return a;
}
更相減損術
while(!(a%2) && !(b%2))
{
a = a/2;
b = b/2;
}
while(a != b)
{
if(a>b){
a = a-b;
}else{
b = b-a;
}
}
輾轉相除法與更相減損術的比較
(1)兩者都是求最大公因數的方法,計算上輾轉相除法以除法爲主,更相減損術以減法爲主,計算次數上輾轉相除法計算次數相對較少,特別當兩個數字大小區別較大時計算次數的區別較明顯。
(2)更相減損術本質是輾轉相除法,輾轉相除法效率比較高
(3)從結果體現形式來看,輾轉相除法體現結果是以相除餘數爲0則得到,而更相減損術則以減數與差相等而得到。
GCD的二進制寫法(stein算法)
二進制寫法先用移位的方式對兩個數除2,直到兩個數不同時爲偶數。然後將剩下的偶數(如果有的話)做同樣的操作,這樣做的原因是如果u和v中u爲偶數,v爲奇數,則有gcd(u,v)=gcd(u/2,v)。到這時,兩個數都是奇數,將兩個數相減(因爲gcd(u,v) = gcd(u-v,v)),得到的是偶數t,對t也移位直到t爲奇數。每次將最大的數用t替換。
Stein算法只有整數的移位和加減法。下面就來說一下Stein算法的原理:
不加證明的給出:
- 若a和b都是偶數,則記錄下公約數2,然後都除2(即右移1位);
- 若其中一個數是偶數,則偶數除2,因爲此時2不可能是這兩個數的公約數了
- 若兩個都是奇數,則a = |a-b|,b = min(a,b),因爲若d是a和b的公約數,那麼d也是|a-b|和min(a,b)的公約數。
遞歸式:
int SteinGCD(int a, int b) {
if (a < b) { int t = a; a = b; b = t; }
if (b == 0) return a;
if ((a & 1) == 0 && (b & 1) == 0)
return SteinGCD(a >> 1, b >> 1) << 1;
else if ((a & 1) == 0 && (b & 1) != 0)
return SteinGCD(a >> 1, b);
else if ((a & 1) != 0 && (b & 1) == 0)
return SteinGCD(a, b >> 1);
else
return SteinGCD(a - b, b);
}
非遞歸:
int SteinGCD(int a, int b) {
int acc = 0;
while ((a & 1) == 0 && (b & 1) == 0) {
acc++;
a >>= 1;
b >>= 1;
}
while ((a & 1) == 0) a >>= 1;
while ((b & 1) == 0) b >>= 1;
if (a < b) { int t = a; a = b; b = t; }
while ((a = (a - b) >> 1) != 0) {
while ((a & 1) == 0) a >>= 1;
if (a < b) { int t = a; a = b; b = t; }
}
return b << acc;
}
LCM
LCM(A,B)=A*B/GCD(A,B);
這樣寫法有可能會錯,因爲a * b可能因爲太大 超出int 或者 超出 long long)
最好寫成LCM(A,B)=A/GCD(A,B)*B;
分數的lcm
公式:lcm(S/a, S/b) = S/gcd(a, b) 例如:S = 9,a = 4,b = 6