福哥答案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[nO(最大公约数)]。
2.【二分法】,分桶法中的一种。并行和非并行。时间复杂度是O[nO(最大公约数)]。
代码用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 命令,结果如下: