【问题标题】:I have a Python list of the prime factors of a number. How do I (pythonically) find all the factors?我有一个数字的主要因素的 Python 列表。我如何(以python方式)找到所有因素?
【发布时间】:2011-04-08 07:23:25
【问题描述】:

我正在研究一个需要对整数进行因式分解的 Project Euler 问题。我可以列出作为给定数字因子的所有素数的列表。算术基本定理意味着我可以使用这个列表推导出数字的每个因数。

我目前的计划是获取基素数列表中的每个数字并提高其幂,直到它不再是整数因子,才能找到每个素数的最大指数。然后,我将乘以质数-指数对的所有可能组合。

例如,对于 180:

Given: prime factors of 180: [2, 3, 5]
Find maximum exponent of each  factor: 
    180 / 2^1 = 90
    180 / 2^2 = 45
    180 / 2^3 = 22.5 - not an integer, so 2 is the maximum exponent of 2.

    180 / 3^1 = 60
    180 / 3^2 = 20
    180 / 3^3 = 6.6 - not an integer, so 2 is the maximum exponent of 3.

    180 / 5^1 = 36
    180 / 5^2 = 7.2 - not an integer, so 1 is the maximum exponent of 5.

接下来,将这些组合进行到最大指数以获得因子:

    2^0 * 3^0 * 5^0 = 1
    2^1 * 3^0 * 5^0 = 2
    2^2 * 3^0 * 5^0 = 4
    2^0 * 3^1 * 5^0 = 3
    2^1 * 3^1 * 5^0 = 6
    2^2 * 3^1 * 5^0 = 12
    2^0 * 3^2 * 5^0 = 9
    2^1 * 3^2 * 5^0 = 18
    2^2 * 3^2 * 5^0 = 36
    2^0 * 3^0 * 5^1 = 5
    2^1 * 3^0 * 5^1 = 10
    2^2 * 3^0 * 5^1 = 20
    2^0 * 3^1 * 5^1 = 15
    2^1 * 3^1 * 5^1 = 30
    2^2 * 3^1 * 5^1 = 60
    2^0 * 3^2 * 5^1 = 45
    2^1 * 3^2 * 5^1 = 90
    2^2 * 3^2 * 5^1 = 180

所以因子列表 = [1, 2, 3, 4, 5, 6, 9, 10, 12, 15, 18, 20, 30, 36, 45, 60, 90, 180]

这是我到目前为止的代码。两个问题:首先,我认为它根本不是 Pythonic。我想解决这个问题。其次,我真的没有 Python 的方式来完成组合的第二步。出于羞耻,我让你免于荒谬的循环。

n 是我们想要分解的数字。 listOfAllPrimes 是一个预先计算的最多 1000 万个素数的列表。

def getListOfFactors(n, listOfAllPrimes):
    maxFactor = int(math.sqrt(n)) + 1
    eligiblePrimes = filter(lambda x: x <= maxFactor, listOfAllPrimes)
    listOfBasePrimes = filter(lambda x: n % x ==0, eligiblePrimes)

    listOfExponents = [] #(do I have to do this?)
    for x in listOfBasePrimes:
        y = 1
        while (x**(y+1)) % n == 0:
            y += 1
        listOfExponents.append(y)

【问题讨论】:

  • 您的代码错误。可能存在大于 n 的平方根的合格素数。例如,n = 7 或 n = 22。
  • @Sheldon L. Cooper 谢谢,绝对错了。我混淆了算法。
  • 出于好奇,你解决的是哪个数字的问题?

标签: python algorithm factorization


【解决方案1】:

考虑简单地重复每个素因数是一个因数的次数,而不是指数列表。之后,处理生成的primefactors list-with-repetitions,itertools.combinations 正是您需要的——您只需要包含长度为 2 到 len(primefactors) - 1 的项目的组合(只有一个的组合是主要因素,所有因素都将是原始数字 - 如果您也想要这些,只需使用 range(1, len(primefactors) + 1) 而不是我的主要建议将使用的 range(2, len(primefactors)))。

结果中会出现重复(例如,6 将作为 12 的因子出现两次,因为后者的 primefactors 将是 [2, 2, 3])当然可以按照通常的方式清除它们方式(例如sorted(set(results)))。

要在给定 listOfAllPrimes 的情况下计算 primefactors,请考虑以下示例:

def getprimefactors(n):
    primefactors = []
    primeind = 0
    p = listOfAllPrimes[primeind]
    while p <= n:
        if n % p == 0:
            primefactors.append(p)
            n //= p
        else:
            primeind += 1
            p = listOfAllPrimes[primeind]
    return primefactors

【讨论】:

  • 很好的建议。我真的应该学习itertools。您的代码运行良好,稍作修改。
【解决方案2】:

您可以使用itertools.combinations() 获取所有可能的因子组合,一旦您获得了重复质数列表,例如[2, 2, 3, 3, 5] 对应180。然后,只需将每个组合中的分量相乘即可获得一个因子。

【讨论】:

    【解决方案3】:

    这里有一个简单有效的解决原始问题的方法:

    def getDivisors(n):
        divisors = []
        d = 1
        while d*d < n:
            if n % d == 0:
                divisors.append(d)
                divisors.append(n / d);
            d += 1
        if d*d == n:
            divisors.append(d)
        return divisors
    

    输出列表未排序。如果你愿意,你可以让它更“pythonic”,不管这意味着什么。

    【讨论】:

    • 这对我来说似乎不是很有效。这不只是测试所有可能的数字以查看它是否是一个因素吗?这似乎是效率最低的方法。不过,我可能会遗漏一些东西。
    • 不是每一个可能的数字,只有 n 的平方根。
    【解决方案4】:

    您为什么要从一组素因数开始您的解决方案?当你分解一个数字时,你可以很容易地得到它的所有素因子(重复),并从中得到每个因子的指数。考虑到这一点,你可以这样写:

    import itertools
    prime_factors = get_prime_factors(180) 
    # 2, 2, 3, 3, 5 
    factorization = [(f, len(list(fs))) for (f, fs) in itertools.groupby(prime_factors)]
    # [(2, 2), (3, 2), (5, 1)]
    values = [[(factor**e) for e in range(exp+1)] for (factor, exp) in factorization]
    # [[1, 2, 4], [1, 3, 9], [1, 5]]
    print sorted(product(xs) for xs in itertools.product(*values))
    # [1, 2, 3, 4, 5, 6, 9, 10, 12, 15, 18, 20, 30, 36, 45, 60, 90, 180]
    

    get_prime_factorsproduct 在这里没有实现,但你明白了(两者都非常简单)

    恕我直言,作为数学问题,欧拉问题可以使用函数式而不是命令式风格很好地解决(尽管我承认某些解决方案可能不会像期望的那样像 Python 那样)。

    【讨论】:

    • 谢谢,特别是关于功能风格的好建议。从 C 出来后,我现在正在学习 Python,对我来说,关于 Python 的一件奇怪的事情是它在许多不同的范例中工作的容易程度。我绝对应该利用这个机会来了解更多关于函数式编程的知识。
    【解决方案5】:

    具有一些更酷的 Python 功能:

    def factors( num ):
        for p in primes:
            if num==1: break # stop when num is 1
            while True: # these factors may be repeated 
                new, rest = divmod(num, p) # does div and mod at once
                if rest==0: # its divisible
                    yield p # current prime is factor
                    num = new # continue with the div'd number
                else:
                    break # no factor so break from the while
    
    
    print list(factors(2*2*3*3*5*7*11*11*13)) # [2, 2, 3, 3, 5, 7, 11, 11, 13] ofc
    

    【讨论】:

    • 有趣的是,divmod 是一个简洁(而且非常 Pythonic)的小功能!
    【解决方案6】:

    多合一的解决方案;即不需要现有的主要因素列表。

    #!/usr/bin/python3 -O
    
    from primegen import erat3 as generate_primes # see Note[1]
    import operator as op, functools as ft, itertools as it
    
    def all_factors(number):
        prime_powers= []
    
        for prime in generate_primes(): # for prime in listOfAllPrimes
            if prime > number: break
    
            this_prime_powers= [1]
            new_number, modulo= divmod(number, prime)
    
            while not modulo:
                number= new_number
                this_prime_powers.append(this_prime_powers[-1] * prime)
                new_number, modulo= divmod(number, prime)
    
            if len(this_prime_powers) > 1:
                prime_powers.append(this_prime_powers)
    
        # at this point:
        # if number was 360, prime_powers is [[1, 2, 4, 8], [1, 3, 9], [1, 5]]
        # if number was 210, prime_powers is [[1, 2], [1, 3], [1, 5], [1, 7]]
    
        return sorted(
            ft.reduce(op.mul, combination, 1)
            for combination in it.product(*prime_powers))
    
    if __name__ == "__main__":
        def num_result(number):
            return number, all_factors(number)
        print(num_result(360))
        print(num_result(210))
        print(num_result(7))
    

    注意[1]:作为素数生成器,您可以从How to implement an efficient infinite generator of prime numbers in Python? 中选择一个或使用您自己的(例如您的listOfAllPrimes)。

    这会产生一个完整的因子列表,即包括 1number 参数本身。如果你想省略这些,你可以使用all_factors(number)[1:-1]

    $ allfactors.py 
    (360, [1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 18, 20, 24, 30, 36, 40, 45, 60, 72, 90, 120, 180, 360])
    (210, [1, 2, 3, 5, 6, 7, 10, 14, 15, 21, 30, 35, 42, 70, 105, 210])
    (7, [1, 7])
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-04-19
      • 2012-08-09
      • 2018-09-04
      • 1970-01-01
      • 2019-02-07
      • 1970-01-01
      相关资源
      最近更新 更多