【问题标题】:Memoization python function记忆python函数
【发布时间】:2014-06-02 20:15:54
【问题描述】:

这里有一小段代码可以将每个函数转换成它的记忆版本。

def memoize(f): # Memoize a given function f
    def memf(*x):
        if x not in memf.cache:
            memf.cache[x] = f(*x)
        return memf.cache[x]

    memf.cache = {}
    return memf

例如,如果我们有一个函数fib,它返回nth 斐波那契数:

def fib(n):
    if n < 2:
        return 1
    else:
        return fib(n-1) + fib(n-2)

现在,上面的函数可以通过使用来记忆了

fib = memoize(fib)

到目前为止一切都很好,但我无法理解的是,如果我们这样做,而不是:

fib = memoize(fib)

我们改为这样做:

fib2 = memoize(fib)

函数fib2 不是fibmemoized 函数。当我们运行fib2 时,它就像普通的 fib 一样运行。请解释为什么这个 memoize 函数被应用于说一个函数 f 当且仅当我们使用:

f = memoize(f)

memoization 代码取自 edx.org 提供的 MOOC 6.00x。它现在没有运行,这就是我来这里询问的原因。

【问题讨论】:

标签: python function memoization


【解决方案1】:

因为当fib2递归调用时

return fib(n-1) + fib(n-2)

那是原始的,un-memoized 版本;您只能在第一次调用fib2 时获得装饰器的好处,而不是所有对香草fib 的递归调用。

会发生什么:

  1. 当你调用fib2时,你实际上是在调用memf,这
  2. 如果参数不在缓存中(因为它们不会在第一次调用中),则依次调用 fib,然后
  3. fib,递归调用fib。这不是修饰版本fib2,所以所有其余的递归调用都不是memoized。

如果您再次使用相同的参数调用fib2,则将从缓存中返回,但您已经失去了大部分好处。

您可以一般使用以下方法创建装饰函数:

decorated = decorator(original)

但是由于你的函数被装饰是递归,你会遇到问题。


下面是一个演示示例。创建两个相同的fib 函数fib_decfib_undec。修改装饰器告诉你它在做什么:

def memoize(f): # Memoize a given function f
    def memf(*x):
        print("Memoized call.")
        if x not in memf.cache:
            print("Filling cache.")
            memf.cache[x] = f(*x)
        else:
            print("Cache retrieve.")
        return memf.cache[x]
    memf.cache = {}
    return memf

然后运行:

fib_dec = memoize(fib_dec) # fully memoized
fib_undec_1 = memoize(fib_undec) # not fully memoized

print("Calling fib_dec(2)")
print(fib_dec(2))
print("Calling fib_dec(1)")
print(fib_dec(1))
print("Calling fib_undec_1(2)")
print(fib_undec_1(2))
print("Calling fib_undec_1(1)")
print(fib_undec_1(1))
print("Calling fib_undec_1(2)")
print(fib_undec_1(2))

这将给出:

Calling fib_dec(2) # fully decorated
Memoized call.
Filling cache.
Memoized call.
Filling cache.
Memoized call. # recusive calls all memoized
Filling cache.
2
Calling fib_dec(1)
Memoized call.
Cache retrieve. # previous recursive result cached
1
Calling fib_undec_1(2) # not fully memoized
Memoized call. # only one memoized call, recursion not memoized
Filling cache.
2
Calling fib_undec_1(1)
Memoized call.
Filling cache. # recursive result not cached
1
Calling fib_undec_1(2)
Memoized call.
Cache retrieve. # but original call is cached
2

【讨论】:

  • 能否详细说明。我现在无法理解它。
  • 感谢您的详细说明,但我仍不清楚。假设我们使用正确的东西 fib = memoize(fib) 那么 fib 将调用 fib ,然后再调用 fib。这样他们就永远无法达到 fib = fib(n-1) + fib(n-2) 的定义,那么我将如何得到答案呢?
  • 我想说的是 fib 会调用 memoize(fib) 但是因为 memoize 里面的 fib 确实是 fib 这就是为什么 memoize(fib) 会调用 memoize(fib)(n-1) 和记忆(fib)(n-2)。这将永远持续下去,并且 fib 定义当 n
  • 是的,它会 - 在某些时候你会打电话给memoize(fib)(1)。第一次发生这种情况时,memf 将调用fib(1)cache 的结果(实际上是cache.update({1: 1})),之后它将像任何其他值一样从cache 中检索。
  • 请解释一下当我们使用 fib = memoize(fib) 时它是如何工作的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-03
  • 2010-12-04
  • 2013-05-04
  • 2021-03-30
  • 1970-01-01
  • 2014-03-04
相关资源
最近更新 更多