公认的递归(Recursion)的标准定义是非常难理解的:若一个对象部分地包含它自己,或用它自己给自己定义,则称这个对象是递归的;若一个过程直接地或间接地调用自己,则称这个过程是递归的过程。
递归一词很少有过专业的定义,因此本文不在于去解释上一段文字的意义。虽然概念抽象,但递归其本身是不难理解的。通过本文的介绍,读者不一定能深入了解递归,只要能通过具体的例子模模糊糊地知道一些递归的思想和用途就可以了。
究竟什么是递归呢?其实,递归就是大鱼吃小鱼,就是一条蛇咬住自己的尾巴。递归是指一样东西自己包含了自己。对于这一点,拿“谢尔品斯基地毯”(Sierpinski Gasket)来说明是最恰当不过的了。
曲线在几何学中的概念很好理解,就是只有长度而没有宽度的线条。数学中有各种各样的曲线,如圆、直线、抛物线、双曲线、正弦曲线等。它们都可以用一定的方法画出来。例如,圆可以用圆规画出来,正弦曲线也可以用机器边在纸带上往复记号边拉纸带的方法画出来。事实上却没那么麻烦,画曲线有一个最常用的“万能方法”——似乎所有的曲线都可以用“描点法”画出,因为曲线没有宽度嘛,一个一个的点连起来,随便多奇怪的曲线都应该能画出。但随着数学的发展,这一点遭到了置疑。波兰数学家谢尔品斯基就想出了一个的确是一种曲线但永远无法画出的图形。他构造这种曲线的方法就运用了递归。
随便找了一个正方形,把它分成3×3规格的相等的9个小正方形,然后把正中间的那一个挖掉。现在就只剩周围的八个小正方形了。接着重复这个过程,把8个小正方形的每一个都分成更小的9份,并挖掉它中间的那个。现在得到的就有8×8=64个正方形了。把这64个正方形继续这样划分,并且无限制的继续下去。这就是递归的思想,自己包含了自己,而后面的自己又包含了规模更小的自己。这样递归下去是没完的,因此最终得到的会是没有宽度的线条。这符合曲线的定义,但显然它是没办法画出来的。
在现实生活中,递归的现象也是可以见到的。如果一台电视机的屏幕正显示着摄象机拍到的东西,那么把摄象机正对着这台电视机拍摄就会形成一个简单的递归。电视机显示着摄象机拍到的内容,而摄象机又对着电视机,这也就是说,摄象机将会拍摄到自己所拍到的东西。于是,递归形成了——在电视机上会显示出一层一层电视机的轮廓,即电视机里有电视机,层层循环下去永无止境。类似的例子也有一些,比如那个永远也讲不完的古老的故事,和Linda的第二张专辑的封面。
递归通常是可以无限循环下去的。因此有这样一个笑话。作为一个狂热的电脑游戏迷,如果有一天你从一个完全陌生的地方醒来,你如何判断这是虚拟空间还是在现实中?答案是,找两面镜子来,互相对着放。如果出现周围的物体运动变慢等不正常的情况,说明你是在虚拟空间中。大自然是神奇的,它能处理两面镜子相对放置时镜子里应该显示的内容;但电脑就模拟不出来,如果电脑真遇到这种情况,指定会把CPU累死。
但是,一旦给一个递归过程加上一个限制条件,让它递归到某一步时就停下来不要继续循环的话,递归将会派上大用场。
我举一个最简单的例子。偶数就是能被2整除的数。我也可以用递归的方法定义偶数:一个偶数加上2还是偶数。这句话似乎足以说明了全部的数字,其实不然。因为如果没有任何限制,那么这个递归过程将是永无止境的,最终不会得到任何具体的答案。我们可以加上一个条件“0是偶数”。这样,情况就变了。比如,我们要看6是否为偶数,根据“一个偶数加上2还是偶数”,我们只需要看4是不是偶数。如果4是偶数,那么4+2也是偶数。而看4是否为偶数,又要看2是否为偶数,要看2是否为偶数,又要看0是否为偶数。本来这个递归应该是像这样无限地做下去的,但我们有了一个限制条件:我们已经知道了0是偶数。于是,2就是偶数了,4和6都是偶数了。同样的,我们就可以判断一切数字的奇偶性了。这就是利用递归来进行数学上的定义。
这种定义方式有什么好处呢?一个简单的例子——
很多人不明白为什么0的阶乘要规定成1,其实这用阶乘的递归定义一解释就清楚了。
阶乘可以这样递归地定义:
1)n的阶乘等于n-1的阶乘乘以n;
2)1的阶乘是1;
这样,所有自然数的阶乘就可以通过上面的两句话表示了。2的阶乘就是1×2;3的阶乘就是2的阶乘乘3,即1×2×3……不仅如此,我们还可以知道0的阶乘是多少:1的阶乘应该等于0的阶乘乘以1,显然0的阶乘必须是1才行。类似的,我们还能知道,负整数的阶乘没有意义。
接下来,我将用两个经典的用递归的思想解决问题的例子来结束这篇文章。
法国数学家艾得渥·卢卡斯(Edouard Lucas)于1883年在一份杂志上介绍了一个引人入胜的数学谜题——汉诺塔(Tower of Hanoi),并称这与古印度的一个传说有关。显然传说的具体内容已经不在本文论述的范围内了,但我想简单的介绍一下。
相传印度有座大寺庙,它曾被认为是宇宙的中心。神庙中放置了一块上面插有三根长木钉的木板,在其中的一根木钉上,由上至下放了64片直径由小至大的圆环形金属片。古印度教的天神指示他的僧侣们将64片金属片全部移至另一根木钉上。移动规则只有两个:
1.在每次的移动中,只能移动一片金属片;
2.过程中任意时刻必须保证金属片小的在上,大的在下。
直到有那么一天,僧侣们能将64片金属片按规则从指定的木钉上全部移至另一根木钉上,那么,世界末日即随之来到,世间的一切终将被毁灭,万物都将至极乐世界。
这个传说经常被认为是卢卡斯教授为推广这个数学谜题而捏造的,但不管怎么说,卢卡斯是成功了。这玩意儿变成了家喻户晓的益智游戏之一,后来又成为了学习递归的必修课程。
对汉诺塔问题的研究焦点集中在如何以最少的步骤完成全部金属片的转移这一问题上。解决这个问题的方法运用了递归的思想。
我们可以这样想。64片金属片太多了,我们似乎能简化一下。假如我们已经知道了如何移动63片,我们就可以把这63片看成一个整体。那么这64片的移动过程就出来了:第一步,移动前63片到另一根木钉上;第二步,移动第64片到第三根木钉上;第三步,把那63片移回第64片上面。看到了吗?问题已经解决了,因为这形成了递归。我们可以继续对移动63片的方法进行研究:把前62片移开,移动第63片,移回前62片。继续研究62片金属的移动方法……这样下去,一直推到如何移动2片金属。而移动2片金属的方法是非常简单的,已经不需要继续讨论了,于是,全部问题到此解决。
发现递归思想的实质了吗?这让我想起了一个笑话。笑话的主人公是一个反应迟钝,只具有数学思维的数学家;为了使这个笑话更形象,我们就把这个人暂且定为童明国(注:我们数学老师的名字)。
童明国去做消防队员。队长问:“如果你这里起火了,你怎么办?”童明国答:“用消火栓。”
“那么如果这里没有起火呢?”
“很简单,先把这里点燃,然后这个问题就转化为了一个我已经解决的问题了。”
我要举的下一个例子与这个有异曲同工之处。
小学奥赛接触过一个叫作“报30”的游戏,就是从1开始,两人轮流报数,每个人都只能报接下来的一个数或两个数。比如,第一个人可以报1,也可以报1、 2;如果第一个人报1、2,第二个人就可以报3和3、4;然后第一个人又报;这样报下去,最先报到30的人获胜。
这个游戏非常没意思,因为它有必胜策略。
最先报到30的人获胜,很显然,先报到27的人一定可以胜;那么,先报到24的人就一定能胜了……递归下去,21,18,最终得到的结论就是,先报到3的人一定必胜。也就是说,后报者必胜。不管先报者报多少,后报者始终报到3的倍数,这样定能获胜。
这个游戏有很多变种,但换汤不换药,万变不离其宗。比如,把规则改成“最先报到30的人就输”。这样,先报到29的人就赢了,然后同样递归,26,23,20……
前几天在网上看到了这个游戏的一个较难的变种。
有10枚硬币,每人轮流取硬币,可以拿一枚、两枚或四枚;取到最后一枚硬币者胜。
这样还有必胜策略吗?答案是肯定的,而且同样可以运用递归的思想来解决。
如果硬币的总数只有一枚,则先取者赢;
如果硬币的总数是两枚,则先取者赢;
如果硬币的总数是三枚,则先取者输;
如果硬币的总数是四枚,则先取者赢;
如果硬币的总数是五枚,则先取者赢(取两枚,对方面临三枚的情形,必输);
如果硬币的总数是六枚,则先取者输(不管取多少,对方面临的情形都是必胜的情形);
如果硬币的总数是七枚,则先取者赢(取一枚,对方面临六枚的情形,必输);
如果硬币的总数是八枚,则先取者赢(取两枚,对方面临六枚的情形,必输);
如果硬币的总数是九枚,则先取者输(不管取多少,对方面临的情形都是必胜的情形);
如果硬币的总数是十枚,则先取者赢(取一枚,对方面临九枚的情形,必输)。
递归趣文
时间: 2024-10-17 05:37:23
递归趣文的相关文章
趣文:程序员/开发人员的真实生活
本文借用了多张动态图,以诙谐的方式,展示了程序员/开发人员的真实生活.文章原始版本是西班牙语,由Alex Soto 翻成了英文版,再由伯乐在线翻成了中文版. (更多 IT 相关趣文,请查看伯乐在线博客的"趣文漫画"分类.) 往工作环境上传东西的时候: 没保存,就关了 IDE 的时候: 凌晨三点调代码的时候: 正则表达式返回了了预期结果的时候: 当老板告诉我,我那一直负责的模块失效了的时候: 刚修复了Bug,我给老板演示的时候: 上传的代码,没有测试,居然也能如预期工作的时候: 市场部同
递归XML文档
1 <?php 2 //递归XML文档 3 function getChilds($node){ 4 echo "<ul>"; 5 if($node->nodeType==3){ 6 echo "<li>".$node->nodeValue."</li>"; //取出节点的值 7 } 8 else 9 { 10 echo "<li>".$node->nod
递归回文
运用了布尔类型进行判断,递归调用了isHuiWen函数. package ketangceshia; import java.util.Scanner; public class HuiWendg { public static void main( String args[] ) { Scanner scan=new Scanner(System.in); String a=scan.next(); int n=a.length();
趣文:我是一个线程
来自:码农翻身(微信号:coderising) 作者:IBM 刘欣 我是一个线程,我一出生就被编了个号: 0×3704,然后被领到一个昏暗的屋子里, 这里我发现了很多和我一模一样的同伴. 我身边的同伴0×6900 待的时间比较长, 他带着沧桑的口气对我说: “我们线程的宿命就是处理包裹.把包裹处理完以后还得马上回到这里,否则可能永远回不来了.” 我一脸懵懂,包裹,什么包裹? “不要着急,马上你就会明白了, 我们这里是不养闲人的.” 果然,没多久,屋子的门开了, 一个面貌凶恶的家伙吼道: “0×3
(趣文)我是一个线程
http://blog.csdn.net/liaodehong/article/details/51206607 作者:IBM 刘欣 我是一个线程,我一出生就被编了个号: 0×3704,然后被领到一个昏暗的屋子里, 这里我发现了很多和我一模一样的同伴. 我身边的同伴0×6900 待的时间比较长, 他带着沧桑的口气对我说: "我们线程的宿命就是处理包裹.把包裹处理完以后还得马上回到这里,否则可能永远回不来了." 我一脸懵懂,包裹,什么包裹? "不要着急,马上你就会明白了, 我们
趣文:如果编程语言是车
C语言是全能手,小巧,强大,所向披靡,可靠,任何事情都能对付. C++是新的C,双倍的能力,双倍的尺寸,适应险恶的环境,但是你如果没练好就去驾驶,很可能会撞车. C#是加了很多安全特性的C++,所以屌丝也能驾驭.它看起来傻傻的,但只要附近有加油站.车行和良好的条件,它就有C++一样的动力.有个著名的大块头肌肉男老是到处吹捧它.(译者注:应该说的是微软吧) Java是另一个升级版的C.你开着它跑没问题,但是有点慢,有点笨,尾气排得到处都是,而且开着它别人会当你是乡巴佬.(译者注:请javaer们别
趣文假如编程语言在一起聚餐
appstore%E6%8E%92%E5%90%8D%E4%B8%80%E5%9C%BA%E7%8C%AB%E6%8A%93%E8%80%81%E9%BC%A0%E7%9A%84%E6%B8%B8%E6%88%8F http://www.zcool.com.cn/collection/ZMTcwNzc1MTY http://www.zcool.com.cn/collection/ZMTY5Nzg3Njg/ http://www.zcool.com.cn/collection/ZMTY4NTkzO
趣文:你对程序员适不适合做老公的看法是?
内容来自网络整理. 首先我们看原文: 原文来自csdn论坛某贴: 找了个程序员做老公,所以认识了很多程序员.以前就没对程序员有过不好的看法,现在则更不会.发现csdn上老有悲观的论调,说程序员找不到老婆.今天我就以一个程序员妻子的身份,来写写找程序员做老公的好处.希望能给各位mm有借鉴,找程序gg做男朋友. 一.找程序员不用担心外遇. 程序员是对着电脑工作,周围同事大部分是男生.他的生活中基本接触不到mm,所以不会有办公室恋情的发生,也就不会有外遇问题发生.而且面对的诱惑少,不 像销售啊等职
【热文】 为什么程序员痴迷于猫?
校对:伯乐在线 - 黄利民 链接: 1. Why are programmers obsessed with cats? 2. Michael Disibio's answer 3. Bruno Skvorc's answer 4. 趣文:为什么有些程序员特别喜欢猫? Why are programmers obsessed with cats(为什么程序员痴迷于猫) I have seen it in many pics of programmers, blog posts etc. 我很好奇