为什么我的代码速度慢(甚至可以工作)? [项目Euler 12] [Python 3.3]

时间:2013-10-01 18:25:53

标签: python performance python-3.x

我为Project Euler #12编写了此代码。它非常慢(或不起作用),我发现另一个非常相似的代码并立即得到答案。 我的代码:

import math

def get_triangular(nth):
    return sum(range(1,nth+1))

def get_divisors_count(n):
    divisors = 0
    for a in range(1,math.ceil(n/2)+1):
        if n%a == 0:
            divisors += 1
    return divisors

a = 1
while(True):
    if get_divisors_count(get_triangular(a)) > 500:
        print(a)
    a += 1

我在Stack Overflow上找到的代码:

import math

def main():
    l = []
    one = 0
    a = 1
    b = 2
    while one == 0:
        a = a + b 
        b += 1
        l = []

        sqrt_a = int(math.sqrt(a))

        for x in range(1, sqrt_a + 1):
            if a % x == 0:
                l.append(x)
                if x < math.sqrt(a):
                    l.append(a // x)
                if len(l) > 500:
                    # print(a)
                    one = 1

    print(a, b, len(l))

if __name__ == '__main__':
    main()

1 个答案:

答案 0 :(得分:3)

首先,你的程序永远不会结束。你有一个没有while True的{​​{1}}循环,或任何其他退出循环的方法。您发布的其他代码包含break而不是while one == 0,并且只要while True设置one = 1。这有点尴尬,但它确实有效。

所以:

len(l) > 500

这仍然很慢,但不是无限慢。


下一个最大的区别是你计算到n / 2 + 1,而另一个代码计数到sqrt(n),并计算每个除数两次。 (为什么这样做?想一想:如果a = 1 while(True): if get_divisors_count(get_triangular(a)) > 500: print(a) break a += 1 是[{1}}的除数,那么a也是如此,其中只有一个必须小于n除非他们都等于它。)


你也在其他程序没有的一些领域浪费了一些时间,比如反复计算n/a,而不是保持一个运行总和并做sqrt(n)。另一方面,你已经节省了一些时间,只需保留一个除数,而不是建立一个列表然后再取其长度。

但与之前的问题相比,这些都是微不足道的。至少您的程序现在具有相同的算法复杂度sum(range(1, nth+1)),而不是running_sum += a(或无限);在我的机器上,它在15.3秒内比12.1运行。

如果你真的想让它更快,有两个主要选择:

  1. 以数学方式看待它,看看有没有比蛮力更好的解决方法。 (提示:可以看看素数因子化对你有什么帮助吗?)
  2. 弄清楚是否有可以记忆的信息(缓存)会有所帮助。例如,如果你想计算96的因子,并且你已经有24的因子,那对你有什么好处吗?如果你只计算因子数怎么办?或者只是主要因素?