当我努力做Problem 14 in Project Euler时,我发现我可以使用一种称为memoization的东西来加速我的过程(我让它运行了15分钟,它仍然没有回复答案)。问题是,我该如何实现它?我试过,但是我得到了一个keyerror(返回的值无效)。这让我很烦恼,因为我很肯定我可以对此应用memoization并加快速度。
lookup = {}
def countTerms(n):
arg = n
count = 1
while n is not 1:
count += 1
if not n%2:
n /= 2
else:
n = (n*3 + 1)
if n not in lookup:
lookup[n] = count
return lookup[n], arg
print max(countTerms(i) for i in range(500001, 1000000, 2))
感谢。
答案 0 :(得分:3)
对于Collatz序列,记忆的重点是避免计算已经完成的列表部分。序列的其余部分完全由当前值确定。因此,我们希望尽可能经常检查表格,并尽快摆脱剩下的计算。
def collatz_sequence(start, table={}): # cheeky trick: store the (mutable) table as a default argument
"""Returns the Collatz sequence for a given starting number"""
l = []
n = start
while n not in l: # break if we find ourself in a cycle
# (don't assume the Collatz conjecture!)
if n in table:
l += table[n]
break
elif n%2 == 0:
l.append(n)
n = n//2
else:
l.append(n)
n = (3*n) + 1
table.update({n: l[i:] for i, n in enumerate(l) if n not in table})
return l
有效吗?让我们监视它以确保使用的是备忘元素:
class NoisyDict(dict):
def __getitem__(self, item):
print("getting", item)
return dict.__getitem__(self, item)
def collatz_sequence(start, table=NoisyDict()):
# etc
In [26]: collatz_sequence(5)
Out[26]: [5, 16, 8, 4, 2, 1]
In [27]: collatz_sequence(5)
getting 5
Out[27]: [5, 16, 8, 4, 2, 1]
In [28]: collatz_sequence(32)
getting 16
Out[28]: [32, 16, 8, 4, 2, 1]
In [29]: collatz_sequence.__defaults__[0]
Out[29]:
{1: [1],
2: [2, 1],
4: [4, 2, 1],
5: [5, 16, 8, 4, 2, 1],
8: [8, 4, 2, 1],
16: [16, 8, 4, 2, 1],
32: [32, 16, 8, 4, 2, 1]}
编辑:我知道可以优化它!秘诀是函数中有两个位置(两个返回点)我们知道l
和table
没有共享任何元素。虽然之前我通过测试它避免使用table.update
中已有的元素调用table
,但这个版本的函数反而利用了我们对控制流的了解,节省了大量时间。
[collatz_sequence(x) for x in range(500001, 1000000)]
现在在我的计算机上大约2秒钟,而@ welter的版本时钟在400ms内表达类似的表达式。我认为这是因为函数实际上并没有计算相同的东西 - 我的版本生成整个序列,而@ welter只是找到它的长度。所以我不认为我可以将我的实施速度降低到相同的速度。
def collatz_sequence(start, table={}): # cheeky trick: store the (mutable) table as a default argument
"""Returns the Collatz sequence for a given starting number"""
l = []
n = start
while n not in l: # break if we find ourself in a cycle
# (don't assume the Collatz conjecture!)
if n in table:
table.update({x: l[i:] for i, x in enumerate(l)})
return l + table[n]
elif n%2 == 0:
l.append(n)
n = n//2
else:
l.append(n)
n = (3*n) + 1
table.update({x: l[i:] for i, x in enumerate(l)})
return l
PS - 发现错误!
答案 1 :(得分:3)
还有一种很好的递归方式,它可能比poorsod的解决方案慢,但它更类似于你的初始代码,所以你可能更容易理解。
lookup = {}
def countTerms(n):
if n not in lookup:
if n == 1:
lookup[n] = 1
elif not n % 2:
lookup[n] = countTerms(n / 2)[0] + 1
else:
lookup[n] = countTerms(n*3 + 1)[0] + 1
return lookup[n], n
print max(countTerms(i) for i in range(500001, 1000000, 2))
答案 2 :(得分:-1)
这是我对PE14的解决方案:
memo = {1:1}
def get_collatz(n):
if n in memo : return memo[n]
if n % 2 == 0:
terms = get_collatz(n/2) + 1
else:
terms = get_collatz(3*n + 1) + 1
memo[n] = terms
return terms
compare = 0
for x in xrange(1, 999999):
if x not in memo:
ctz = get_collatz(x)
if ctz > compare:
compare = ctz
culprit = x
print culprit