java 算法基礎之一尋找最大公約數


最近發現在搞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這兩個數,簡化的方法由下:

(1)如果y=k*y1,x=k*x1.那么有f(x,y)=k*f(x1,y1)
(2)如果x=p*x2,p為素數,並且y%p != 0,那么f(x,y) = f(p*x2,y) = f(x2,y)
於是我們得到下面的解決方法:
將p = 2,
若x,y均為偶數,f(x,y) = 2*f(x/2,y/2) = 2*f(x>>1,y>>1);
若x是偶數,y是奇數,f(x,y) = f(x/2,y) = f(x>>1,y);
若x是奇數,y是歐式,f(x,y) = f(x,y/2) = f(x,y>>1);
若x和y均為奇數,f(x,y) = f(y,x-y)。這時x-y一定是偶數,下一步一定會除以2。


上面的方法來源都可以用到上面的證明過程,
代碼實現由下:

?
代碼片段,雙擊復制
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 ;
                }
        }



上面的代碼都比較簡單,我就沒有做注解了,這些思想大部分都來自了網絡,代碼自己重寫過,有不對之處還請大家指出,共同學習。

全部代碼:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM