【问题标题】:How to make a recursive fib-function return the correct value with memoization如何通过记忆化使递归 fib 函数返回正确的值
【发布时间】:2019-09-17 12:11:38
【问题描述】:

我正在学习递归函数中的记忆,并在 Youtube 上偶然发现了一个斐波那契示例。我从来没有见过这个人运行代码,所以也许他写错了。

当我复制代码并尝试运行它时,我首先收到一个错误,因为我声明了没有范围的备忘录,所以我只是将范围设置为输入 + 1(因为我没有使用 0 索引)从而解决了这个问题。但是现在我卡在了错误的返回值上。

def fib(num, memo=[]):
    if num == 0:
        return 0
    elif num == 1 or num == 2:
        return 1
    elif memo[num] != None:
        return memo[num]
    else:
        memo[num] = fib(num-1, memo) + fib(num-2, memo)
        return memo[num]

uInput = int(input("Fibonacci: "))
memo = list(range(uInput + 1))
fibNum = fib(uInput, memo)

print(fibNum)

上面的代码没有抛出错误,只是简单地返回“uInput”值。因此,如果我输入 6,对于第 6 个斐波那契数,程序将返回 6 而不是 8,这是实际的第 6 个数。我不知道为什么会发生这种情况。

当我用谷歌搜索这个问题时,我发现了建议使用字典而不是列表的线程。如果这是唯一的方法,那就太好了。但是,如果有办法让它与列表一起工作,我想了解它是如何完成的,以便我理解为什么会遇到这个问题。

【问题讨论】:

    标签: python recursion fibonacci memoization


    【解决方案1】:

    访问memo[num] 永远不会返回None。如果num 超出范围,将引发IndexError。此外,您通常不想将 memo 参数传递给您的函数,而是让它依赖于自己的 memo 对象。

    在您的情况下,您想检查索引是否在len 的范围内。每当num 超出范围时,必须递归计算输出并进行记忆。只有这样才能退货。

    def fib(num, memo=[0, 1]):
        if num >= len(memo):
            memo.append(fib(num - 1) +  fib(num - 2))
        return memo[num]
    
    print(fib(10)) # 55
    

    顺便说一下,上面可以很容易地转换成一个迭代函数,这在 Python 中通常更有效。

    def fib(num, memo=[0, 1]):
        while num >= len(memo):
            memo.append(memo[-1] + memo[-2])
        return memo[num]
    

    【讨论】:

    • 我真的很喜欢 append 的想法,我发现它看起来更棘手,因为它看起来第一眼。为此 +1。
    • 这可行,通过您必须的修改,也可以使用递归调用发送备忘录。该函数需要两个参数,如果两个参数都没有发送,则会抛出错误。
    【解决方案2】:

    您用list(range(uInput + 1)) 填写了memo 列表。然后你的函数测试memo[num] != None,这必须是真的。所以它总是return memo[num],从而返回range函数放在memo列表中的数字。

    你应该删除这一行

    memo = list(range(uInput + 1))
    

    并且只在调用你的函数时传递第一个参数。

    【讨论】:

      【解决方案3】:

      这里有错误:

      memo = list(range(uInput + 1))  # wrong
      

      memo 应在索引i 处包含None,对于尚未计算的每个结果fib(i)

      memo = [None] * (uInput + 1)  # right
      

      备忘录可以初始化为序列0,1的开头,这将简化功能:

      def fib(num, memo):
          if memo[num] is None:
             memo[num] = fib(num-1, memo) + fib(num-2, memo)
          return memo[num]
      
      uInput = int(input("Fibonacci: "))
      memo = [0,1] + [None]*uInput
      fibNum = fib(uInput, memo)
      
      print(fibNum)
      

      更新:

      原代码还有一个错误:memo是必填参数,一般默认不能使用。

      【讨论】:

        【解决方案4】:

        我相信你的问题是一个很好的论据,为什么 memoization 应该作为 decorator 而不是与函数本身交织在一起:

        from functools import lru_cache
        
        @lru_cache()
        def fibonacci(number):
            if number == 0:
                return 0
        
            if number == 1 or number == 2:
                return 1
        
            return fibonacci(number - 1) + fibonacci(number - 2)
        
        uInput = int(input("Fibonacci: "))
        
        fibNum = fibonacci(uInput)
        
        print(fibNum)
        

        否则,您会尝试同时调试两个不同的程序。尝试上面的输入 100 和 @lru_cache() 装饰器。

        当然,这仍然受限于 Python 调用堆栈的深度相对较小的输入。有些迭代(甚至更好的递归)算法可以做得更好。

        【讨论】:

          猜你喜欢
          • 2014-10-31
          • 1970-01-01
          • 2022-11-29
          • 2012-11-26
          • 1970-01-01
          • 2018-10-10
          • 2010-09-20
          • 1970-01-01
          • 2012-04-01
          相关资源
          最近更新 更多