2020-09-12:手撕程式碼:最小公倍數,複雜度多少?
阿新 • • 發佈:2020-09-12
福哥答案2020-09-12:#福大大架構師每日一題#
最大公約數
1.【更相減損法】=【等值演算法】,避免了取模運算,但是演算法效能不穩定,最壞時間複雜度為O(max(a, b)))。
2.【輾轉相除法】,迭代和遞迴,時間複雜度不太好計算,可以近似為O(log(max(a, b))),但是取模運算效能較差。
3.【Stein演算法】,不但避免了取模運算,而且演算法效能穩定,時間複雜度為O(log(max(a, b)))。
4.【試除法】,時間複雜度是O(min(a, b)))。
兩個數的最小公倍數
1.【利用最大公約數】。時間複雜度是O(最大公約數)。
2.【試乘法】。時間複雜度是O(min(a, b)))。
n個數的最小公倍數
1.【遍歷法】,時間複雜度是O[n*O(最大公約數)]。
2.【二分法】,分桶法中的一種。並行和非並行。時間複雜度是O[n*O(最大公約數)]。
程式碼用go語言編寫,程式碼如下:
package test39_lcm import ( "fmt" "testing" ) //go test -v -test.run TestLcm func TestLcm(t *testing.T) { fmt.Println("----最大公約數") fmt.Println(Gcd1(36, 42), " 1.更相減損法") fmt.Println(Gcd2(36, 42), " 2.輾轉相除法") fmt.Println(Gcd3(36, 42), " 3.Stein演算法") fmt.Println(Gcd4(36, 42), " 4.試除法") fmt.Println("----兩個數的最小公倍數") fmt.Println(Lcm1(36, 42), " 1.利用最大公約數") fmt.Println(Lcm2(36, 42), " 2.試乘法") fmt.Println("----N個數的最小公倍數") fmt.Println(LcmN1([]int{2, 4, 6, 8}), " 1.遍歷法") fmt.Println(LcmN2([]int{2, 4, 6, 8}), " 2.二分法") } //1.最大公約數:【更相減損法】=【等值演算法】 func Gcd1(a int, b int) int { k := 1 //這段程式碼其實可以不用的,但是演算法介紹裡有除以2的操作,故有這段程式碼 if true { for a&1 == 0 && b&1 == 0 { a /= 2 b /= 2 k <<= 1 } } //兩數相減 for a != b { //保證第一個數大於等於第二個數 if a < b { a, b = b, a } a, b = b, a-b } return b * k } //2.最大公約數:【輾轉相除法】 func Gcd2(a int, b int) int { if true { //迭代 for b != 0 { a, b = b, a%b } return a } else { //遞迴 if a%b == 0 { return b } else { return Gcd2(b, a%b) } } } //3.最大公約數:【Stein演算法】 func Gcd3(a int, b int) int { k := 1 //最大公約數跟係數k有關,不能省略 for a&1 == 0 && b&1 == 0 { a /= 2 b /= 2 k <<= 1 } for a != b { //a和b,做除以2的操作 for a&1 == 0 { a >>= 1 } for b&1 == 0 { b >>= 1 } //a和b經過除以2的操作後,可能相等了 if a == b { break } //保證第一個數大於等於第二個數 if a < b { a, b = b, a } //做減法操作 a, b = b, a-b } return a * k } //4.最大公約數:【試除法】 func Gcd4(a int, b int) int { //保證第一個數大於等於第二個數 if a < b { a, b = b, a } //試除 for i := b; i >= 2; i-- { if a%i == 0 && b%i == 0 { return i } } //試除失敗,1就是最大公約數 return 1 } //1.兩個數的最小公倍數:【利用最大公約數】 func Lcm1(a int, b int) int { return a / Gcd2(a, b) * b } //2.兩個數的最小公倍數:【試乘法】 func Lcm2(a int, b int) int { //保證第一個數大於等於第二個數 if a < b { a, b = b, a } //試乘 for i := 1; i < b; i++ { if i*a%b == 0 { return i * a } } //試乘失敗,兩個數的乘積就是最小公倍數 return a * b } //1.n個數的最小公倍數:【遍歷法】 func LcmN1(s []int) int { ret := 1 for i := len(s) - 1; i >= 0; i-- { ret = Lcm1(ret, s[i]) } return ret } //2.n個數的最小公倍數:【二分法】 func LcmN2(s []int) int { slen := len(s) if slen == 1 { return s[0] } else { if true { //並行 ch := make(chan int, 0) go func() { ch <- LcmN2(s[0 : slen/2]) }() go func() { ch <- LcmN2(s[slen/2:]) }() return Lcm1(<-ch, <-ch) } else { //非並行 return Lcm1(LcmN2(s[0:slen/2]), LcmN2(s[slen/2:])) } } }
敲 go test -v -test.run TestLcm 命令,結果如下: