Loops 循环和递归函数之间的逻辑区别是什么?

Loops 循环和递归函数之间的逻辑区别是什么?,loops,recursion,Loops,Recursion,我看到一个视频,它正在讨论如何使用for循环编写大多数递归函数,但当我想到它时,我看不出两者之间的逻辑区别。我在这里找到了这个主题,但它只关注实际差异,就像web上的许多其他类似主题一样,那么循环和递归的处理方式在逻辑上有什么区别呢?最重要的一点是——递归更通用,但实际上通常比循环效率低 如果您愿意,循环原则上总是可以实现为递归。实际上,堆栈资源的限制严重限制了您可以解决的问题的大小。我可以并且已经构建了循环,可以迭代10亿次,除非我确信编译器能够并且会将递归转换为循环,否则我永远不会尝试递归。

我看到一个视频,它正在讨论如何使用for循环编写大多数递归函数,但当我想到它时,我看不出两者之间的逻辑区别。我在这里找到了这个主题,但它只关注实际差异,就像web上的许多其他类似主题一样,那么循环和递归的处理方式在逻辑上有什么区别呢?

最重要的一点是——递归更通用,但实际上通常比循环效率低

如果您愿意,循环原则上总是可以实现为递归。实际上,堆栈资源的限制严重限制了您可以解决的问题的大小。我可以并且已经构建了循环,可以迭代10亿次,除非我确信编译器能够并且会将递归转换为循环,否则我永远不会尝试递归。由于堆栈的限制和效率,人们经常试图找到递归的循环等价物


尾部递归始终可以转换为循环。但是,有些递归无法转换。作为一个例子,我使用统计设计的实验。有时,大型设计是通过“交叉”几个较小的子设计来构建的。交叉是将第二个设计的每一行连接到第一个设计的每一行。对于两个子设计,所有这些都需要简单的嵌套循环,但是对于三个或更多的设计,您需要增加嵌套级别,为每个附加的子设计添加一个嵌套级别。因此,虽然原则上这是嵌套循环,但实际上嵌套的数量是可变的。如果您试图用循环实现它,那么每次处理不同数量的要交叉的子设计时,您都必须修改程序以添加/减去嵌套循环,因此您无法编写基于循环的不变版本。这可以很容易地通过递归实现。在这种情况下,我很乐意牺牲一些效率,因为我在6年前编写并调试了代码,从那以后就再也不用修改了,尽管从那时起创建了许多复杂度不同的交叉设计。

但要想解决这一问题,一种方法是递归或迭代的选择取决于您对要解决的问题的看法。某些“思维方式”更自然地导致递归解决方案,而其他思维方式则导致更多的迭代解决方案。对于任何问题,原则上你都可以用一种方法来思考,给你一个递归的解决方案,或者用一种方法来给你一个迭代的解决方案。(有时迭代解决方案只会模拟递归堆栈,但那里没有实际的递归。)

这里有一个例子。您有一个整数数组(正数或负数),并且希望找到最大段和。段是数组中连续的一部分。所以在数组[3,-4,2,1,-2,4]中,最大段和是5,你可以从段[2,1,-2,4]中得到;它的总数是5

好的,那么我们该如何解决这个问题呢?你可能会做的一件事是这样的推理:“如果我知道左半部分的最大段和,右半部分的最大段和,那么也许我可以把它们组合在一起,计算出整体的最大段和。”。这个想法需要你找到两个子阀的最大段和,这是原始问题的一个较小的例子。这就是递归,因此,将这个想法直接翻译成代码就是递归的

但是最大段和问题不是“递归”或“迭代”——它可以是两者兼而有之,这取决于您对解决方案的看法。我在上面给出了一个递归的思考过程。这是一个迭代过程:“好吧,如果我把从某个索引I开始到某个索引j结束的每个段中的元素相加,我就可以取其中的最大值来解决问题。”。直接尝试编写这种方法会产生三重嵌套循环(在赋值上会有一个坏标记,因为它效率极低!)

因此,同样的问题,取决于问题是如何概念化的,可以导致递归或迭代的解决方案。现在,我碰巧选择了一个问题,有很多方法可以解决它,并且有合理的递归和迭代解决方案。然而,有些问题只允许一种类型的解决方案,而这种解决方案可能最自然地使用递归或迭代来实现。例如,如果我要求您编写一个函数,该函数不断要求用户输入字母,直到他们输入y或n,您可能会开始思考:“不断重复提示并要求输入…”,在您知道之前,您已经有了一些迭代代码。也许您可以递归地思考:“如果用户输入y或n,我就完成了;否则,请用户输入y或n”。。。在这种情况下,您将生成一个递归算法。但是这里的递归并没有给你太多的帮助:它不必要地使用堆栈,并且不会使程序更快。(递归有时更容易证明正确性,在这种情况下,即使可以交替给出合理的迭代解,也可以递归地表示某些内容。)