面试算法之求最大公约数

题目:求两个数的最大公约数,要尽量优化算法的性能

  1. 方法一:暴力枚举是绝对不可取的,效率不高如果我们传入的整数是10000和10001,那我们需要循环10000 / 2 - 1 = 4999
for(int i = small / 2; i > 1; i--) {
	if(small % i == 0 && big % i == 0) {
		return i;
	}
}
return 1;
  1. 方法二:辗转相除法。
    辗转相除,又叫欧几里得算法,该算法的目的求两个数的最大公约数。他是已知最古老的算法,要追溯到公园300年前。
    原理:两个正整数a 和 b (a > b),他们的最大公约数等于a 除以b的余数c 和 b之间的最大公约数。
    代码实现:
///Swift 实现
func getGreateCommonDivisor(_ number: Int, _ number2: Int) -> Int {
    let big = number > number2 ? number : number2
    let small = number2 > number ? number : number2
    if big % small == 0 {
        return small
    }else {
        return getGreateCommonDivisor(big % small, small)
    }
}
 缺点:当两个数都比较大的时候,取模运算的性能比较差
  1. 方法三:更相减损术

更相减损术:出自中国古代《九章算术》,也是一种求最大公约数的算法。
原理:两个正整数a > b ,他们的最大公约数等于a - b的差值c 和 较小数b的最大公约数,直到,两个数相等,就是他们的最大公约数啦
例如:15 和 10,第一次相减 5 和 10, 第二次:5 和 5啦,那他们的最大公约数就是5.
代码:

///递归调用
func getGreateCommonDivisorV3(_ number: Int, _ number2: Int) -> Int {
    let big = number > number2 ? number : number2
    let small = number2 > number ? number : number2
    if big == small {
        return small
    }else {
        return getGreateCommonDivisor(big - small, small)
    }
}

缺点:当两个数相差很大的时候,那么相减次数就会很多,该算法是不稳定算法,例如:10000 和 3 就要递归大约: 10000 / 3次

  1. 方法四: 移位运算和更相减损术结合,达到比较好的效果

解释:众所周知,位运算的性能非常好。
说明:公约数用gcd() 表示,其中a > b
当a和b都是偶数时:gcd(a, b) = 2 x gcd(a / 2, b / 2) = 2 x gcd(a>>1, b>>1)
当a为偶数和b为奇数时,gcd(a, b) = gcd(a / 2, b ) = gcd(a>>1, b)
当b为偶数和a为奇数时,gcd(a, b) = gcd(a , b / 2 ) = gcd(b>>1, a)
当a和b都是奇数时,先利用更相减损术运算一次,gcd(a, b) = gcd(b, a - b),此时a - b必然是偶数,然后可以继续利用移位运算。

这种算法的优势,当两个数越大时,计算次数的减少就越明显。
例如:15 和 25的最大公约数
第一次:都为奇数,两者相减:10 , 15
第二次:10是偶数,变为:5 , 15
第三次:都是奇数,两者相减:5, 10
第四次:10位偶数:5, 5 ,得出结果:5

func gcdFinal(_ number: Int, _ number2: Int) -> Int {
    if number2 == number {
        return number
    }
    if number & 1 == 0 && number2 & 1 == 0 {
        return gcdFinal(number >> 1, number2 >> 1) << 1 //都是偶数,除以2,最外层再乘以2
    }else if number & 1 == 0 {
        return gcdFinal(number >> 1, number2)
    }else if number2 & 1 == 0 {
        return gcdFinal(number2 >> 1, number)
    }else {
    //都是奇数的时候才用:更相减损术
        let big = number > number2 ? number : number2
        let small = number2 > number ? number : number2
        return gcdFinal((big - small) >> 1, small)
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章