2020-09-12:手撕代码:最小公倍数,复杂度多少?

本文详细介绍了四种求最大公约数的方法:更相减损法、辗转相除法、Stein算法及试除法,并探讨了两种求最小公倍数的方法:利用最大公约数和试乘法。同时,对比了不同算法的时间复杂度,包括O(max(a,b))、O(log(max(a,b)))和O(min(a,b)),并提供了Go语言实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

福哥答案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[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 命令,结果如下:
在这里插入图片描述


评论

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

福大大架构师每日一题

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值