【发布时间】:2019-06-15 10:04:21
【问题描述】:
我正在努力提高我的编程逻辑技能,我正在观看videos 之一,了解如何处理斐波那契数列。
看了6:34的伪代码后,我写了这个:
In [14]: def my_fib(x, memo=dict()):
...: if memo.get(x):
...: return memo[x]
...: if x == 1 or x == 2:
...: result = 1
...: else:
...: result = my_fib(x - 1, memo) + my_fib(x -2, memo)
...: memo[x] = result
...: return result
这很好,但是当我观看视频到最后那个家伙斥责他的 python 代码时,我发现它与我的略有不同。
CS Dojo 代码:
In [68]: def fib_dyn_2(x, memo):
...: if memo[x] is not None:
...: return memo[x]
...: if x == 1 or x == 2:
...: result = 1
...: else:
...: result = fib_dyn_2(x-1, memo) + fib_dyn_2(x-2, memo)
...: memo[x] = result
...: return result
...:
...: def fib_memo(x):
...: memo = [None] * (x + 1)
...: return fib_dyn_2(x, memo)
我使用字典缓存他使用列表存在“轻微”差异。
让我感动的是我的代码似乎快了一点。当得到序列中的数字X >= 100 以及运行相同的数字时,序列不止一次。
即我的代码:
In [4]: %time my_fib(100)
CPU times: user 70 µs, sys: 44 µs, total: 114 µs
Wall time: 92 µs
Out[4]: 354224848179261915075L
CS Dojo 代码:
In [5]: %time fib_memo(100)
CPU times: user 99 µs, sys: 128 µs, total: 227 µs
Wall time: 187 µs
Out[5]: 354224848179261915075L
问题是哪个“更好”或更希望作为答案?
【问题讨论】:
-
我认为您无法真正回答这个问题。这取决于。通常你只做这样的性能优化,如果你真的需要,因为它们使代码更复杂(即使在你的例子中只是一点点),这通常会降低代码的可读性和可维护性,因为主要逻辑可能“淹没”在技术代码中
-
所以这种缓存一般来说是个好主意,如果性能是一个真正的问题,但可能视频的创建者跳过了这一步,只是介绍了函数式编程和真正的函数式编程语言的概念这样的一种兑现方式(我认为 haskell 就是这样做的)。
-
@jottbe 我假设您更喜欢基于可读性的
CS Dojo版本,对吗? -
啊,好吧,对不起,我被误导了。我认为这是关于函数式编程的,但似乎视频解决了性能问题。嗯,很奇怪,您的解决方案更快,因为像 dict 中的散列查找通常比基于索引的查找慢。顺便说一句,你是如何初始化备忘录的?因此,如果性能没有问题,我也不会使用 CS Dojo 创建的原始版本。
-
我认为您的代码速度如此之快的原因是因为当您定义
my_fib时,dict()会在那里创建,并且每次运行该函数时都已经存在。另一方面,fib_memo每次调用该函数时都会创建一个新列表。如果您只计时fib_dyn_2而不是时间可能会更近。希望这可以帮助。 TLDR:您在参数列表中定义的字典会在定义函数后立即创建。