【发布时间】:2014-03-08 21:00:52
【问题描述】:
前奏
对于一个特定问题,我有两种实现方式,一种是递归的,一种是迭代的,我想知道是什么导致迭代解决方案比递归解决方案慢约 30%。
鉴于递归解决方案,我编写了一个迭代解决方案,使堆栈显式。
显然,我只是简单地模仿递归正在做的事情,因此 Python 引擎当然可以更好地优化以处理簿记。但是我们能不能写出类似性能的迭代方法呢?
我的案例研究是 Problem #14 欧拉项目。
找到起始数低于 100 万的最长 Collatz 链。
代码
这是一个简洁的递归解决方案(归功于问题线程中的 veritas 以及来自 jJjjJ 的优化):
def solve_PE14_recursive(ub=10**6):
def collatz_r(n):
if not n in table:
if n % 2 == 0:
table[n] = collatz_r(n // 2) + 1
elif n % 4 == 3:
table[n] = collatz_r((3 * n + 1) // 2) + 2
else:
table[n] = collatz_r((3 * n + 1) // 4) + 3
return table[n]
table = {1: 1}
return max(xrange(ub // 2 + 1, ub, 2), key=collatz_r)
这是我的迭代版本:
def solve_PE14_iterative(ub=10**6):
def collatz_i(n):
stack = []
while not n in table:
if n % 2 == 0:
x, y = n // 2, 1
elif n % 4 == 3:
x, y = (3 * n + 1) // 2, 2
else:
x, y = (3 * n + 1) // 4, 3
stack.append((n, y))
n = x
ysum = table[n]
for x, y in reversed(stack):
ysum += y
table[x] = ysum
return ysum
table = {1: 1}
return max(xrange(ub // 2 + 1, ub, 2), key=collatz_i)
以及使用 IPython 在我的机器(具有大量内存的 i7 机器)上的计时:
In [3]: %timeit solve_PE14_recursive()
1 loops, best of 3: 942 ms per loop
In [4]: %timeit solve_PE14_iterative()
1 loops, best of 3: 1.35 s per loop
评论
递归解决方案很棒:
- 经过优化,可根据两个最低有效位跳过一两步。
我最初的解决方案没有跳过任何 Collatz 步骤,耗时约 1.86 秒 - 很难达到 Python 的默认递归限制 1000。
collatz_r(9780657630)返回 1133,但需要少于 1000 次递归调用。 - 记忆避免回溯
-
collatz_r为max按需计算长度
玩弄它,时间似乎精确到 +/- 5 毫秒。
像 C 和 Haskell 这样具有静态类型的语言可以获得低于 100 毫秒的时间。
我将 memoization table 的初始化放在这个问题的设计方法中,以便时间反映每次调用时表值的“重新发现”。
collatz_r(2**1002) 提升 RuntimeError: maximum recursion depth exceeded。collatz_i(2**1002) 高兴地返回 1003。
我熟悉生成器、协程和装饰器。
我正在使用 Python 2.7。我也很高兴使用 Numpy(我的机器上是 1.8)。
我在寻找什么
- 缩小性能差距的迭代解决方案
- 讨论 Python 如何处理递归
- 与显式堆栈相关的性能损失的详细信息
我主要寻找第一个,尽管第二个和第三个对这个问题非常重要,并且会增加我对 Python 的理解。
【问题讨论】:
-
好问题。我在探索节点树时观察到了同样的行为。
-
我怀疑递归方法中的隐式堆栈(即调用堆栈)正在使用非常高效的 c,并且比使用 python 列表的显式堆栈提供更多的性能(它必须做更多工作不仅仅是对堆栈/帧指针进行几次操作)。
-
您是否尝试过使用deque 作为您的堆栈?
-
@cmh 好电话,该注释实际上是在我的第一次修订中,但在我随后的修改中失败了。 @nmclean 实际上是的。
reversed(stack)告诉我。我不记得结果是什么,但我稍后会报告。
标签: python performance recursion iteration