【问题标题】:How to rewrite a program to use recursion?如何重写程序以使用递归?
【发布时间】:2020-03-01 11:29:27
【问题描述】:

刚开始处理递归 - 我还不了解其中的所有内容。我认为我不使用基本条件,但我不知道如何编写它。该程序本身可以运行并执行我需要的所有内容,但没有递归。

该程序的想法是有一个列表,其中需要对列表中的每个第 x 个数字求和 - 这里的 x 作为一个步骤。如果 x = 0,则总和自动为零。如果 x 超出范围,那么总和也是 0

def sum_elements(nums, x) -> int::
    if x not in range(-len(nums), len(nums)) or x == 0:
        return 0
    if x > 0:
        nums = nums[x - 1::x]
        return sum(nums)
    return sum_elements(nums[::-1], -x)

if __name__ == '__main__':
    print(sum_elements([], 0))  # x = 0 -> 0
    print(sum_elements([1, 5, 2, 5, 9, 5], 3))  # 2 + 5 = 7
    print(sum_elements([5, 6, 10, 20], -2))  # 10 + 5 = 15
    print(sum_elements([5, 6, 10, 20], -20))  # x = -20 -> 0

【问题讨论】:

  • 没有递归是什么意思?这就是return sum_elements(...) 正在做的事情。不过,我不确定我是否理解为什么该步骤可能是负面的
  • 你可以试试return nums[position] + sum_elements(nums, x, position=i+x),而不是return sum(nums),但这需要添加一个参数
  • 有趣的问题。我喜欢它的复杂性。你的问题到底是什么?
  • @cricket_007,我相信 OP 所说的递归是用递归调用替换 sum,该调用实际上是对 xth 元素而不是所有列表求和
  • @cricket_007 我也是这么想的,但是在我的大学里有一个自动测试器,它说我的代码中没有递归,所以我很困惑

标签: python list recursion


【解决方案1】:

递归是当一个函数调用自己并且有一些(非正式的)规则在编写这些规则时总是很好地记住:

1。基本情况。

每个递归函数都必须有一个基本情况,它基本上充当recursive call 中堆栈的末尾。

2。每个递归函数都遵守non-base(s)base case

换句话说,您的代码必须以函数调用自身或终止递归调用的方式编写。您可以通过执行if else 语句来做到这一点,或者只编写if 语句来捕获基本情况。

3。函数的输入要记住前一个函数的状态。

在数学中,您可能还记得调用自己的函数(为了解释而转换了语法):

f(x)_(n=0) = f(x)_(n=1) + 10

变成:

f(x)_(n=1) = ( f(x)_(n=2) + 10 ) + 10

等等。本质上,您正在使用代码编写此代码并设置一个基本情况,可能会说(对于上面的示例,即)“当n 为 10 时停止”。如果是这种情况,您应该注意到当我们深入到该函数以及f(x)_(n=10) 出现时的级联效应(并假设返回0 + 10)我们将如何获得f(x)_(n=0) = 0 + 10 + 10 + 10 + ... 的最终形式。

所以对于这个函数,你有两个输入,numsx。这些输入是我们将在递归堆栈中进行修改的内容。


1。编写我们的基本案例。

编写基本情况通常是编写递归函数最简单的部分。我们知道,对于您的问题,必须抓住以下情况:

  • 如果x不在nums的长度范围内,那么我们必须返回0
  • 如果len(nums)0,那么我们应该返回0

让我们开始吧:

def sum_elements(nums, x) -> int:
    if len(nums) == 0 or not x in range(-len(nums), len(nums)):
        return 0

但请注意,range(len([1, 2])) 将返回 range(0, 2),但 list(range(0, 2)) 将返回 [0, 1]。因此,我们必须确保在我们的len(nums) 中添加一个1,这样我们才能真正看到x 是否在适当的范围内:

def sum_elements(nums, x) -> int:
    if len(nums) == 0 or not x in range(-len(nums), len(nums) + 1):
        return 0

请注意,range(-len(nums), len(nums) + 1)nums = [1, 2, 3] 等于range(-3, 4),但list(range(-3, 4)) 等于[-3, -2, -1, 0, 1, 2, 3]。因此,我们不需要-len(nums) + 1-len(nums) - 1

一旦我们弄清楚了基本情况,我们就可以开始处理我们的实际功能了。至此,我们已经完成了 #1#2 的一部分,但现在我们必须编写 non-base(s) 案例。

2。识别我们的other-case(s)

正如 #2 中所写,我们的函数输入是随着函数堆栈向下移动而动态变化的。因此,我们需要考虑如何修改nums 和/或x 以适应我们的目的。但是,您应该首先查看的是,如果我们在向下堆栈时仅更改其中一个变量会发生什么。

  1. 保持nums 不变,修改x:我们知道我们的基本情况确保x 在正方向和负方向上都保持在nums 的长度约束内,这很好。但是,每次函数由原始 xx_0 运行时,我们都必须增加x。如果我们创建函数并在每次调用时都说x + x,我们不会将原始x 添加到自身,而是将更新的x 添加到自身。这是个问题。举个例子:
def sum_elements(nums, x) -> int:
    print(nums, x)

    # Base case.
    if len(nums) == 0 or not x in range(-len(nums), len(nums) + 1):
        return 0

    # Other case. We must differentiate between positive x, and negative x.
    if x > 0:
        # Since x is an index that starts at 1, not 0, we must do x-1.
        number = nums[x - 1]
    else:
        # For negative values of x this does not apply. [1, 2][-2] = 1
        number = nums[x]

    return number + sum_elements(nums, x + x)

注意我们是如何得到的:

#    [NUMS]        x
[1, 2, 3, 4, 5, 6] 2
[1, 2, 3, 4, 5, 6] 4
[1, 2, 3, 4, 5, 6] 8
# OUTPUT
6

以及第三次调用中的x 值是8。这不是bueno。你练习递归的次数越多,这个概念就会变得越直观,因为你会注意到改变某个输入可能不是最好的。你应该想:“当函数继续向下堆栈时,这个值会是什么?”

  1. 保持x 不变,修改nums:如果我们这样做,我们应该确定x 的值不会有问题。那么问题就变成了我们将如何修改nums 列表并使用x 来获得优势。我们所知道的是,x 在技术上可以用作索引,如上所示。因此,如果我们不修改索引,而是修改该索引所在的列表,该怎么办?举个例子:
nums = [1, 2, 3, 4]
x = 2

print(nums)        # > [1, 2, 3, 4]
print(nums[x - 1]) # > 2
nums = nums[x:]    # > [3, 4]
print(nums[x - 1]) # > 4

所以看起来我们可以修改列表并保持一个常量x 来检索我们想要的信息。惊人的!在这种情况下,#2是要走的路。

3。写我们的other-case(s)

所以现在我们将尝试编写一个保持x 不变但修改nums 的函数。从上面的代码中我们有了一个大致的思路,从前面的点我们知道我们将不得不以不同的方式处理-xx。因此,让我们写点东西:

def sum_elements2(nums, x) -> int:
    # Base case.
    if len(nums) == 0 or not x in range(-len(nums), len(nums) + 1):
        return 0

    # Other case.
    if x >= 0:
        number = nums[x - 1]
        nums = nums[x:]
    else:
        number = nums[x]
        # Not sure what goes here. 

    return number + sum_elements(nums, x)

如果我们测试上面的函数,它似乎适用于任何正值x,但不适用于负值x。然而,这是有道理的,无论我们对积极的一面做什么,我们都必须对消极的一面做相反的事情。如果我们尝试使用nums = nums[:x],我们很快就会意识到它有效。我们的最终函数变为:

def sum_elements(nums, x) -> int:
    # Base case.
    if len(nums) == 0 or not x in range(-len(nums), len(nums) + 1):
        return 0

    # Other case.
    if x >= 0:
        number = nums[x - 1]
        nums = nums[x:]
    else:
        number = nums[x]
        nums = nums[:x]

    return number + sum_elements(nums, x)

运行示例

如果我们使用上述函数运行示例,我们会得到:

print(sum_elements([1, 2, 3, 4, 5, 6], 2))  # > 2 + 4 + 6 = 12
print(sum_elements([], 0))  # > 0
print(sum_elements([1, 5, 2, 5, 9, 5], 3))  # > 7
print(sum_elements([5, 6, 10, 20], -2))  # > 15
print(sum_elements([5, 6, 10, 20], -20))  # > 0

【讨论】:

  • 您能否为不同的 x 值添加输入示例和代码输出示例?我已经尝试过代码,但我不确定它会产生所需的内容。谢谢你。例如sum_elements([1, 2, 3, 4, 5, 6], 2) 产生5
  • 添加到底部——但它遵循 OP 的预期输出。我在帖子底部指出:“我遵循了您的预期输出,应该提到它们在索引方面有点不稳定 - 如果这不是正确的功能(预期输出的索引不佳),它将当然看起来和它很相似。”
  • @Pynchia 对帖子进行了编辑。正如我所提到的,他的预期输出并没有包含很多背景——然而,我应该自己做尽职调查,并测试更明显的东西。感谢您指出您的示例。
【解决方案2】:

也许这种方法可以帮助你理解。

它从第一个元素开始,其余的每个 x 个元素相加。

这是我的假设,因为您没有提供输入及其所需的输出作为示例。

如果您需要从xth 元素开始,代码可以很容易地修改,我留给您自己尝试。

def sum_elements(nums, x) -> int:
    if x>0 and x<=len(nums):
        return nums[0] + sum_elements(nums[x:], x)
    return 0

lst = [1, 2, 3, 4, 5, 6]
print(sum_elements(lst, 2))
print(sum_elements(lst, 3))
print(sum_elements(lst, 0))

生产

9
5
0

注意:它只是演示了递归,但由于多种原因它并不是最佳的。

它还会丢弃 x 的负值

【讨论】:

    猜你喜欢
    • 2011-07-16
    • 2023-03-13
    • 2011-10-23
    • 2017-12-01
    • 1970-01-01
    • 2017-02-15
    • 2016-05-01
    • 1970-01-01
    • 2012-05-31
    相关资源
    最近更新 更多