2次幂的python itertools置换太慢

2次幂的python itertools置换太慢,python,performance,permutation,itertools,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):

我有一个非常奇怪的问题,似乎找不到解决它的方法

以下代码查找
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
导入数学
导入系统
进口itertools
从itertools导入置换
def素数(n):
primfac=[]
d=2
而d*d 1:
primfac.append(n)
返回primfac
def primecombo(n):
b=[]
对于范围(1,len(素数(n))+1)内的i:
对于置换中的子集(素数(n),i):
b、 追加(和((子集)))
a=列表(集合(b))
a、 排序()
归还
代码本身在大多数情况下似乎运行良好且高效,但由于某些非常奇怪的原因,当您处理的任何数字的唯一素数为2时,它的速度会变得非常缓慢

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

有人知道为什么会这样吗?我能做些什么来解决它?

简短回答 使用
itertools.permutations
可以使算法对素因子的冗余分区求和。使用
itertools.compositions
应该是一个相当大的改进,但我们仍然可以做得更好

长话短说 使用
itertools查找所有置换。置换
使函数
primecombo
在因子数量方面以阶乘时间运行,比指数时间运行得最差

让我们看看关于因子k的数量的时间复杂性。主要步骤是迭代
置换(素数(n),len(素数(n))
。有k!个置换,你要对每个置换求和。因此,你的算法的时间复杂度是

O(k*k!)

这就是为什么有11个因素的2048比有7个因素的23452823要长得令人无法忍受的原因

可供替代的 幸运的是,访问每个排列是不必要的。举例来说,如果你有因子2、3和4,你将对每个2、3和4的排列求和,这是多余的。一个快速的改进是对组合求和,但即使如此,当有因子出现超过on时,我们有时也会对同一分区求和两次行政长官

下面的解决方案通过使用
计数器而不是
列表来跟踪基本因子来解决此问题。这以后允许我们使用
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}
导入itertools
从收款进口柜台
def素数(n):
primfac=计数器()
d=2
而d**2 1:
primfac[n]+=1
返回primfac
def primecombo(n):
因子_和=[[p*e表示范围内的e(exp+1)]表示p,exp表示素数(n)。items()]
sums=集合(itertools.product(*factor_sums))中分区的总和(分区)
回报金额
素数组合(4096)#{0,2,4,6,8,10,12,14,16,18,20,22,24}
时间复杂性 时间复杂度取决于主因子的分布。如果存在k个不同因子,则最糟糕的情况出现。我们的
itertools.product
则大小为2k。因此,算法

O(k*2k)


23452823只有7个因子,而2048只有11个因子。由于排列是指数型的,所以它的时间长得令人无法忍受。此外,您在每次迭代中都要重新计算素数,请将它们存储在变量中。此外,您不需要素数的排列,只需要组合。这将大大缩短您的时间。谢谢Olivier它大大加快了速度当一个因子存在多次时,通过使用因子计数器并对其组合进行迭代,可以进一步加快速度。如果没有人这样做,我稍后会写一个完整的答案。请在您的伟大答案中添加OP的原始代码和您的代码的运行时间,好吗?