一、輾轉相除法
舉個例子,比如155和65
155=65*2+25
65=25*2+15
25=15*1+10
15=10*1+5
10=5*2+0
一直除到餘數爲0爲止,所以最大公約數是5
public static int gcd1(int a, int b) {
int l = Math.max(a, b);
int s = Math.min(a, b);
while (s != 0) {
int r = l % s;
l = s;
s = r;
}
return l;
}
輾轉相除法的最大問題在於,"%"這個操作因爲是除法比較耗時,尤其是當l和s比較大的時候,會造成性能下降。
二、更相減損術
還是155和65
155-65=90
90-65=25
65-25=40
40-25=15
25-15=10
15-10=5
10-5=5
一直減到被減數和差相等爲止,所以最大公約數是5
public static int gcd2(int a, int b) {
int l = Math.max(a, b);
int s = Math.min(a, b);
int r = l - s;
while (s != r) {
l = Math.max(s, r);
s = Math.min(s, r);
r = l - s;
}
return r;
}
更相減損術的問題是遇到差別很大的數據,比如99999和1的時候要互減99998次,這樣顯然不行,所以我們做一個改進,把上面兩個方法的優勢結合起來。
三、兩種方法的結合
結合的規律是這樣的:
1.如果a、b都是偶數,則最大公約數
比如gcd(100,50)=50=2*gcd(50,25)=2*25=50
2.如果a、b一個是偶數一個是奇數,則最大公約數[這裏假定a是偶數]
比如gcd(100,25)=25=gcd(50,25)=25
3.如果a、b都是奇數,則執行一次更相減損術,將其轉化爲1、2兩種狀態
比如a,b分別是49和21,執行一次相減,49-21=28,那a,b就變成了28和21,這就回到了狀態2
注意每一次變換完都要減一下看看是不是已經結束了。
——————————————————————————————————
舉個例子:
a=67870,b=23446,multi=1
a-b=44424!=b
a、b都是偶數,執行狀態1,則a=33935,b=11723,multi=2
(因爲都是偶數時要把2提出來,我們把這個倍數賦給multi,最後記得乘回來就行)
a-b=22212!=b
a、b都是奇數,執行狀態3,則a=22212,b=11723
a-b=10489!=b
a是偶數,b是奇數,執行狀態2,則a=11106,b=11723,交換一下讓a是較大數,a=11723,b=11106
a-b=617!=b
a是奇數,b是偶數,執行狀態2,則a=11723,b=5553
a-b=6170!=b
a、b都是奇數,執行狀態3,則a=6170,b=5553
a-b=617!=b
a是偶數,b是奇數,執行狀態2,則a=5553,b=3085
a-b=2468!=b
a、b都是奇數,執行狀態3,則a=3085,b=2468
a-b=617!=b
a是奇數,b是偶數,執行狀態2,則a=3085,b=1234
a-b=1851!=b
a是奇數,b是偶數,執行狀態2,則a=3085,b=617
a-b=2468!=b
a、b都是奇數,執行狀態3,則a=2468,b=617
a-b=1851!=b
a是偶數,b是奇數,執行狀態2,則a=1234,b=617
a-b=617==b
所以最大公約數是617*multi=1234
———————————————————————————————————
public static int gcd3(int a, int b) {
int l = Math.max(a, b);
int s = Math.min(a, b);
int r = l - s;
int multi = 1;
while (s != r) {
if ((l & 1) == 0 && (s & 1) == 0) {
l = l >> 1;
s = s >> 1;
multi <<= 1;
} else if ((l & 1) == 0 && (s & 1) != 0) {
l = l >> 1;
if (l < s) {
int p = l;
l = s;
s = p;
}
} else if ((l & 1) != 0 && (s & 1) == 0) {
s = s >> 1;
} else {
l = Math.max(s, r);
s = Math.min(s, r);
}
r = l - s;
}
return r * multi;