简体   繁体   English

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

[英]how to change this recursion to tail recursion?

here is the recursion code I am trying to change it to 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)

When a procedure can recur multiple times per procedure application, to achieve a tail call, we must somehow sequence the multiple recursive calls 当一个过程可以针对每个过程应用程序多次重复执行以实现尾部调用时,我们必须以某种方式对多个递归调用进行排序

Using a technique called continuation-passing style , we can add a second parameter to our function that tells our function what the next step of our computation should be 使用一种称为“ 连续传递样式”的技术,我们可以在函数中添加第二个参数,该参数告诉函数下一步应该进行什么计算

A simple CPS conversion looks like this 一个简单的CPS转换如下所示

def my_func (x):
  return x

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

Python has neat features tho, so we can write our function to support both direct style and continuation-passing style. Python具有简洁的功能,因此我们可以编写函数来支持直接样式连续传递样式。 We do this by assigning a default function as the continuation – we'll use this technique more below, so make sure you understand it here first 为此,我们将默认函数指定为延续函数-我们将在下面更多地使用此技术,因此请确保您首先了解此技术

               # 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

Your stair_climb is like a 3-fibonacci procedure (sometimes called " tribonacci ") only yours uses a unique (1,2,4) seed instead of a more traditional (0,0,1) seed – we'll show tribonacci converted to CPS then we'll look at your procedure after that 您的stair_climb就像是3斐波纳契过程(有时称为“ 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

But the time complexity of that function is O (3 n ) - since lambdas can abstract any number of values, this can be massively optimized by using a multi-parameter lambda – here we compute the same answer in O (n) where lambda a, b, c: ... represents our "seed" 但是该函数的时间复杂度O(3 n )-由于lambda可以抽象任意数量的值,因此可以使用多参数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

All we have to do is change the k (0, 0, 1) seed to k (1, 2, 4) 我们要做的就是将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

And yep, if you look at each line, every procedure application is in tail position 是的,如果您查看每一行,则每个过程应用程序都位于尾部位置

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))

But you have a bigger problem – Python doesn't have tail call elimination 但是您有一个更大的问题– Python没有尾叫消除功能

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

No worries, once you're using continuation-passing style, there's all sorts of ways around that 不用担心,一旦您使用了延续传递样式, 围绕它的方法有很多种

use accumulator: 使用累加器:

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

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

and call it with the initial numbers: 并以初始数字命名:

stairClimb(n, 1, 2, 4)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM