资源,以提高您的递归理解?[关闭]


13

我知道什么是递归(当一个patten在其内部重新出现时,通常是一个函数,在有条件的突破后会在其一行上调用自己...对吗?),如果我仔细研究它们,我就能理解递归函数。我的问题是,当我看到新的示例时,最初总是很困惑。如果我看到一个循环,或者一个映射,压缩,嵌套,多态调用等,我仅通过查看就知道发生了什么。当我看到递归代码时,我的思考过程通常是“这是wtf吗?” 其次是“哦,这是递归的”,其次是“如果他们说可以的话,我想它一定有用。”

那么,您在此方面有什么技巧/计划/资源来培养技能吗?递归是一个很奇怪的概念,因此我认为解决它的方法可能同样很奇怪而且很模糊。


28
要了解递归,您必须首先了解递归。
Andreas Johansson

1
苏斯博士的“戴帽子的猫回来了”,这可能并不完全有用,但是对猫的递归调用确实消除了那种讨厌的污渍。:-)它还具有快速阅读的好处!
DKnight 2011年

2
练习,练习,练习。
David Thornley


3
@Graham Borland:这是无限递归的一个例子。在大多数程序中,缺少基本情况通常会导致堆栈溢出或内存不足错误。对于网站用户而言,这可能只会造成混乱。;)
FrustratedWithFormsDesigner

Answers:


10

从简单的东西开始,然后用铅笔和纸把它找出来。认真地 树遍历算法是一个很好的起点,因为与常规迭代相比,使用递归更容易处理它们。它不一定是一个复杂的示例,但是很简单,您可以使用。

是的,这很奇怪,有时是违反直觉的,但是一旦点击,一旦您说出“尤里卡!” 您会想知道您以前是如何理解的!;)我建议使用树木,因为它们是(IMO)递归中最容易理解的结构,并且使用铅笔和纸可以轻松地工作。;)


1
+1这就是我摸索的方式。例如,如果您使用的是OO,请创建一些具有父子关系的类,然后尝试创建一个函数/方法来检查对象是否具有特定的祖先。
阿尔布

5

我强烈推荐Scheme,使用的是《 The Little Lisper》。完成后,您深入了解递归。几乎可以保证。


1
+1这本书确实为我做到了。但是它被重命名为“小计划者”
mike30

4

我绝对建议SICP。另外,您应该在此处查看作者的入门课程视频;他们令人难以置信的开放性。

另一条与编程没有严格关系的道路是阅读霍夫施塔特的哥德尔,埃舍尔,巴赫:永恒的金色辫子。一旦陷入低谷,递归将看起来像算术一样自然。另外,您将确信P = nP,并且您将要构建思维机器-但这是一个副作用,与收益相比,它是如此之小。


无论如何,GEB还是值得一读。即使他谈论的某些事情有些过时(在过去的40年中CS的基础研究取得了一些进展),基本的理解也没有。
Donal Fellows

2

从本质上讲,它只是要付诸实践...处理一般性问题(排序,搜索,数学问题等),看看如果多次应用单个函数,是否可以找到解决这些问题的方法。

例如,快速排序的操作方式是将列表中的元素移动到两半,然后将自身再次应用于这些两半。进行初始排序时,它并不担心在这一点上将两个半部分进行排序。而是将枢轴元素放在一边,并将所有小于该元素的元素放在一侧,而所有大于或等于该元素的元素放在另一侧。这在当时如何递归调用自身以对两个新的较小列表进行排序是否有意义?他们也是清单。只是更小。但是它们仍然需要进行排序。

递归背后的力量是分而治之的概念。反复将一个问题分解为本质上相同但规模较小的较小问题。如果您做得足够多,最终您将解决剩下的唯一问题,那么您就可以从循环中跳出,问题就可以解决了。 研究您提到的那些示例,直到您理解它们为止。这可能需要一段时间,但最终会变得更容易。然后尝试解决其他问题,并创建一个递归函数来解决它们!祝好运!

编辑:我还必须补充说,递归的关键元素是该函数能够停止的保证能力。这意味着原始问题的分解必须不断缩小,并最终需要有一个保证的停止点(新的子问题可以解决或已经解决的点)。


是的,我想我之前已经看过快速的排序说明,我可以从上面的提醒中设想它的工作原理。递归的表达/灵活性如何-大多数问题是否可以被强制为递归方法(即使不是最佳方法)?我见过人们回答网络上大多数人都在按程序解决的编码难题,好像他们可以在只想做鬼事的时候使用递归一样。我还读过一次,我认为某些语言依赖或递归来代替循环结构。您提到了保证的停止点。我觉得这些事情之一可能是关键。
Andrew M

您自己创建的一个好的简单开始问题是编写一个找到数字阶乘的递归程序。
肯尼思

任何循环结构都可以放入递归结构中。任何递归结构都可以或多或少地放入循环结构中。能够学习何时以及何时不使用递归需要花费时间和实践,因为您必须记住,在使用递归时,在硬件级别使用的资源方面存在很多开销。
肯尼思

例如,我可以看到创建一个执行快速排序的循环结构是可行的...但是,可以肯定的是,这将是一个皇家难题,并且取决于它的完成方式,最终可能会使用比递归函数更多的系统资源适用于大型阵列。
肯尼思

所以这是我的阶乘尝试。公平地讲,我之前已经看过这个内容,尽管我是从头开始写的,而不是从内存中写的,但它可能比以前要容易得多。在JS中进行了尝试,但是遇到了解析错误,但是可以在Python中使用 def factorial(number): """return factorial of number""" if number == 0: return 0 elif number == 1: return 1 else: return number * factorial(number - 1)
Andrew M

2

我个人认为,最好的选择是实践。

我学会了使用LOGO进行递归。您可以使用LISP。在这些语言中,递归是很自然的。否则,您可以将其比作数学套件和系列的研究,在该系列中,您可以根据之前的内容表达下一步的内容,即u(n + 1)= f(u(n)),或更复杂的系列,其中包含多个变量,并且多个依赖关系,例如u(n)= g(u(n-1),u(n-2),v(n),v(n-1));v(n)= h(u(n-1),u(n-2),v(n),v(n-1))...

因此,我的建议是,您会发现简单的(以其表达方式)标准递归“问题”,并尝试以您选择的语言来实现它们。实践将帮助您学习如何思考,阅读和表达这些“问题”。注意,这些问题中的一些通常可以通过迭代来表达,但是递归可能是解决它们的更优雅的方法。其中之一是阶乘数的计算。

我发现图形“问题”使它更容易看到。因此,通常检查一下科赫的薄片,斐波那契,龙曲线和分形。还要看看快速排序算法...

您需要崩溃一些程序(无限循环,临时使用无限资源),以及错误处理结束条件(以获得意想不到的结果),然后再全神贯注。即使您得到了它,您仍然会犯这些错误,而且这种错误的发生频率会降低。


2

计算机程序的结构和解释

这是本用于学习书,不仅用于递归,而且还用于各种大学的编程。这是一本基础书籍,可以产生更多的信息,您获得的经验越多,阅读的内容就越多。


0

正如我喜欢SICPGödel,Escher,Bach:永恒的金辫子一样,Touretzky的LISP:对符号计算的温和介绍在引入递归方面也做得很好。

基本概念是:首先,您必须知道递归函数何时完成,以便它可以返回结果。然后,您必须知道如何处理未完成的案件,并将其简化为可以重复使用的案件。对于传统阶乘(N)的示例,当N <= 1时完成,未完成的情况为N * factorial(N-1)。

对于一个更丑陋的例子,有阿克曼函数 A(m,n)。

A(0,n) = n+1.                                   This is the terminal case.
A(m,0) = A(m-1,1) if m > 0.                     This is a simple recursion.
A(m,n) = A(m-1, A(m, n-1)) if m > 0 and n > 0.  This one is ugly.

0

我建议使用OML或Haskell等ML风格的功能语言。我发现模式匹配语法确实帮助我理解了甚至相对复杂的递归函数,当然比Scheme ifcond语句要好得多。(我同时学习了Haskell和Scheme。)

这是一个简单的对比示例:

(define (fib n)
   (cond [(= n 0) 0]
         [(= n 1) 1]
         [else (+ (fib (- n 1)) (fib (- n 2)))]))

以及模式匹配:

fib 0 = 0
fib 1 = 1
fib n = fib (n - 1) + fib (n - 2)

这个示例并没有真正实现差异正义-我从未在函数的任何版本上遇到问题。只是为了说明这两个选项的样子。一旦使用了列表和树之类的功能获得了更为复杂的功能,两者之间的区别就会变得更加明显。

我特别推荐Haskell,因为它是一种简单的语言,语法非常好。使用诸如corecursion之类的更高级的想法,它也变得容易得多

fibs = 0 : 1 : zipWith (+) fibs (drop 1 fibs)
fib n = fibs !! n

(在与Haskell一起玩之前,您不会理解上面的代码,但是请放心,它基本上是神奇的:P。)当然,您可以对Scheme中的流执行相同的操作,但是在Haskell中它自然得多。


0

它已经绝版了,但是如果您能找到它,Richard Lorentz的“递归算法”几乎就是递归。它涵盖了递归的基础以及特定的递归算法。

这些示例使用的是Pascal语言,但范围不大,以至于语言的选择很麻烦。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.