河内塔问题

在经典河内塔问题中,有3根柱子和N个不同大小的穿孔圆盘,盘子可以滑入任意一根柱子。一开始,所有盘子自上而下按从大到小依次套在一根柱子上,现在想将所有的圆盘按照原来的位置从一根柱子移动到另一根柱子上,移动过程需要遵守一些规则:
1.每次只能移动一个盘子
2.盘子只能从柱子顶端滑出移动到下一根柱子
3.盘子只能叠在没有盘子的柱子或者比它大的盘子上

聚焦河内塔问题。河内塔的规则,限定了较大的圆盘要先转移到目标柱子(选择的任意一根柱子)上,这时直接转移是不可行的,一定要利用其他柱子。问题中只有3根柱子,所以,另外一个柱子一定是要将N-1个圆盘按照从大到小堆起,(这个柱子我们叫它cache peg存放柱),这才使得最大圆盘可以直接从原来位置转移到目标位置。
并且,这种方案最优。因为在这个问题下,最大圆盘转移到目标柱子仅用一次是最少且必须的。
抽象一下算法,解决N个圆盘的移动,记TnT_{n}Tn​:
(这里会用到记号,a small tips for algorithm to solve the questions)

  1. 将最上面的N-1个圆盘移动到cache peg,记为Tn−1T_{n-1}Tn−1​
  2. 可以拿出最大的圆盘,且仅能将圆盘从原柱子移动到目标柱子
  3. 将在cache peg上的N-1个圆盘最终都移动到目标柱子上,也是Tn−1T_{n-1}Tn−1​了

分解成具体步骤后,可以发现移动的规律,TnT_{n}Tn​被分解成2*Tn−1T_{n-1}Tn−1​+1次移动了。河内塔问题完全是利用子问题解决原问题的策略,可以解决的数学问题。

这种通过子问题解决原问题,不断地向前推进,找到最小子问题的解,再回溯计算原问题的方法,被定义成递归,用递归解决问题的被定义为递归问题。

河内塔问题可以轻松利用递归解决,时间复杂度是O(2n2^n2n)。

回到数学

接下来用数学角度去看河内塔问题,旨在给出一种数学手段,解释一般递归问题有关时间复杂度的计算。读者可以酌情阅读,不影响对河内塔问题的理解。

河内塔问题是经典的N个圆盘移动的递归问题。因为三柱河内塔的TnT_{n}Tn​最少移动数是:Tn=2∗Tn−1+1(n>0)T_{n}=2*T_{n-1}+1(n>0)Tn​=2∗Tn−1​+1(n>0)

我们可以直接利用递归解决河内塔问题,但递归仅能表明TnT_{n}Tn​和Tn−1T_{n-1}Tn−1​之间的关联,如果要计算时间复杂度、空间复杂度时,纯观察一般很难得出正确的结论。

就如河内塔问题,很难通过观察递归实现得出O(2n2^n2n)的时间复杂度的解。

但数学给了一种直观且有效的解决递归问题的策略,并把问题总结成计算N个规模递归式


在数学角度,当你有了转移方程(转移方程指河内塔的Tn=2∗Tn−1+1(n>0)T_{n}=2*T_{n-1}+1(n>0)Tn​=2∗Tn−1​+1(n>0)),求解河内塔最少移动数的数学表达,即递归式,一般都会联想到一种数学证明方法。

这和等差/等分数列问题的解题思路一致。

对于这种手段解决问题,高中数学便已经有介绍并清楚的定义——数学归纳法

数学归纳法是证明某个命题对所有满足n≥n0n_0n0​的整数n都成立的一种方法。

(1) 研究小规模的情况。如n=1,2,3的最后结果,观察结果寻找规律
(2)假设从n=n0n_0n0​到n=k都满足你找到的规律,将注意力放在n=k和n=k-1上,验证规律的正确性

再看河内塔问题,可以通过小的样本发现TnT_nTn​的递归式,Tn=2n−1T_n=2^n-1Tn​=2n−1
然后验证递归式的正确性,当n=k时,Tk=2∗Tk−1+1T_k=2*T_{k-1}+1Tk​=2∗Tk−1​+12k−1=2∗(2k−1−1)+12^k-1=2*(2^{k-1}-1)+12k−1=2∗(2k−1−1)+1同时左右式加1:2k=2∗(2k−1−1)+22^k=2*(2^{k-1}-1)+22k=2∗(2k−1−1)+2左右式相等,即递归式是2n−12^n-12n−1

河内塔的最小移动数是2n−12^n-12n−1,与N个规模的圆盘成正比。时间复杂度即O(2n2^n2n-1),也是O(2n2^n2n)。

当你遇到递归问题,尝试用数学归纳法帮助你完成很多工作。

河内塔问题的实现

至此,具体数学对河内塔问题的理解白话结束。透过河内塔问题,寻找到一般递归问题的解题思路,并对递归问题的复杂度计算给出一种数学解释。当面临递归问题时,可以帮助读者更全面的思考。

现在,我们会根据前面的算法,实现对河内塔问题的解答,这并不需要太多代码,代码的工作会尽可能保持clean coding的风格。

C++实现如下:

#include<vector>
#include<iostream>void move(std::vector<int>& A,std::vector<int>& B,std::vector<int>& C,int n){if(0==n)return;//移动N-1个圆盘move(A,C,B,n-1);//将圆盘的第N个直接转移到目标柱C.push_back(A.back());A.pop_back();//将cache peg的N-1个圆盘移动到Cmove(B,A,C,n-1);}/*
* A peg:原柱
* B peg:cache peg 存放柱
* C peg:目标柱
*/
void hanota(std::vector<int>& A,std::vector<int>& B,std::vector<int>& C){//目标是移动所有的A,到达Cint n=A.size();//移动原柱的N个圆盘到目标柱上move(A,B,C,n);
}void traverse(std::vector<int>& peg,const char* info){std::cout<<info<<std::endl;for(int plant:peg){std::cout<<plant<<",";}std::cout<<std::endl;
}int main(int argc,char* argv[]){//顺序不变的移动A到C,移动过程中。顺序保证降序std::vector<int> A{10,9,8,7,6,5,4,3,2,1,0};std::vector<int> B;std::vector<int> C;hanota(A,B,C);//验证结果traverse(A,"A");traverse(B,"B");traverse(C,"C");std::cin.get();
}

河内塔问题的运行结果

小结

三柱河内塔问题从算法的角度是一个经典的递归问题。相信你认真读完这一则文章,对加深递归问题的印象会有很大的帮助。

递归问题实际上就是一个完整的全局问题可以被分解成若干个嵌套的局部问题,局部问题是只关注原问题(大规模)和子问题(小规模)的联系,每一个原问题和子问题的算法结构相同。

递归不熟悉的帮转:递归

下一篇文章,我们在河内塔问题的基础上讨论四柱多柱河内塔问题,它是河内塔问题的泛化,算法也不仅仅是简单的递归,还会涉及到择优,最优选择等。它将进一步加深读者对原问题和子问题的理解,也会对问题的算法逻辑有更高层次的掌握。

掌握数学,是掌握算法的一把钥匙。当你掌握算法,你便能创造你的世界。

参考资料:

Ronald L. Graham,Donald E. Knuth,Oren Patashnik,“具体数学”, 张明尧,张凡 译,人民邮电出版社

具体数学 递归问题1.1 从河内塔/汉诺塔开始相关推荐

  1. 递归 (一): 递归思想与 C++ 中的递归函数及两个递归应用示例 (斐波那契, 汉诺塔)

    您也可以在我的个人网站中查看此文:http://zhaokaifeng.com/?p=1502 谢谢您的访问 : ) 什么是递归 从汇编层面上看递归 在汇编层面上, 递归可以看作是两个循环, 每个循环 ...

  2. 【汉诺塔】C语言递归解法,深层次地带你理解汉诺塔公式

    目录 汉诺塔公式 汉诺塔问题在数学层面的公式: C语言递归公式 两层汉诺塔 三层汉诺塔 递归问题可谓是学习C语言以来的第一个拦路虎,而汉诺塔问题更是递归中对新手很不友好的一道经典题,我们接下来从公式角 ...

  3. 小知识系列(3):Hanoi塔(汉诺塔,河内塔)

    同样,借此来强化学习,但是说实话我写这个感觉很玄.Hanoi塔是昨天刚学到的东西,想了很久,感觉还是没有悟透,可能学到更多新东西,或产生了新的想法,或突然悟到了什么,届时会再做修改. 看了很多关于Ha ...

  4. 小甲鱼Python第二十三讲、第二十四讲(递归-这帮小兔崽子、汉诺塔)

    def fab(n):迭代的方法if n<1:return -1while(n-2)>0:n3=n2+n1n1=n2n2=n3n=n-1return n3 def rabbit(n):递归 ...

  5. 小甲鱼《零基础学习Python》课后笔记(二十三、二十四):递归——这帮小兔崽子和汉诺塔

    由于递归实在不太理解,而且觉得题目大多是为了用递归而使用递归,觉得题目暂时没有做的必要,所以先跳过,以后用到再补充学习- 2018年8月11日

  6. 经典递归——斐波那契数列,汉诺塔

    斐波那契 汉诺塔 0 1 1 2 3 5 8 13 21 int fibonacci(int a){if(a==0)return 0;else if(a==1)return 1;elsereturn ...

  7. [河内塔]汉诺塔实现

    function hanoi (n, from, to, spare) {if(n == 1) {console.log(`${from} => ${to}`) }else {hanoi(n-1 ...

  8. python面向过程实践汉诺塔_递归汉诺塔-和递归汉诺塔相关的内容-阿里云开发者社区...

    多柱汉诺塔最优算法设计探究 多柱汉诺塔最优算法设计探究 引言 汉诺塔算法一直是算法设计科目的最具代表性的研究问题,本文关注于如何设计多柱汉诺塔最优算法的探究.最简单的汉诺塔是三个柱子(A.B.C),因 ...

  9. 个盘子的汉诺塔需要移动几步_图解汉诺塔问题( Java 递归实现)

    汉诺塔简介 最近在看数据结构和算法,遇到了一个非常有意思的问题--汉诺塔问题. 先看下百度百科是怎么定义汉诺塔的规则的: 汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具.大梵天创造世界的时候 ...

最新文章

  1. centos中卸载php,在Centos 7上完成PHP的卸载和重新安装
  2. Python 计算机视觉(十三)—— 图像的傅里叶变换
  3. OS / CPU是如何访问内存的?
  4. Form表单中的元素 控件集
  5. Android学习网站
  6. linux set权限,Linux 特殊权限set_uid(示例代码)
  7. Flume实战采集文件内容存入HDFS
  8. python flask框架是什么_Python三大web框架分别是什么 哪个更好
  9. hdu 3333 Turing Tree 求区间内不同数的和——线段树解法
  10. ZZNU-oj-2141:2333--【O(N)求一个数字串能整除3的连续子串的个数,前缀和数组+对3取余组合数找规律】...
  11. 零基础如何入门MATLAB(适用于所有编程语言)?(建议收藏)
  12. Chrome浏览器中比较实用的一些插件(文字复制、广告拦截、视频倍速、文献下载)
  13. marlab中主成分得分怎么求_雅漾恒润保湿精华乳,做完配方成分解读,我表示遗憾,决定守住我的花呗...
  14. Linux dstat 监控工具
  15. navicat12.1.18破解 亲测
  16. 左程云算法 哈希函数
  17. 怎么学计算机玩游戏,如何才能录制手机玩游戏的视频-电脑自学网
  18. 双目活体检测:人证核验一体机
  19. Vuex基本使用的总结
  20. 【IoT】产品设计:包装盒型设计

热门文章

  1. 啊哈c语言逻辑推箱子答案,啊哈C语言!逻辑的挑战(修订版)-2018-02-04
  2. 广州立信从兴电子笔试[2006.11.9]
  3. 舆情热点分析项目流程
  4. python panda 库安装_python安装lib库
  5. javah javac 不是内部或外部命令 解决方法
  6. 在王者荣耀角度下分析面向对象程序设计B中23种设计模式之组合模式
  7. Python scrapy 爬取安软市场
  8. devos勒索病毒解决办法|devos勒索病毒解密|devos勒索病毒专杀工具|devos勒索病毒如何感染电脑
  9. 写一封「用户体验」良好的求职邮件
  10. 面试官问我为啥B+树一般都不超过3层?3层B+树能存多少数据?redo log与binlog的两阶段提交?