python itertools 2的幂的排列太慢了

时间:2018-04-12 11:23:14

标签: python performance permutation itertools

我遇到了一个非常奇怪的问题,似乎无法找到解决问题的方法。

以下代码找到n的素数因子分解,将素因子放入列表然后找到素数因子的所有可能的和变量,并打印出该列表的唯一值。

示例:44的素数因子是2 * 2 * 11,因此44将打印出来

2,2+2,11,2+11,2+2+11 = 2,4,11,13,15:

这是我的代码:

import math
import sys
import itertools
from itertools import permutations

def primes(n):
    primfac = []
    d = 2
    while d*d <= n:
        while (n % d) == 0:
            primfac.append(d)
            n //= d
        d += 1
    if n > 1:
       primfac.append(n)
    return primfac


def primecombo(n):
    b = []
    for i in range(1, len(primes(n))+1):
        for subset in permutations(primes(n), i):
            b.append(sum((subset)))
    a = list(set(b))
    a.sort()
    return a

代码本身似乎在大多数情况下都能正常工作,但是出于一些非常奇怪的原因,当你处理任何只有主要因素为2的数字时它会变得非常慢。

如果您尝试打印primecombo(444444)或打印primecombo(23452823),它几乎会立即打印结果,但如果您尝试2048或4096,它会变得非常慢。

任何人都可以看到为什么会这样,我可以做些什么来解决它?

1 个答案:

答案 0 :(得分:5)

简短回答

使用itertools.permutations使您的算法成为素数因子的冗余分区。使用itertools.combinations应该是一个相当大的改进,但我们仍然可以做得更好。

答案很长

使用itertools.permutations查找所有排列会使您的函数primecombo在因子数上运行,而不是指数。

让我们看一下因子数量 k 的时间复杂度。主导步骤是迭代permutations(primes(n), len(primes(n))。有 k!排列,你正在总结每一个。因此,算法的时间复杂度为

  

O(k * k!)

这就是为什么2048,有11个因素,比23452823长得难以忍受,有7个因素需要处理。

替代

幸运的是,访问每个排列都是不必要的。例如,如果您有因子2,3和4,则将对2,3和4的每个排列求和,这是多余的。一个快速的改进就是总和组合,但即便如此,当有多个因素出现时,我们有时会将相同的分区加两次。

以下解决方案通过使用Counter而不是list跟踪素数因子来解决此问题。稍后我们可以使用itertools.product

此算法能够在几毫秒内找到4096所需的总和,请参阅下面的时间复杂度分析。

import itertools
from collections import Counter

def primes(n):
    primfac = Counter()
    d = 2

    while d ** 2 <= n:
        while (n % d) == 0:
            primfac[d] += 1
            n //= d
        d += 1

    if n > 1:
       primfac[n] += 1

    return primfac

def primecombo(n):
    factor_sums = [[p * e for e in range(exp + 1)] for p, exp in primes(n).items()]

    sums = set(sum(partition) for partition in itertools.product(*factor_sums))

    return sums

primecombo(4096) # {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24}

时间复杂度

时间复杂性取决于素因子的分布。最糟糕的情况是,如果有 k 不同的因素。我们的itertools.product的大小为2 k 。从而制作算法

  

O(k * 2 k