我遇到了一个非常奇怪的问题,似乎无法找到解决问题的方法。
以下代码找到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,它会变得非常慢。
任何人都可以看到为什么会这样,我可以做些什么来解决它?
答案 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 )