面試算法之求最大公約數

題目:求兩個數的最大公約數,要儘量優化算法的性能

  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)
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章