「輾轉相除法」和「更相減損術」求最大公約數

一、輾轉相除法

舉個例子,比如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(a,b)=2gcd(a2,b2)gcd(a,b)=2*gcd(\frac{a}{2},\frac{b}{2})
比如gcd(100,50)=50=2*gcd(50,25)=2*25=50
2.如果a、b一個是偶數一個是奇數,則最大公約數gcd(a,b)=gcd(a2,b)gcd(a,b)=gcd(\frac{a}{2},{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;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章