如何改进euler 14的代码?

时间:2012-04-09 19:57:29

标签: python

我解决了Euler problem 14,但我使用的程序非常慢。我看看其他人做了什么,他们都提出了优雅的解决方案。我试图理解他们的代码但没有取得多大成功。

这是我的代码(确定Collat​​z链长度的函数

def collatz(n):
a=1
while n!=1:
    if n%2==0:
        n=n/2
    else:
        n=3*n+1
    a+=1
return a

然后我用蛮力。它很慢,我知道它很弱。有人能告诉我为什么我的代码很弱,以及如何用简单的英语改进我的代码。 请记住,我是初学者,我的编程技巧是基本的。

2 个答案:

答案 0 :(得分:11)

您可以保留链启动的缓存及其生成的长度,而不是从开始到结束计算每个可能的链。例如,对于链

13 40  20  10  5  16  8  4  2  1

您可以记住以下内容:

  1. 以13开头的Collat​​z链长度为10
  2. 以40开头的Collat​​z链长度为9
  3. 以20开头的Collat​​z链长度为8
  4. ......等等。

    我们可以在遇到已经在缓存中的数字时立即使用此保存的信息停止计算链。

    实施

    在Python中使用词典将起始数字与其链长相关联:

    chain_sizes = {}
    chain_sizes[13] = 10
    chain_sizes[40] = 9
    chain_sizes[40]   # => 9
    20 in chain_sizes # => False
    

    现在你只需要调整你的算法来使用这个字典(用值填充它以及查找中间数字)。

    顺便说一下,使用递归可以非常好地表达。这里可能出现的链尺寸不会溢出堆栈:)

答案 1 :(得分:1)

简单地说,因为我的英语太可怕了; - )

Forall n >= 1, C(n) = n/2     if n even,
                      3*n + 1 if n odd

可以一次计算几个连续的迭代次数。

以k 0位结尾的数字的第k次迭代:

C^k(a*2^k) = a

(2k)以k 1位结尾的数字的迭代:

C^(2k)(a*2^k + 2^k - 1) = a*3^k + 3^k - 1 = (a + 1)*3^k - 1

比照。公式Wikipédia article (in French);另请参阅我的Python包my website中的tnp1(法语)和模块DSPython

将以下代码与Niklas B解释的记忆技术结合起来:

#!/usr/bin/env python
# -*- coding: latin-1 -*-
from __future__ import division  # Python 3 style in Python 2
from __future__ import print_function  # Python 3 style in Python 2


def C(n):
    """Pre: n: int >= 1
    Result: int >= 1"""
    return (n//2 if n%2 == 0
            else n*3 + 1)


def Ck(n, k):
    """Pre: n: int >= 1
         k: int >= 0
    Result: int >= 1"""
    while k > 0:
        while (n%2 == 0) and k:  # n even
            n //= 2
            k -= 1

        if (n == 1) and k:
            n = 4
            k -= 1
        else:
            nb = 0
            while (n > 1) and n%2 and (k > 1):  # n odd != 1
                n //= 2
                nb += 1
                k -= 2

            if n%2 and (k == 1):
                n = (n + 1)*(3**(nb + 1)) - 2
                k -= 1
            elif nb:
                n = (n + 1)*(3**nb) - 1

    return n


def C_length(n):
    """Pre: n: int >= 1
    Result: int >= 1"""
    l = 1

    while n > 1:
        while (n > 1) and (n%2 == 0):  # n even
            n //= 2
            l += 1

        nb = 0
        while (n > 1) and n%2:  # n odd != 1
            n //= 2
            nb += 1
            l += 2
        if nb:
            n = (n + 1)*(3**nb) - 1

    return l


if __name__ == '__main__':
    for n in range(1, 51):
        print(n, ': length =', C_length(n))