Swift算法之求最大公约数-欧几里得算法

求两个非零整数a ,b的最大公约数.本人在最初想到的是最粗暴的方式,其实就是小学老师教的分解因数

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中对最大公约数的解法.

欧几里得算法公式为:
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公约数.如果是使用用欧几里得算法来计算的话

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的最大公因数.

但是gcd(a ,b) = gcd(b ,a % b)是怎么来的,勾起了对证明欧几里得算法的渴望.以下是证明过程

  1. 令c = gcd(a,b),则设 a = mc , b = nc .

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

  3. 条件1中的 b = nc ,条件2中的 r = (m-kn)c,则b和r有公约数c,只要证明 (m -kn)与n互质,就能证明c是b与r的最大公约数.

  4. 用反证法证明m-kn 与 n互质,假设 m-kn 与 n 不互质既有除1之外的公约数

    则可设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)

    证明完毕.如有逻辑错误或者是表述不清楚,看不懂的地方希望大家能指出.(很久没有写文章了.自己都感觉很乱)

转载于:https://juejin.im/post/5d09af0e6fb9a07eb15d59f9

猜你喜欢

转载自blog.csdn.net/weixin_33777877/article/details/93173179