求两个非零整数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)是怎么来的,勾起了对证明欧几里得算法的渴望.以下是证明过程
-
令c = gcd(a,b),则设 a = mc , b = nc .
-
令 a = kb + r (a > b ,a,b,k,r皆为正整数,且r<b) ,r为a除以b的余数 r = a - kb = mc -knc = c(m-kn)
-
条件1中的 b = nc ,条件2中的 r = (m-kn)c,则b和r有公约数c,只要证明 (m -kn)与n互质,就能证明c是b与r的最大公约数.
-
用反证法证明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)证明完毕.如有逻辑错误或者是表述不清楚,看不懂的地方希望大家能指出.(很久没有写文章了.自己都感觉很乱)