如何将此递归更改为尾递归?

时间:2017-11-04 19:01:59

标签: python recursion tail-recursion

这里是我试图将其改为尾递归的递归代码

def stairClimb(n):
    if n <= 3:
        WaysToClimb = [1, 2, 4]
        return WaysToClimb[n - 1]
    else:
        return stairClimb(n - 1) + stairClimb(n - 2) + stairClimb(n - 3)

2 个答案:

答案 0 :(得分:2)

当一个过程可以在每个过程应用程序中重复多次,为了实现尾调用,我们必须以某种方式序列多个递归调用

使用一种名为continuation-passing style的技术,我们可以在我们的函数中添加第二个参数,告诉我们的函数我们计算的下一步应该是什么

简单的CPS转换如下所示

def my_func (x):
  return x

def my_cps_func (x, k)
  # k represents the "next step"
  return k (x)

Python具有简洁的功能,所以我们可以编写我们的函数来支持直接样式延续传递样式。我们通过指定默认函数作为延续来实现这一点 - 我们将在下面更多地使用此技术,因此请务必先在此处理解

               # by default, pass x thru untouched
               # this is known as the "identity" function
def add (x, y, k = lambda x: x):
  return k (x + y)

# direct style
print (add (2, 3)) # 5

# continuation-passing style
add (2, 3, print) # 5

# direct and CPS mixed! invent your own freedom
print (add (2, 3, lambda sum: add (sum, sum))) # 10

你的stair_climb就像一个3-fibonacci程序(有时称为“tribonacci”)只有你的使用一个独特的(1,2,4)种子而不是更传统的(0,0,1)种子 - 我们将 tribonacci 转换为CPS然后我们将在此之后查看您的程序

def tribonacci (n, k = lambda x: x):
  if n == 0:
    return k (0)
  elif n == 1:
    return k (0)
  elif n == 2:
    return k (1)
  else:
    return tribonacci (n - 1, lambda a:
      tribonacci (n - 2, lambda b:
        tribonacci (n - 3, lambda c:
          k (a + b + c))))

for x in range (1,10):
  print (tribonacci (x))

# 0
# 1
# 1
# 2
# 4
# 7
# 13
# 24
# 44

但该函数的time complexity O(3 n ) - 因为lambdas可以抽象任意数量的值,这可以通过使用大量优化一个多参数lambda - 这里我们在 O(n)中计算相同的答案,其中lambda a, b, c: ...代表我们的“种子”

                   # by default, return the first value of the seed
def tribonacci (n, k = lambda a, b, c: a):
  if n == 0:
           # base seed values
    return k (0, 0, 1)
  else:
                              # the next seed (this is our "next step")
    return tribonacci (n - 1, lambda a, b, c:
      # new seed
      #  b is the "next a"
      #     c is the "next b"
      #        the sum of each is the "next c"
      k (b, c, a + b + c))

for x in range (1,10):
  print (tribonacci (x))

# 0
# 1
# 1
# 2
# 4
# 7
# 13
# 24
# 44

我们所要做的就是将k (0, 0, 1)种子更改为k (1, 2, 4)

def stair_climb (n, k = lambda a, b, c: a):
  if n == 0:
    return k (1, 2, 4)
  else:
    return stair_climb (n - 1, lambda a, b, c:
      k (b, c, a + b + c))

for x in range (1,10):
  print (stair_climb (x))

# 2
# 4
# 7
# 13
# 24
# 44
# 81
# 149
# 274

是的,如果你看一下每一行,每个程序应用都处于尾部位置

def stair_climb (n, k = lambda a, b, c: a):
  if n == 0:
           # tail
    return k (1, 2, 4)
  else:
           # tail
    return stair_climb (n - 1, lambda a, b, c:
      # tail
      k (b, c, a + b + c))

但是你有一个更大的问题 - Python没有尾调用消除

print (stair_climb (1000))
# RecursionError: maximum recursion depth exceeded in comparison

不用担心,一旦你使用了延续传递方式,那就是all sorts of ways around that

答案 1 :(得分:0)

使用累加器:

def stairClimb(n, acc, acc1, acc2):

if n == 3:
    return acc2
else:
    return stairClimb(n-1, acc1, acc2, acc+acc1+acc2)

并以初始号码呼叫它:

stairClimb(n, 1, 2, 4)