最近發現在搞Android的都要懂一點數據結構和算法才能進階到高手,所以就回去復習了一下基礎,為一些公司招聘做題做准備。
今天研究了一下最大公約數的求法,在網上也找了不同的解法,現在就想總結一下,拿出來分享給大家,共同 學習
首先講一個什么是公約數,這個問題我們小學都學過,可能有一部分人已經忘記了,所以還是講一下,假設有兩個數a,b,所謂的公約數就是能把a,b整除的最大整數。
明白了要求我們就來解決問題,一拿到問題我們都應該都能想到一個方法,就是使用窮舉法,從2開始一個個找,到一個兩個都能除的就記錄起來,一直找到小於min(a,b)結束,
記錄到的值就是他們的最大公約數代碼由下:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
//找出最大公約數,窮舉法
public
static
int
getMaxDivide_ab(
int
a,
int
b){
int
value=
1
;
int
max;
int
min;
if
(a==b){
return
a;
}
if
(a>b){
max=a;
min=b;
}
else
{
max=b;
min=a;
}
for
(
int
i=
2
;i<min;i++){
if
(
0
==max%i &&
0
==min%i){
value=i;
}
}
return
value;
}
|
第二種方法是使用歐幾里德算法,這個已經有2000+年的歷史了,這個比起上一個來的要高效,假設我們的最大公約數表示為f(a,b),並且有a>=b>0,
歐幾里德就給了我們一個很好的定理,f(a,b)=f(b,a%b),有了這個等式我們就很容易得出這個算法的遞歸式,現在我們來看下這個等式是怎么來的
設有 r=a/b ,q=a%b
所以就有 a=a/b*b+q ----(這里的a/b*b!=a ,原因就是我們用的是整數來計算的)
也就是a=r*b+q 變換一下有:q=a-r*b 設d=f(a,b),a/d=m,b/d=n;則 有q=dm-r*dn=d(m-rn)
所以q/d也為0;設d|q表示d是q的約數;以下相同;
又有d|b;所以有d|(b,q),設D是(b,q)的最大公約數,則會有d<=D=f(b,q);
再回到前面r=a/b,q=a%b這兩個條件有
a=r*b+q,由於D|(b,q),所以D|a,所以有D|(a,b)
所以有D<=d=f(a,b),結合上部分就有d<=D <+d,及D=d;
所以得證;
代碼實現由下:
01
02
03
04
05
06
07
08
09
10
11
12
|
public
static
int
oujilide(
int
a,
int
b){
if
(a<b){
int
temp;
temp=a;
a=b;
b=temp;
}
if
(
0
==b){
return
a;
}
return
oujilide(b,a%b);
}
|
第三種方法是上一種的變形
我們在對大整數求最大公約數,第二種方法的效率就出現了瓶頸,實際上對於大整數而言,取模運算(用到除法)的開銷非常的昂貴,這就是歐幾里得算法的局限性,那么我們就得優化一下它了,借鑒歐幾里得的輾轉相除法,既然是取模運算導致的問題,那么我們就不用取模運算,換用“-”運算,即 f(x,y)=f(x-y,y);深入考慮一下發現在算法運行的過程可能會出現x<y的情況,這時候要交換x和y,但是結果不受影響。
這個方法的證明可以看上面一種給出的證明,細想一下都是一樣的
實現代碼也很簡單:
01
02
03
04
05
06
07
08
09
10
11
12
|
public
static
int
xymod(
int
a,
int
b){
if
(a<b){
int
temp;
temp=a;
a=b;
b=temp;
}
if
(
0
==b){
return
a;
}
return
xymod(a-b,b);
}
|
在算法的效率上第二種和第三種都有個自的局限性,一個在大整數上,一個在迭代上, 我們還可以找到一個綜合上面兩種的算法就是,一步步的簡化a,b這兩個數,簡化的方法由下:
上面的方法來源都可以用到上面的證明過程,
代碼實現由下:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
public
static
int
moveCombe(
int
a,
int
b){
if
(a<b){
return
moveCombe(b, a);
}
if
(
0
==b){
return
a;
}
if
(isTwo(a)){
if
(isTwo(b)){
return
moveCombe(a/
2
, b/
2
)*
2
;
}
return
moveCombe(a/
2
, b);
}
else
{
if
(isTwo(b)){
return
moveCombe(a, b/
2
);
}
return
moveCombe(b, a-b);
}
}
private
static
boolean
isTwo(
int
a) {
if
(
0
==a%
2
){
return
true
;
}
else
{
return
false
;
}
}
|
上面的代碼都比較簡單,我就沒有做注解了,這些思想大部分都來自了網絡,代碼自己重寫過,有不對之處還請大家指出,共同學習。
全部代碼: