Swift算法之求最大公約數-歐幾里得算法

求兩個非零整數a ,b的最大公約數.本人在最初想到的是最粗暴的方式,其實就是小學老師教的分解因數git

func gcd(_ a :Int ,_ b :Int) -> Int {
    
    if a == b {//若是兩個數相等.則直接返回
        return a
    }
    let big = max(a, b)
    let small = min(a, b)
    
    var divi = 0
    for i in 1..<small+1 {//選出兩個數中較小的那個數將其分解因數
        if small % i  == 0{
            divi = small/i  //分解因子,由於是從1到small遍歷.因此i 爲較小的那個 ,divi爲較大的那個
            if big%divi == 0{//判斷divi可否被較大的那個數整除,若是能則divi是最大公約數
                return divi
            }
        }
    }
    return 1
}
複製代碼

這個算法邏輯比較簡單,可是還不夠快,接下來看一下優質解法,歐幾里得算法. 能夠參考swift-algorithm-club中對最大公約數的解法.github

歐幾里得算法公式爲:
gcd(a, b) = gcd(b, a % b)
複製代碼

用Swift實現的代碼是這樣子的算法

func gcd(_ a: Int, _ b: Int) -> Int {
  let r = a % b
  if r != 0 {
    return gcd(b, r)
  } else {
    return b
  }
}
複製代碼

這樣寫的好處不只比以前的代碼簡潔的多,並且最大的好處是當數字特別大的時候比分解因子要快不少. 舉個例子// 求 7623 和 10989公約數.若是是使用用歐幾里得算法來計算的話swift

1. gcd(10989 ,7623) =  gcd(7623 ,10989 % 7623) =  
2. gcd(7623,3366) = gcd(3366 , 7623 % 3366) =  
3. gcd(3366 ,891) = gcd(891 ,3366 % 891) =  
4. gcd(891 ,693) = gcd(693 ,891 % 693) =  
5. gcd(693 , 198) = gcd(198 , 693 % 198) =  
6. gcd(198 ,99) = gcd (99 , 198 % 99) =  
7. gcd(99 , 0)
複製代碼

只須要7次計算就能等到最大公約數,若是用上面分解數字的辦法 須要for循環循環到 (7623/99 = 77)時才能獲得7623和10989的最大公因數.bash

可是gcd(a ,b) = gcd(b ,a % b)是怎麼來的,勾起了對證實歐幾里得算法的渴望.如下是證實過程ui

  1. 令c = gcd(a,b),則設 a = mc , b = nc .spa

  2. 令 a = kb + r (a > b ,a,b,k,r皆爲正整數,且r<b) ,r爲a除以b的餘數 r = a - kb = mc -knc = c(m-kn)code

  3. 條件1中的 b = nc ,條件2中的 r = (m-kn)c,則b和r有公約數c,只要證實 (m -kn)與n互質,就能證實c是b與r的最大公約數.get

  4. 用反證法證實m-kn 與 n互質,假設 m-kn 與 n 不互質既有除1以外的公約數it

    則可設m- kn = xd , n = yd (d > 1)
    m - kyd = xd , m = kyd + xd ,a = mc 則 a = kydc + xdc = (ky + x)dc, b = ydc
    那麼a,b的最大公約數則是dc 不是c ,與c = gcd(a,b)的設定衝突,因此(m-kn)與n互素,因此r,與b最大的公約數也是c.因此 gcd(a,b) = gcd(b,a % b)

    證實完畢.若有邏輯錯誤或者是表述不清楚,看不懂的地方但願你們能指出.(好久沒有寫文章了.本身都感受很亂)

相關文章
相關標籤/搜索