zoukankan      html  css  js  c++  java
  • 《具体数学》——递归问题

      以后的搬砖转化一下思路,以一本书为主体,进行类似读书笔记式的讨论和总结,首先便从这本高德纳的《具体数学》开始吧。

      大致翻了一下这本书,能够明显感觉到大师所著的书和国内的一些出版物是没办法比的。看了一下这本书的序言,发现这本书有一个很大的特色就是“数学涂鸦”,好像是有一部分读者在阅读该书时的注释被收录到了这本书当中,这些涂鸦中不仅仅有俏皮的玩笑,还不乏精巧的解题方略,与作者在论述清晰优雅而又诙谐的语调融为一体,大概才看了一节,感觉《具体数学》的确是一本值得仔细研读的好书。

      那么下面我们便开始正式开始搬砖工作。

      

      汉诺塔问题。

      这个问题有个非常具有历史气息的背景:“卢卡斯[260] 给这个玩具赋予了一个罗曼蒂克的传说,说的是一个大得多的婆罗贺摩塔 (Tower of Brahma),它由64个纯金的圆盘堆放在三座钻石做成的方尖塔上.他说,上帝一开 始把这些金圆盘放到了第一座方尖塔上,并命令一组牧师按照上面的规则把它们移动到第三 座方尖塔上.据说牧师们夜以继日地工作,当他们完成任务时,那座塔就将坍塌,世界也将毁灭。”

      汉诺塔最初是的简化模型是这样的,给三个柱子,一个柱子上拍着自下而上逐渐变小的n个圆盘,要求每次只能移动柱子上最上面的圆盘,并且这个圆盘不能移动到顶层比该圆盘小的柱子上。其实这样文字叙述有些拗口,汉诺塔是作为一种精巧的智力游戏的,读者不妨去体验一下汉诺塔游戏以便更好地理解其规则。

      那么现在我们尝试从数学的角度来看这个模型。

      我们记T(n)表示将n个圆盘从一个柱子移动到另一个柱子需要的最少步数。为了表述方便,我们将三个柱子记为A、B、C,我们探讨将A柱上n个圆盘移动到B柱上的最小步数。我们可以将求解T(n)分解成如下三个步骤:

      1.将n-1个圆盘从A移动到C。

      2.将第n个盘子移动到B。

      3.将n-1个圆盘从C移动到B。

      由此我们不难发现,T(n) = 2T(n-1) + 1,而T(1) = 1,我们可推知T(n) = 2^n - 1。

      那么基于最简单的汉诺塔模型,我们下面来看一些汉诺塔的变式问题。

      变式1:三个柱子间的移动呈现出逻辑关系,即A柱上只能移动到B柱上,B柱上只能移动到C柱上,游戏的目的变为将n个圆盘从A移动到C。

      那么基于这种限制条件,为了求解T(n)(需要注意,此时T(n)所表达的意思也随着游戏目的的改变而发生变化,即T(n)表示将n个圆盘从A移动到C所需要的最小步数),我们可以讲整个过程概括成如下几个步骤。
      1.将n-1个圆盘从A移动到C。
      2.将第n个圆盘移动到B。
      3.将n-1个圆盘从C移动到A。
      4.将第n个圆盘从B移动到C。
      5.将n-1个圆盘从A移动到C。
      通过这个过程我们不难看出,T(n) = 3T(n-1) + 2。

      变式2:基于变式1的限制条件,我们可以将最大的圆盘放在最上面,那么请问将n个圆盘从A移动到C最少需要多少步?

      我们尝试通过模拟游戏过程来找到递推关系。

      1.先将n - 1个圆盘移动到B。记为T(n-1)

      2.然后将第n个圆盘移动到C,需要2步。

      3.再将n - 1个圆盘移动到C,此时仍然需要T(n-1)步。

      需要注意的是,这里的T(n - 1)表示将n-1个圆盘从A移动到B或从B移动到C,即移动到相邻的柱子上的步数。

      如果我们记F(n)是变式2问题的解,那么F(n) = 2T(n-1) + 2     

      那么下面我们需要探讨的便是如何求解T(n)了,即将n个圆盘从A移动到B,考虑到题设的限制条件(圆盘在柱子间移动的逻辑关系),我们模拟出一下的游戏过程。

      1.先将n - 1个圆盘移动到C,步数为2T(n-1)。

      2.再将第n个圆盘移动到B,需要一步。

      3.再将n-1个圆盘从C移动到B,需要T(n-1)步。

      即T(n) = 3T(n-1) + 1 , T(1) = 1。

      变式3:如果给出四个柱子a、b、c、d,那么将n个圆盘从a移动到d的最少步骤如何求解?

      这是在基于经典汉诺塔问题上的一个四柱汉诺塔问题,我们设置F[n]记录将n个圆盘从a移动到d的最少步骤,下面我们不妨模拟一下游戏过程。

      1.我们先将x个圆盘从a依靠b、d移动到c,x∈[1,n-1],这其实等效于将其从a依靠b、c移动到d,则步数为F(x)。

      2.将n-x个圆盘从a依靠c移动到d,由于大小原因,c柱是不能使用的,因此这是一个经典的三柱汉诺塔,步骤为2^(n-x) - 1.

      3.将位于c柱上的x个圆盘,从c依靠a、b移动到d上,步骤为F(x).

      经过上面的步骤,对于x取区间[1,n-1],我们会得到一系列的解,加上最少步骤的限制,由此我们可知:f[n] = min(2*f[x] + 2^(n-x)-1 | x∈[1,n-1])。

      变式4:基于最经典的三柱汉诺塔,n个圆盘,求解第k大圆盘移动了几步?

      我们回忆在经典三柱汉诺塔问题中的分析,对于a、b、c三个柱子,首先需要借助c柱子,将n-1个圆盘移动到b,然后将第n个圆盘移动到c,随后将n-1个圆盘借助a从b移动到c,整个过程中,第n个圆盘移动了一次。

      我们再进一步的模拟这个递归过程,从n-1个圆盘的移动这个角度。将n-1个圆盘从a借助c移动到b过程中,我们需要将n-2个圆盘借助b移动到c,随后将第n-1个圆盘移动到b,这里第n-1个圆盘移动了一次,同理,随后将n-1个圆盘借助a从b移动到c,第n-1个圆盘也移动了一次。由此我们可以猜想,设f[k]表示n个圆盘的三柱汉诺塔第k大的圆盘移动的次数,有f[k] = 2f[k+1],或者写成f[k] = 2^(n-k)。

      关于这个猜想的证明,其实在关于这个猜想的给出过程中就是含变量的,也就是说可以直接推出来的,如果你还觉得不够,我们对于n个圆盘的三柱汉诺塔,可基于这个猜想将n个圆盘移动次数写出:2^0 , 2^1 , ……,2^n-1.通过等比数列求和公式,容易看到总步数为2^n - 1,这与我们之前得出的结论是自洽的,由此足见猜想的正确性。

      变式5:对于有3个柱子n个圆盘的汉诺塔,问:n个圆盘在三个圆柱上有多少种分配方式?

      其实这个变式仅仅就是基于汉诺塔的一个组合计数问题了,和递推已经没太大的关系。需要明确的是,三个柱子的位置不同,因此这里应该认定三个柱子是互不相同的。基于此,每个圆盘有3种情况,最终的总数即为3^n。

      约瑟夫环问题。

      这个问题也是有个很有意思的历史起源:“传说如果不是由于他的数学天赋,约瑟夫不会活到出名的那一 天.在犹太罗马战争期间,他们41名犹太反抗者困在了罗马人包围的洞穴中.这些反抗者宁 愿自杀也不愿被活捉,于是决定围成一个圆圈,并沿着圆圈每隔两个人杀死一个人,直到剩 下后两个人为止.但是,约瑟夫和一个未被告发的同谋者不希望无谓地自杀,于是他迅速 计算出他和其朋友在这个险恶的圆圈中应该站的位置。”

      我们先将将这个问题抽象化成数学模型:即将n个人围成一圈,从n开始每隔1个人杀死一个,那么求解最终活下来的那个人的起始编号?

      这个问题似乎进行模拟运算是可解的,但是过程太过繁杂丑陋,我们需要一些更加漂亮的数学技巧。

      我们以n = 10为例来模拟这个过程,我们绕着圆周筛选一次,则2、4、6、8、10被删除,剩余1、3、5、7、9,为了方便新一轮的筛选,我们此时进行重新编号。

      1 -> 1

      3 -> 2

      5 -> 3

      7 -> 4

      9 -> 5

      我们设置J(n)表示约瑟夫问题的解,通过上述问题的转化我们发现,我们在模拟求解J(n)的时候,生成了一个子问题,而这个子问题又可以转化成另外一个完全独立的问题,也就是在求解J(10)的过程中,我们形成了一个1、3、5、7、9作为环的子问题,而我们又将这个子问题转化成了求解J(5),基于原问题和子问题具有同解性,再基于将子问题转化成J(5)的过程,我们容易建立起J(10)和J(5)的递推关系,即:J(10) = 2J(5) - 1。

      由此我们不难进行推广,J(2n) = 2J(n) - 1。

      其实讲到这里,如果理解了的话,其实掌握了解决约瑟夫环问题的核心思维了,虽然此时我们对于奇数情况还没有讨论。

      我们基于这种思维,进行一个推广,对于n个人,从1开始,每次删掉m的整数倍,该如何求解呢?

      类似上文的思路,这次我们缩短我们的脚步,不要一次删掉一圈再进行子问题转化,而删掉一个数后就进行子问题转化。以n = 10,m = 3为例。

      第一次删除:3。

      删除后的结果:1、2、4、5、6、7、8、9、10。

      转化子问题:1 -> 8

                       2 -> 9

                       4 -> 1

                       5 -> 2

                       6 -> 3

                       7 -> 4

                       8 -> 5

                       9 -> 6

                      10-> 7

      我们容易看到,J(10) = (J(9)   + 3) %10,我们也不难找到推广规律——J(n) = (J(n-1) + m)%n。

      然后基于J(1) = 1,我们便可以对所有问题进行递推求解。

  • 相关阅读:
    Yii中缓存依赖的处理
    简单实现Tab切换(带框架)
    Server Application Unavailable出现的原因及解决方案集锦
    Microsoft.ReportViewer.WebForms, Version=10.0.0.0的报错问题,解决方案
    ASP.NET中树型DropDownList的绑定
    VS2010 使用时选择代码或双击时出错,点击窗口按钮后VS自动重启问题
    js实现文本框限制输入数字和小数点--兼容多个浏览器
    输入的全角字符转换成半角字符--css、js、ASP.NET
    sql把表格拼成字符串,多半使用于GROUP BY
    Sql将逗号分隔的字符串分拆成表格的方法
  • 原文地址:https://www.cnblogs.com/rhythmic/p/5435949.html
Copyright © 2011-2022 走看看