【问题标题】:Tracing the execution of a cached Fibonacci Sequence (using python decorator)跟踪缓存的斐波那契数列的执行(使用 python 装饰器)
【发布时间】:2015-05-23 04:07:26
【问题描述】:

使用下面带有斐波那契函数的常见记忆模式,我在想象它的实际工作方式时遇到了一些麻烦。我理解这个概念,但在思考程序时我会感到困惑并开始思考。

def memoize(func):
    cache={}
    def wrapper(*args,**kwargs):
        if args in cache:
            return cache[args]
        else:
            cache[args]=func(*args) #to store the result, doesn't fib still have to go through all it's calculations here?
            return cache[args]
    return wrapper

@memoize
def fib(n):
    if n==0:
        return 0
    elif n==1:
        return 1
    else:
        return fib(n-2)+fib(n-1)

所以从概念上讲,我了解递归是如何工作的,并且我了解记忆化背后的想法(首先尝试查找它以避免多次运行相同的计算)。但是让我感到困惑的是,当您为尚未看到参数的函数调用向字典中添加键时,如何在不通过完整函数调用的情况下完成此操作。

例如,如果我们调用 fib(5),那么当我们点击 cache[args]=func(*args) 时,我们会在字典中添加一对 key:value5:fib(5)(因为还没有看到 5)。但是k:v 对中的v 是对 fib(5) 的函数调用,它仍然需要返回它的值,我认为这需要经历整个递归过程。如果我们尝试缓存返回值,那么memoization如何在不经过整个递归过程的情况下为fib(5)获取这个返回值?

【问题讨论】:

    标签: python caching recursion decorator memoization


    【解决方案1】:

    你说:“但是 k:v 对中的 v 是对 fib(5) 的函数调用,它仍然需要返回它的值,我认为这需要经历整个递归过程。”我认为这里的v是函数返回的值,所以当你再次遇到相同的k时,会在缓存中找到并返回。

    我已在您的代码中添加了一些打印输出,以查看发生了什么。 Level 将是递归的级别:

    def memoize(func):
        cache={}
        def wrapper(*args,**kwargs):
            if args[0] in cache:
                print("  "*args[1]+"in cache: fib("+str(args[0])+")")
                return cache[args[0]]
            else:
                cache[args[0]]=func(*args)
                return cache[args[0]]
        return wrapper
    
    @memoize
    def fib(n, level):
        print("  "*level+"fib("+str(n)+")")
        if n==0:
            return 0
        elif n==1:
            return 1
        else:
            return fib(n-2, level+1)+fib(n-1, level+1)
    

    所以 fib(5, 0) 的结果如下:

    fib(5)
      fib(3)
        fib(1)
        fib(2)
          fib(0)
          in cache: fib(1)
      fib(4)
        in cache: fib(2)
        in cache: fib(3)
    

    【讨论】:

    • 对,但是要得到返回值不是要计算fib(5)吗?
    • 所以我认为关键是 5:fib(5) 只有在整个递归过程之后才会进入缓存。但是fib(1)、fib(2)和fib(3)会在这个过程中从缓存中使用。
    猜你喜欢
    • 2014-01-20
    • 2020-12-12
    • 1970-01-01
    • 2013-08-03
    • 2013-02-24
    • 1970-01-01
    • 2017-07-22
    • 2020-01-18
    • 2014-05-23
    相关资源
    最近更新 更多