“看来我们就有了一个求通项是n的线性关系的数列的前n项和的工具了?”小P一边吸着在回教研室的路上买的酸奶一边问。
“不仅仅是这样,根据前人的经验总结,求解递归问题最后往往会归结为一个求数列前n项和的问题。”老C喝着他的茶水,“我们现在只是进行一些知识储备而
已。而且有了现在的这个工具,我们还可以求一些更复杂的数列求和问题。”老C说着将白板擦干净,将已经掌握的公式写在最上面。
若
T0 = α
Tn = Tn-1 + β + γn
则
Tn = α + βn + γn(n + 1)/2
“现在要求一个稍微复杂一些的数列的前n项和,”老C说着在白板上写下问题,“提醒你可以用我们刚才得到的公式求解……这样解决问题的方法就限定在很小的区域了,省得你东想西想……这样求解问题就变得稍微容易一些了……因为我们的公式就是线索……”
求
的通项公式。
“呵呵,好好想想吧,答案就在我们已有的公式上。”说完老C跑到一旁一边喝茶一边窃笑。
“哦?”小P眯起眼睛,“让我想想,应当可以做出来的……”
“好的,你做出来了后就给我讲讲吧,我也一同学习学习。”老C说完就跑到自己的电脑上噼里啪啦的忙起来。
一个小时后……
“唉,老C,你帮我看看,我为甚做出来的结果有些问题,但是我又发现不了错误在什么地方……”小P跑过去拍拍老C。
“哦?是么?”老C扭过椅子,“你是怎么做的呢?”
“嗯,你看看我是这样推导的。”小P说着将他的推导过程抄到白板上。
又T0 = 02 = 0
所以原问题可以转化为求
T0 = 0
的通项
“看,这样就转化为一个求递推公式的通项问题。”小P说道,“但是我们手头上求递推公式通项的工具就只有一个,为了达到使用此工具求解问题的目的,经过我殚精竭虑的多方考虑,我决定……在等式两边求导……”
“咦?你的想法……很好很强大啊,真的是很具有想象力嘛……”老C赞叹。
“是啊是啊,到目前为止还比较顺利,但是问题就是不知道出在什么地方……”小P道,然后接着向下写。
令
所以原方程可以化简为
Sn = Sn-1 + 2n
又可得
S0 = 2 * 0 = 0
所以根据已知公式得到
Sn = n(n + 1)
又有
所以
Tn = n3/3 + n2/2
又因为
T0 = 02 = 0
所以
C = 0
所以
Tn = n3/3 + n2/2
“看,这个就是我的解答,但是在代入n = 1, 2, 3…进行检查的时候发现并不正确……我看了好几遍,都不知道问题在什么地方……”小P委屈的说道。
“唔……我来看看。”老C搬过椅子坐在白板前,仔细观看起推导过程来……“这里为什么是S0 = 2 * 0 = 0?”老C问道。
“是这样,”小P解释,“因为我认为可以将Sn = Sn-1 + 2n看为一个求和,令an = 2n,这样原来的式子就可以看成对数列an求前n项和。而S0 = a0 = 2*n = 2*0 = 0……”
“……你啊在这里犯了一个经典的错误……”老C说道,“为了纪念你犯的错误,我决定将这个错误命名为小P之由递推公式得到边界错误。”看到小P还是不明
白,老C接着说道,“现在的这个问题对于我这种智商比较低的人,是一时无法和你说清楚的,与我们一般讨论问题的思路相同,我们来看看一个更简单的例子。”
说罢老C在白板上写下一个求和问题。
“我们知道Tn的通项可以写为n(n+1)/2,把这个结论作为我们验证问题的工具。现在看看使用你的方法会造成什么结果。”说完老C继续在白板上比划。
令
令
an = 1
因为
所以
S0 = 0
Sn = n
则
得
Tn = n2/2 + C
因为T0 = 0,所以C = 0
所以
Tn = n2/2
“看,推导结果和我们已经知道的结果不一样!”老C说道,“让我们看看哪一步出的问题。”他在白板上画了一个箭头,指向一行推导。
“我们现在把Tn = n(n+1)/2代入这个公式,看看出了什么问题。”老C说道。
“看看,其实我们可以根据这个公式得到Sn = (2n+1)/2的,所以S0 = 1,”说完他又强调,“为什么你会得到S0 = 0这样错误的结论呢?因为你错误的认为可以用a0 来求出S0!但是当a0出现时,根据递归公式必然出现S-1,而S-1是
没有定义的!记住永远无法从递推项得到递推边界的值,就像无法从微分方程本身得到边界值一样!”老C说道,“为了纪念你这个错误并且让你在数学的发展史上
留下深刻的印记,我把这个错误称为小P之从递推公式得到边界错误!”看到小P有些囧,他笑道,“呵呵,开玩笑的。但是这样一个错误的确是很经典的,所以你
一定要记住,永远无法从递推本身看出递推结束的条件,这个条件需要从外部给出!”
“哦?的确是这样啊,因为我永远没有办法从一个微分方程中看出微分方程的边界值的……因为微分方程只会解出通解,特解需要特别的边界条件。好像递推方程也
一样……就像我无法仅用当前汽车的加速度就看出它在下一时刻的速度,因为初始时刻的速度信息必须由另外一个条件给出的……”小P回答,“现在我恍然大悟
啦,我不应当从递推项中推出边界,而应当根据原来的求和公式中求出边界的值。现在我知道我的错误在哪里了。”说完他继续在白板上写下求平方和通式的新推导
过程。
令
S0 =α
Sn = Sn-1 + 2n
根据递推到通项的结论,得
Sn = α + n2 + n
对方程两边积分,得
Tn = αn + n3/3 + n2/2+C
因为
T0 = 0,得C = 0
Tn = n2,所以求得上面方程一个特解为T1 = 1,n = 1,代入通项公式得
1 = 5/6 + α
所以
α = 1/6
所以原通项可以写为
Tn = n3/3 + n2/2 + 1/6n
“呵呵,这下就对了!”小P又代入几个n值验算了一下。
“这下你是否有些明白?”老C问道,“其实我们可以通过我们的结论得到很多的数列和的通项公式。”
“是啊,只要我们使用微积分,可以得到很多有趣的结论。”小P说道。
“嗯,的确,但是只使用微积分是远远不够的,”老C说道,“因为一开始的那个通项公式无法使用微积分的方法从我们得到的简单公式推导出来。现在我们来看
看,经过我们一番探索后,有什么好的方法可以去求解那个问题。”老C说完将白板擦干净,把需要求解的通项公式写在了正中间。
T0 = T0
anTn = bnTn-1 + cn
“怎么样,有什么想法没有?”老C问道。
“如果我可以有一个Rn = anTn,且Rn-1 = bnTn-1,那么我就可以将原来的递推公式变为一个对cn的求和公式……但是世界上哪有这么好的事情,恰好可以找到一个Rn啊……”小P说道。
“嗯,你的想法很好啊,没错,我们是不能次次都找到这样的Rn,但是我们可以通过某些方法构造出来一个。”老C说道,“最简单的,如果我们在公式两边同时乘上一个数列,比如sn,这样我们就可以得到新的递推数列。”
snanTn = snbnTn-1 + sncn
“看看,我们不用指望rp,而是通过自己的双手,我们可以使得snanTn = Rn,snbnTn-1 = Rn-1,加一加,乘一乘,都是常用的构造方法,你要在走投无路的时候考虑使用它们。”老C挠挠头,“在这里,我们在等式两边同乘的数列sn在圈内有一个学名,叫做summation factor,还是算作小有名气的……”
“哦,下来我就可以求解这个递推公式了!”忍受不了了老C的罗嗦,小P插嘴,然后在白板上演算起来。
令
Rn = snanTn,Rn-1 = snbnTn-1
则原式可以改写为
Rn = Rn-1 + sncn
则
Rn = R0 + s1c1 + s2c2 + s3c3 +…+sncn
所以
下面求sn
因为
Rn-1 = snbnTn-1
所以
Rn = sn+1bn+1Tn
所以
snanTn = sn+1bn+1Tn
即
sn+1bn+1 = snan
所以
“唔,这样就差不多啦。”小P说道,“因为an和bn是已知的,只要我选择的s0不为0,那么就可以求出sn啦,然后递推公式就转换为求数列sncn的前n项和。”
“是啊是啊,然后我们的问题就变为求数列前n项和的问题了。”老C说,“为了解决递推问题,我们发现需要解决数列求和问题。看看,事情发生了奇妙的转换。
所以我们以后还会继续讨论一些求数列和的问题,毕竟递推的思想是计算机科学中一个及其重要的思想,值得我们在这方面下下功夫。以后我们很多的做法都会依赖
于递推的这个基本的思想啦。”他站起来伸个懒腰,“不过仅仅依靠summation factor是不够的,还有一些问题是summation
factor无法解决的,小朋友吃苹果问题就是一个例子。”说完老C笑道,“未知的事物总是多于已知事物嘛。不过……如果你在参加面试或笔试的时候实际上
很有可能不会碰到数到7的小朋友被踢出的问题,一般都是数到2的小朋友被踢出。为什么?……因为面试或笔试的时候时间有限,无法允许你写一个长长的运用
linked
list解决问题所花费的时间,而数到2的小朋友被踢出,实际上可以用一个循环左移的算法来解决。这个就是在数学圈里比较有名的Josephus问
题……”看到小P已经有些反应不过来了,老C停止了长篇大论,“算了,今天就到这里吧。我们明天再运用递归的思想解决这个小朋友吃苹果的问题,顺便再讨论
一些对算法的效率进行评估的方法……这些都是基础,如果不了解这些就去盲目的学习C++语言、面向对象编程和类库什么的,对你有害无益……因为你的思想就
会局限在一个比较低的水平上。”
“是吗是吗?”小P不解,“会吗?”
“的确是这样。”老C回答,“我们学习的是编程,而不仅仅学习的是语言。我们希望通过对语言的学习提高的是编程的能力……这样你在以后的工作中,无论使用
什么语言,都会飞快的上手,同时分析问题和解决问题的能力也会有显得众不同……要深入进去,这就是为什么说teach yourself
programming in ten
years的原因……这10年中,你学习的如果仅仅是语言的话,那么等10年后,你会发现,自己原来没有10年的编程经验,有的只是10个1年编程经
验……”
“呵呵,你这么说还比较有趣啊,10个1年编程经验……哈哈”小P笑道。
“唉,现在这样的人不在少数啊。”老C感慨,“所以你要深练内功。根据我的观点,我们可以把自身的知识技能分为三类,一类是基本知识,一类是专业知识,一
类是基本素质。基本知识是我们思维的根本,如果没有这些知识,我们就会缺乏某些常识,对于编程的人员来说,这部分知识代表了内功,包括数学知识、算法知识
和数据结构方面的知识;专业知识代表我们的经验,在软件来说,可能包括一些面向对象的编程思想、某些结构化编程的方法、以及其他配置管理和项目生命期方面
的知识,也包括你所要从事的行业的知识,比如你要从事于自控行业,那么信号与系统、信号处理、自控原理、线性控制理论、现代控制理论等也是必须的专业知
识;基本素质是我们思考问题的方式方法,以及我们接人待物、和他人相处、沟通方面的能力。这三个方面交织在一起,无法有效的单独训练某一方面,需要在一定
的环境下进行系统的锻炼。”
“哦?有这么复杂嘛?”小P不解。
“是啊是啊,如果你想成为优秀的工程师或者科技工作者,既要深练内功,努力提高个人素质和基础知识,也要锻炼外功,做一个合格的实践者。”老C感叹,“这
两者缺一不可,既要在理论上深入研究,也要懂得如何将理论正确的、良好的、具有工业强度的应用于实践。”他开始自吹自擂,“幸好兄弟我在这方面还有几下散
手,没事我们可以讨论讨论,切磋切磋……”
“呵呵,好啊。”小P点头,飞快,“不过现在也不早了,我们还是闪人吧。”
“Ok,ok。”老C点头,“我们就回吧,对了,上次你给我说的魔兽用人类tower rush很是厉害,怎么我就没有感觉呢……”
“呵呵,等回去了我给你操练操练……”小P笑道……
(下来有josephus问题的一些讨论,以及算法效率方面的一些基本讨论,再后面是有限状态机的讨论,再再后面是标准输入输出库的讨论,再再再后面是配置管理的讨论,再再再再后面是递归下降语法分析讨论……总之哥俩要说的事情还没完没了呢)