用分解质因数求两个数字的最大公约数和最小公倍数

分解质因数采用Pollard Rho快速因数分解算法,该算法描述如下:

输入一个任意数字n后,从最小的质数k=2开始,按下述步骤完成:

1 如果k恰等于n,则说明分解质因数的过程已经结束,打印出即可。

2 如果n>k,但n能被k整除,则应打印出k的值,并用n除以k的商作为新的正整数n,重复执行第一步。

3 如果n不能被k整除,则用k+1作为k的值,重复执行第一步。

def check_num(func):
    def valid(num):
        if num < 2:
            print('请输入大于2的数字')
            return None
        else:
            return func(num)
    return valid

@check_num
def get_prime(n):
    c = []
    k = 2
    while True:
        if n == k:
            c.append(k)
            break
        elif n > k and n % k ==0:
            n /= k
            c.append(k)
        else:
            k += 1
    return c

根据质因数求最大公约数:

例如数字24 [2,2,2,3]和数字18 [2,3,3]的最大公约数就是两者质因数列表的所有公共部分,也就是6 [2,3]

但是不能采用如下的算法计算:

a = [2,2,2,3]
b = [2,3,3]
c = []
for x in a:
    if x in b:
      c.append(x)
print(c) #[2,2,2,3]

显然这是错误的算法,会重复计算很多数字。

所以正确的计算方法是,一旦进入了c列表的数字就应该从a列表和b列表中删除掉,避免重复计算:

a = [2,2,2,3]
b = [2,3,3]
c = []
while len(a):
    x = a[0]
    if x in b:
        c.append(x)
        b.remove(x)
    a.remove(x)
print(c)

例如for循环一边遍历一边删除列表中元素可能会导致列表下标的不正确。所以这里采用了一种变通的方式,每次都取a列表的第一个元素,如果这个元素恰好也在b列表中,在该元素进入c列表,并从b列表和a列表中删除;如果这个元素不在b列表中,则直接从a列表中删除。

可以将上述代码整理为函数:

def get_divider(m,n):
    a = get_prime(m)
    b = get_prime(n)
    c = []
    while len(a):
        x = a[0]
        if x in b:
            c.append(x)
            b.remove(x)
        a.remove(x)
    return c

根据质因数求最小公倍数:

根据公式:m * n = 最大公约数 * 最小公倍数,可以推导出最小公倍数就是两数之积除以最大公约数

根据质因数列表

24 [2,2,2,3]

18 [2,3,3]

则计算24*18之积实际就是列表做加法[2,2,2,3]+[2,3,3]得到[2,2,2,2,3,3,3]

做除法,就是从[2,2,2,2,3,3,3]去除掉除数的质因数列表即可

例如,24和18的最大公约数是[2,3],则最小公倍数就是从[2,2,2,2,3,3,3]去掉[2,3]

最终得到[2,2,2,3,3]就是最小公倍数。

遗憾的是,python中的列表不支持减法,也就是没有[2,2,2,2,3,3,3] - [2,3]这样的操作

所以这里又需要一些算法上的变通:

a = [2,2,2,3]
b = [2,3,3]
c = []
#求最小公倍数
for x in a:
    if x in b:
        b.remove(x)
c = a+b
c.sort()
print(c)

因为两个列表之间只能做加法,因此在做加法之前,先从一个列表中把两个列表中都有的可以成为公约数的内容从一个列表中删除掉,然后再让两个列表做加法即可。

将上述代码整理为函数:

def get_fold(m,n):
    a = get_prime(m)
    b = get_prime(n)
    c = []
    for x in a:
        if x in b:
            b.remove(x)
    c = a+b
    c.sort()
    return c

最终可以将上述代码综合为工具方法,根据需要返回质因数形式或数字形式的最大公约数和最小公倍数:

def get_divfold_bylist(m,n):
    return get_divider(m,n),get_fold(m,n)

def get_divfold(m,n):
    a,b  = get_divfold_bylist(m,n)
    f =lambda x,y:x*y
    return reduce(f,a),reduce(f,b)

完整代码如下:

from functools import reduce

def check_num(func):
    def valid(num):
        if num < 2:
            print('请输入大于2的数字')
            return None
        else:
            return func(num)
    return valid

@check_num
def get_prime(n):
    c = []
    k = 2
    while True:
        if n == k:
            c.append(k)
            break
        elif n > k and n % k ==0:
            n /= k
            c.append(k)
        else:
            k += 1
    return c

def get_divider(m,n):
    a = get_prime(m)
    b = get_prime(n)
    c = []
    while len(a):
        x = a[0]
        if x in b:
            c.append(x)
            b.remove(x)
        a.remove(x)
    return c

def get_fold(m,n):
    a = get_prime(m)
    b = get_prime(n)
    c = []
    for x in a:
        if x in b:
            b.remove(x)
    c = a+b
    c.sort()
    return c

def get_divfold_bylist(m,n):
    return get_divider(m,n),get_fold(m,n)

def get_divfold(m,n):
    a,b  = get_divfold_bylist(m,n)
    f =lambda x,y:x*y
    return reduce(f,a),reduce(f,b)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值