2的幂的python itertools排列太慢了
Posted
技术标签:
【中文标题】2的幂的python itertools排列太慢了【英文标题】:python itertools permutations for powers of 2 is too slow 【发布时间】:2018-04-12 11:23:13 【问题描述】:我遇到了一个非常奇怪的问题,似乎找不到解决方法。
以下代码找到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 的数字时,它变得异常缓慢。
如果您尝试 print primecombo(444444) 或 print primecombo(23452823),它几乎会立即打印结果,但如果您尝试 2048 或 4096,它会变得非常慢。
谁能明白为什么会出现这种情况以及我可以做些什么来解决它?
【问题讨论】:
23452823 只有 7 个因子,而 2048 有 11 个因子。由于得到排列是指数的,所以它的时间长得令人难以忍受。此外,您在每次迭代时都重新计算素数,而是将它们存储在一个变量中。 另外,你不需要你的素数的排列,只需要组合。这应该会显着缩短您的时间。 感谢 Olivier 大大加快了速度 当一个因子存在多次时,可以通过使用因子计数器并迭代其组合来加快速度,如果没有人这样做,我稍后会写一个完整的答案 【参考方案1】:简答
使用itertools.permutations
可使您的算法对素因子的冗余分区求和。使用itertools.combinations
应该是一个相当大的改进,但我们仍然可以做得更好。
长答案
使用itertools.permutations
查找所有排列使您的函数primecombo
在因子数量方面运行在阶乘时间内,比指数最差。
让我们看一下关于因子 k 数量的时间复杂度。主要步骤是迭代permutations(primes(n), len(primes(n))
。有 k! 个排列,而您正在对每个排列求和。因此,您的算法的时间复杂度是
O(k * k!)
这就是为什么有 11 个因子的 2048 比有 7 个因子要处理的 23452823 长得多。
另类
幸运的是,不必访问每个排列。例如,如果您有因子 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
的大小为 2k。从而使算法
O(k * 2k)
【讨论】:
能否请您在您的好答案中添加 OP 原始代码和您的代码的运行时间?以上是关于2的幂的python itertools排列太慢了的主要内容,如果未能解决你的问题,请参考以下文章