Recursion 使用递归理解河内塔的力学
我理解求解河内塔的递归算法所做的工作: 例如,如果我们有三个销钉(A、B、C),我们想从A->C移动3张光盘,我们将光盘1和2移动到B,然后将最大的光盘3移动到销钉C,然后将之前移动到B的光盘1和2移动到C。此算法以伪方式表示,如下所示:Recursion 使用递归理解河内塔的力学,recursion,towers-of-hanoi,Recursion,Towers Of Hanoi,我理解求解河内塔的递归算法所做的工作: 例如,如果我们有三个销钉(A、B、C),我们想从A->C移动3张光盘,我们将光盘1和2移动到B,然后将最大的光盘3移动到销钉C,然后将之前移动到B的光盘1和2移动到C。此算法以伪方式表示,如下所示: FUNCTION MoveTower(disk, source, dest, spare): IF disk == 0, THEN: move disk from source to dest ELSE: MoveTow
FUNCTION MoveTower(disk, source, dest, spare):
IF disk == 0, THEN:
move disk from source to dest
ELSE:
MoveTower(disk - 1, source, spare, dest)
move disk from source to dest
MoveTower(disk - 1, spare, dest, source)
END IF
我打电话:MoveTower(3,A,C,B)
哪个会调用MoveTower(2,A,B,C)
哪个会调用MoveTower(1,A,C,B)
哪个会最终到达将移动A->B的基本情况
这就是我困惑的地方。当到达基本情况时,我们将A上的顶部磁盘移动到B(在一个快照中),递归如何移动其他所有内容?“后退阶段”如何移动其他光盘(在本例中,除最大的光盘外,所有光盘都移动到B)?它不是只在底部移动顶部光盘吗
例如,我知道在阶乘函数中,在到达基本情况后,递归“返回”一个值,该值传递给上一个调用,该值一直传递给上一个调用,直到上一个调用为止。在每一级,它都在等待它的递归返回
有人能帮我理解递归在第一次MoveTower(disk-1,source,spare,dest)
调用中除了到达基本情况之外是如何完成任何事情的吗
谢谢您必须在脑海中想象呼叫树:
MoveTower 3, from A, to B, using C:
1. MoveTower 2, from A, to C, using B:
1.1. MoveTower 1, from A, to B, using C:
1.1.1. Move disk 1 from A to B.
1.2. Move disk 2 from A to C.
1.3. MoveTower 1, from B, to C, using A:
1.3.1. Move disk 1 from B to C.
2. Move disk 3 from A to B.
3. MoveTower 2, from C, to B, using A:
3.1. MoveTower 1, from C, to A, using B:
3.1.1. Move disk 1 from C to A.
3.2. Move disk 2 from C to B.
3.3. MoveTower 1, from A, to B, using C:
3.3.1. Move disk 1 from A to B.
现在,如果我们只读取“移动磁盘”操作,我们有:
Move disk 1 from A to B.
Move disk 2 from A to C.
Move disk 1 from B to C.
Move disk 3 from A to B.
Move disk 1 from C to A.
Move disk 2 from C to B.
Move disk 1 from A to B.
至于“它是如何完成任何事情的”,是这样的:
- 我们必须使用C柱作为临时存储器,将n个磁盘塔从a柱移动到B柱
- 有两种可能,n为1,或n大于1
- 如果n是1,我们就移动磁盘
- 如果n大于1,我们分三步进行:
- 移动一个较小的n塔− 1使用B作为临时存储从A到C的磁盘
- 将底部磁盘从A移到B
- 使用A作为临时存储,将较小的n-1磁盘塔从C移动到B
耐心一点,准确一点
FUNCTION MoveTower(disk, source, dest, spare):
1. IF disk == 0, THEN:
2. move disk from source to dest
3. ELSE:
4. MoveTower(disk - 1, source, spare, dest)
5. move disk from source to dest
6. MoveTower(disk - 1, spare, dest, source)
7. END IF
想象你自己是一台人类计算机,坐在一张舒适的桌子旁,手里拿着大量的纸和铅笔
要调用函数,请将函数配方复制到一张空白纸上,并将其放在面前
要调用另一个函数,请将该函数的配方复制到一张空白的纸上,然后将这张纸放在面前的一堆纸上。无论您是否调用同一个函数,这都无关紧要,因为您正在使用其配方的副本
呼叫移动塔(3,A,C,B)
==>MoveTower_配方{磁盘=3,源=A,目标=C,备用=B}
| 1. 如果磁盘==0,则:
=如果3==0,则:
....
3.其他:
4.调用MoveTower(磁盘-1、源、备用、目标)
=呼叫移动塔(3-1,A,B,C)
==>MoveTower_配方{磁盘=2,源=A,目标=B,备用=C}
| 1. 如果2==0,则:
.....
3.其他:
4.呼叫移动塔(1、A、C、B)
==>MoveTower_配方{磁盘=1,源=A,目标=C,备用=B}
| 1. 如果1==0,则:
3.其他:
4.呼叫移动塔(0、A、B、C)
==>MoveTower_配方{磁盘=0,源=A,目标=B,备用=C}
| 1. 如果0==0,则:
2. ___将磁盘{0}从源{A}移动到目标{B}{uuuuuu(*1)
7.如果结束
.....
.....
看到了吗?MoveTower
函数配方的副本堆叠在一起,每个副本都保存着函数命名参数的实际值(在这里,堆栈会向下扩展,但在你的办公桌上,一堆文件会堆积起来)
您可以在最上面的一张纸上沿着配方工作,在其边距上记下您当前在配方线条上的位置,以及各种命名参数和/或命名内部变量(如果有)和/或临时未命名值(如磁盘-1
)的值
当您处理完最上面的一张纸时,您会将其扔掉,而不是在将其返回值(如果有)复制到现在最上面的一张纸之前,在您输入现在丢弃的配方副本之前的位置
您还可以在您身边的另一张纸上,记录您的程序在现实世界中可能产生的影响(上面标有(*1)
,(*2)
,等等),记录由您的人类计算机执行的输入输出指令,这些指令遵循功能配方(副本)
就这样
计算状态记录在堆栈中每个配方副本的边距中
当您达到基本情况时,不仅生成了第一条输出指令;您还将大量函数配方的副本堆放在面前,每个副本都有相关的数据和状态(当前执行点)
CALL MoveTower(3, A, C, B)
==> MoveTower_recipe{ disk=3, source=A, dest=C, spare=B }
| 1. IF disk==0, THEN:
= IF 3 ==0, THEN:
....
3. ELSE:
4. CALL MoveTower(disk - 1, source, spare, dest)
= CALL MoveTower(3-1, A, B, C)
==> MoveTower_recipe{ disk=2, source=A, dest=B, spare=C }
| 1. IF 2 == 0 THEN:
.....
3. ELSE:
4. CALL MoveTower( 1, A, C, B)
==> MoveTower_recipe{ disk=1, source=A, dest=C, spare=B }
| 1. IF 1 == 0 THEN:
3. ELSE:
4. CALL MoveTower(0, A, B, C)
==> MoveTower_recipe{ disk=0, source=A, dest=B, spare=C }
| 1. IF 0 == 0 THEN:
2. ___move disk{0} from source{A} to dest{B}___ (*1)
7. END IF
<==
5. ___move disk{1} from source{A} to dest{C}___ (*2)
6. CALL MoveTower( 0, C, B, A)
==>
.....
.....