Python分解质因数的性能

13

我对Python相对较新,对于两个相对简单的代码块的性能感到困惑。第一个函数给定一个素数列表,生成一个数字n的质因数分解。第二个生成数字n的所有因子的列表。我本以为prime_factor(对于相同的n)会比factors快,但事实并非如此。我不是在寻找更好的算法,而是想了解为什么prime_factorfactors慢这么多。

def prime_factor(n, primes):
  prime_factors = []
  i = 0
  while n != 1:
      if n % primes[i] == 0:
          factor = primes[i]
          prime_factors.append(factor)
          n = n // factor
      else: i += 1
  return prime_factors

import math
def factors(n):
  if n == 0: return []
  factors = {1, n}
  for i in range(2, math.floor(n ** (1/2)) + 1):
      if n % i == 0:
          factors.add(i)
          factors.add(n // i)
  return list(factors)

使用 timeit 模块,

{ i:factors(i) for i in range(1, 10000) } 需要 2.5 秒

{ i:prime_factor(i, primes) for i in range(1, 10000) } 需要 17 秒

这对我来说很惊讶。 factors 检查从 1 到 sqrt(n) 的每个数字,而 prime_factor 只检查质数。 我希望能获得任何有助于理解这两个函数性能特征的帮助。

谢谢

编辑:(响应 roliu) 以下是我生成从 2 到 up_to 的质数列表的代码:

def primes_up_to(up_to):
  marked = [0] * up_to
  value = 3
  s = 2
  primes = [2]
  while value < up_to:
      if marked[value] == 0:
          primes.append(value)
          i = value
          while i < up_to:
              marked[i] = 1
              i += value
      value += 2
  return primes

3
你在哪里定义你提供给prime_factors()函数的primes参数? - rliu
1个回答

12

没有看到你使用的是什么来计算质数(无法运行您的代码),我们只能猜测。

但其中一部分内容是纯数学:在小于n的数中大约有n/log(n)个质数(非常粗略地说),这比sqrt(n)要大得多。因此,当您传递一个质数时,prime_factor(n)将进行更多的工作:在找到第一个质因数(n本身!)之前,它需要通过O(n/log(n))次操作,而factors(n)在O(sqrt(n))次操作后放弃。

这可能非常重要。例如,sqrt(10000)只有100,但小于10000的质数有1229个。因此,prime_factor(n)可能需要做超过10倍的工作来处理您范围内的大质数。


3
当然,prime_factor函数在找到大于n平方根的一个质因数后也可以停止,因为n只有一个大于其平方根的质因数。 - rici
事实上,如果传递给prime_factor的列表确实只包含按升序排列的质数,则它会检查比终止之前的因子更少的数字; 它可以跳过所有合成数字! - Jeremy West
1
所有这些都对某人有帮助;但原帖作者说他们不是在寻找更好的算法 - 他们只想知道为什么给定的代码会表现出这样的行为。我认为这很清楚了;-) - Tim Peters
@Tim Peters 或许我应该澄清一下,因为我并不是在反驳你的答案,只是确保 OP 看到这个问题。对于像 m=2p 这样的值,其中 p 是质数,prime_factor(n) 首先除以 2,然后扫描 [3,p] 中的每个质数,直到得出 p 是质数为止。他本可以在达到 sqrt(p) 时停止。如果他这样做了,prime_factor(n) 应该与 factors(n) 一样快甚至更快。如果他从小于 sqrt(n) 的最大质数开始,他甚至可以做得更好。 - Jeremy West
1
非常感谢,这非常有帮助。添加计数器验证了您的预期。prime_factor 循环了大约 8700 万次,而 factors 只循环了 2000 万次。 - user2869495

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接