Python,ProjectEuler,Optimization使代码变慢

时间:2016-05-07 17:39:17

标签: python performance optimization

我使用以下代码来解决Project Euler问题14.对于那些不知道这个问题的人,我必须找到数量低于一百万且其Collat​​z序列中“步数”最多的数字。

largen = 0

for i in range (1, 1000000):
    n = 0
    k = i
    while k != 1:

        if k % 2 == 0:
            k = k/2
            n = n+1


        else:
            k = 3*k+1
            n = n+1

    if n > largen:
        largen = n
        answer = i


print "Value with highest number of terms in collatz sequence is %d which has %d terms in its collatz sequence." % (answer, largen)

这在大约1分20秒内给出了正确的答案。但是我想我可以通过以下方式加快速度。首先,我指示程序记住每个值i的collat​​z序列中的步数。然后,如果在找到数字k的序列的过程中,我登陆前一个数字i我已经计算了序列,我只是将i的序列中的项数加到我计算的数字上到目前为止,对于k。

例如,假设我正在尝试计算13序列中的步数。前3个步骤是13-40-20-10。现在我已经计算出序列中10的步数为6(10-5-16-8-4-2-1)。因此,13的序列中的步骤数是3到10加到需要从10到1所需的6,即总共9步。

为此,我将代码修改为以下内容:

nterms = [] # for each value i, contains number of terms in collatz sequence
used = [] # list of used values of i (so can add nterms[i-1] to collatz sequence which redirects to i)

largen = 0

for i in range (1, 1000000):

    n = 0
    k = i
    while k != 1:

        if k in used:
            n = n+nterms[k-1]
            break

        elif k % 2 == 0:
            k = k/2
            n = n+1

        else:
            k = 3*k+1
            n = n+1

    if n > largen:
        largen = n
        answer = i

    used.append(i)
    nterms.append(n)

print "Value with highest number of terms in collatz sequence is %d which has %d terms in its collatz sequence." % (answer, largen)

然而,当我尝试运行它时,我将MemoryError outprinted到终端屏幕。当我尝试使用较小的值(即高达10000)时,我会得到与原始代码相同的答案,但速度要慢得多(即需要7秒而不是1秒)。

为什么会这样?

3 个答案:

答案 0 :(得分:1)

优化的想法很好,但是你选择了错误的数据结构。

nterms = []
used = []

这两个列表用于存储您已经计算过的Collat​​z序列,对吧?但是要在列表中查找元素,时间复杂度为O(n),这是不够有效的。

相反,尝试使用字典,数字是键,以及它们的Collat​​z序列数作为值。例如,键10的值为6

答案 1 :(得分:1)

检查是否可以从k找到used会减慢计算速度,因为检查是否可以从列表中找到元素具有 O(n)时间复杂度。

您可以只使用最初具有1000000元素的一个元素,而不是使用两个列表,这些元素全部初始化为-1。然后在每次迭代时,一旦您知道Collat​​z编号,就将其更新为相应的索引,以便以后可以使用它:

largen = 0
answer = 0
memo = [-1] * 1000000
for i in xrange(1, 1000000):
    n = 0
    k = i
    while k != 1:
        # Since k can grow from original need to check it's within bounds
        if k < 1000000 and memo[k] != -1:
            n += memo[k]
            break
        if k % 2 == 0:
            k /= 2
        else:
            k = 3 * k + 1

        n += 1

    memo[i] = n
    if n > largen:
        largen = n
        answer = i

与字典缓存相比,这种方法在我的机器上快了大约10-15%。

答案 2 :(得分:0)

查找列表中的元素是O(n)操作,对于大型列表来说可能变得相当慢。另一方面,字典保证了恒定的查找时间复杂度(O(1)):

cache = {}

for i in range (1, 1000000):
    n = 0
    k = i
    while k != 1:

        if k in cache:
            n = n + cache[k]
            break

        if k % 2 == 0:
            k = k/2
            n = n+1


        else:
            k = 3*k+1
            n = n+1


    cache[i] = n
    if n > largen:
        largen = n
        answer = i


print "Value with highest number of terms in collatz sequence is %d which has %d terms in its collatz sequence." % (answer, largen)

在我的机器上,这种方法将解决方案的速度提高了约13倍,从OP的约26秒增加到本答案中提供的约2秒。