为什么这个初级检测程序如此之慢?

时间:2015-06-24 13:23:52

标签: python performance

我正在尝试使用此代码

制作小于n的素数列表
stderr

但这需要超过5秒,是程序还是只是我的电脑。我刚才给它洒了一杯饮料,它不再那么快了,所以它可能就是电脑。如果是程序,我应该怎么做才能加快它。

我的想法是,break语句只是在范围内跳出for k循环,但它应该在范围内打破for i循环。

2 个答案:

答案 0 :(得分:1)

两个简单的改进是:

  • 循环使用奇数,因为偶数不是素数,但2除外。
  • 要测试数字的素数,请直接遍历到目前为止找到的素数。

primes_n(10**5)

在150毫秒内运行
from math import sqrt

def primes_n(n):
    primes = [2]
    for i in range(3, n+1, 2):
        for p in primes:
            if p > int(sqrt(i)):
                primes.append(i)
                break
            if i % k == 0:
                break
    return primes

如果你想比这更快,你将不得不实施sieve of Eratosthenes

在此处查看一些最快的实施:Fastest way to list all primes below N

答案 1 :(得分:1)

这很慢,因为有999900个循环运行(减去因break而跳过的循环,但也不计算从k in primes完成的循环。)

您可以采取一些优化措施:

  1. 使用集合代替列表来存储素数(k in primes最终会更快)。如果您需要,请返回sorted(primes)。但是,通过下一次优化,这一点毫无意义。
  2. 通过以下方式减少内循环中的迭代次数:
    1. 最初为素数集合添加2
    2. 只遍历素数集合,因为任何大于已知素数的数字都是素数本身或者具有小于其本身的素数因子。迭代一堆已知不是素数的数字是没有意义的。你清楚地意识到这一点,因为你只需要通过素数来检查可分性。
    3. 为了进一步优化这一点,您可以确保只迭代小于平方根的素数,但与其余的相比,这实际上是一个微优化。 编辑:我试过这个,显然计算平方根需要的时间比遍历素数列表要长。
  3. 我的运行时间:

    • 您的代码:运行时间为77毫秒
    • 使用set而不是list:37 ms;所需时间的48%,快两倍以上
    • 使用素数而不是范围到平方根:18ms;使用套装的时间占38%,比原来快4倍以上
    • 与之前相同,但将素数限制为i平方根下的素数:27ms;仍然比原版快2.9倍,但比之前的
    • 更慢,更复杂

    注意:我使用的是pypy,所以我的运行时间为77毫秒,而你的5秒是部分原因。但是当我在常规的python 2.7解释器上运行时,我得到了980毫秒(.98秒)。因此,您似乎还拥有一台速度较慢的计算机,以及一种优化不佳的算法。

    所以,这是我的最终代码:

    def primes_n(n):
        primes = [2]
        for i in range(3, n+1):
            for prime in primes:
                if i % prime == 0:
                    break
            else:
                primes.append(i)
        return primes
    

    更新:我在中间停止写我的午餐,所以我没有看到另一个人的额外优化。

    我在使用平方根位方面做得很差,所以使用它实际上并不慢(实现得很差)。因此,在应用他的优化之后,再加上我在回答评论中提到的小问题,我得到3ms的运行时间,比我之前的最佳速度快6倍。这是代码:

    def primes3_2(n):
        primes = [2]
        for i in range(3, n+1, 2):
            root_of_i = sqrt(i)
            for prime in primes:
                if prime <= root_of_i:
                    primes.append(i)
                    break
                if i % prime == 0:
                    break
        return primes