计算递归调用中的路径

时间:2016-09-20 22:00:25

标签: python recursion counter

我正在解决的问题是Cracking the Coding Interview:

"一个孩子正在爬楼梯,有n个台阶,可以跳一步,两步, 或一次3个步骤。实现一种方法来计算有多少可能的方法 孩子可以跑上楼梯。"

来自C ++我知道计数器可以作为参考传递,但是在python中你不能。我也试图跟踪导致成功的步骤顺序。我正在编写这样的代码:

def __calculatePaths(currPathLength, paths, currSeries):
  if currPathLength == 0:
    print "successful series is", currSeries
    return 1
  elif currPathLength < 0: return 0

  for i in range(1, 4):
    newSeries = list(currSeries)  # make new series to track steps
    newSeries.append(i)
    paths += __calculatePaths(currPathLength - i, paths, newSeries)
  return paths

def calculatePaths(pathLength):
  paths = __calculatePaths(pathLength, 0, [])
  return paths

if __name__ == '__main__':
    calculatePaths(3)

此次通话的输出为:

successful series is [1, 1, 1]
successful series is [1, 2]
successful series is [2, 1]
successful series is [3]
6

我很困惑,因为我的程序获得了正确的路径序列,但路径数量错误。我应该如何增加我的路径?我知道如何在没有全局变量的情况下做到这一点,但我无法在不使用全局变量的情况下解决这个问题。谢谢!

3 个答案:

答案 0 :(得分:1)

最重要的是,要意识到你不必确定那些序列:你只需要计算它们。例如,从步骤N-1开始只有一种方法:跳1步。从N-2开始,有两种方法:一次跳两个步骤,或者从那里跳一步并完成。我们的完成方式&#34; list现在看起来像这样,向后工作:

way = [1, 2, ...]

现在,看看步骤N-3会发生什么。我们最终有3个选择:

  1. 跳1步,有2种方法完成
  2. 跳2步,有1种方法完成
  3. 跳3步并完成。
  4. 总共有2 + 1 + 1或4种方式完成。

    初始化我们的算法。现在为复发关系。初始列表如下所示:

    way = [1, 2, 4, ...]
    

    从现在开始,我们无法单跳到顶部。相反,我们必须依赖于我们上面的三个步骤。我们从步骤N-J中做出的选择是:

    1. 跳1步并以方式[J-1] 方式完成
    2. 跳跃2步并以方式[J-2] 完成
    3. 的方法
    4. 跳3步并以方式[J-3] 方式完成
    5. 因此,对于所有j&gt; = 3:

      way[j] = way[j-1] + way[j-2] + way[j-3]
      

      这为您提供 O(N)时间的解决方案。

答案 1 :(得分:0)

在函数__calculatePaths中,必须在for循环之前设置paths = 0。否则,它会将值添加到路径的全局实例中,这就是您得错答案的原因。

def __calculatePaths(currPathLength, paths, currSeries):
  if currPathLength == 0:
    print "successful series is", currSeries
    return 1
  elif currPathLength < 0: return 0
  paths = 0
  for i in range(1, 4):
    newSeries = list(currSeries)  # make new series to track steps
    newSeries.append(i)
    paths += __calculatePaths(currPathLength - i, paths, newSeries)
  return paths

def calculatePaths(pathLength):
  paths = __calculatePaths(pathLength, 0, [])
  return paths

if __name__ == '__main__':
    calculatePaths(3)

您可以以非常有效的方式获得多种方式。在O(N)中使用动态编程。甚至更有效地使用矩阵求幂O(logN)。

答案 2 :(得分:0)

这应该是使用您的解决方案进行计算的最有效方式:

from collections import deque


def calculate_paths(length):
    count = 0  # Global count

    def calcuate(remaining_length):
        # 0 means success
        # 1 means only 1 option is available (hop 1)
        if remaining_length < 2:
            nonlocal count  # Refer to outer count
            count += 1
            return

        # Calculates, removing the length already passed.
        # For 1...4 or remaining_length+1 if it's less than 4.
        # deque(, maxlen=0) is the fastest way of consuming an iterator
        # without also keeping it's data. This is the most efficient both
        # memory-wise and clock-wise
        deque((calcuate(remaining_length-i)
              for i in range(1, min(4, remaining_length+1))), maxlen=0)

    calcuate(length)
    return count

>>> calculate_paths(2)
2
>>> calculate_paths(3)
4
>>> calculate_paths(4)
7

正如您所看到的,没有必要保留路径,因为只剩下剩余的长度很重要。

@ Prune的答案有更好的算法。在这里实施:

def calculate_paths(length):
    results = deque((1, 2, 4), maxlen=3)
    if length <= 3:
        return results[length-1]

    for i in range(3, length):
        results.append(sum(results))

    return results.pop()

消除递归也会导致使用较少的帧,并且不会因最大递归而停止。