您的位置:首页 > 编程语言 > C语言/C++

汉诺塔循环解法启蒙---C语言实现

2019-04-28 09:30 113 查看

Hanoi汉诺塔循环解法启蒙—C语言实现

首先我们需要清楚一点:这个游戏解法步骤并不固定,但是它的最优解的步骤确是固定的。
提示:本文的思路和焦点在塔尖上,因为无论哪一步塔尖始终在最上方。
试想:我们把塔尖从一个柱子上移动到另一柱子上,接下来的一步一定不会再是移动塔尖,无论是(1.移动回去)还是(2.移动到第三个柱子上),因为这和(1.塔尖不动)或(2.直接移动到第三根上)得到的结果是一样的,并且还多了一步。
我们可以得到一个结论:塔尖不会连续被移动,所以得到塔尖的那个柱子就暂时失去的行动的能力。
接下来的一步一定发生在没有塔尖的两根柱子上。在这两根柱子上的操作只有两种情况把较小的移动过去,或移动到空柱子上。接下来再继续在这两根柱子上移动已经没有任何意义,所以我们有得到一个结论:两根柱子之间不会重复移动,因此下一步我们必须去移动塔尖,移动完塔尖又必须移动其余两个,然后一直重复操作,直到游戏结束。
我们的疑惑是:塔尖到底该怎么移动呢?? 无论是游戏的开始第一步还是以后每次移动塔尖的时候,都会有疑惑,因为有两个选择,下面我们用一个简单的例子解惑:
<我们移动时遵循上面的结论:1.塔尖不会连续被移动 2.两根柱子之间不会重复移动>
我们的目的是要在遵循上面得到的最优解的结论的前提下,去找到塔尖移动的规律。

假设 n=2 即有两个盘子:

ONE:塔尖移动: A->B->C (循环右移)

steps A B C
0 1/2/0 /0 /0
1移动塔尖 2/0 1/0 /0
2条件移动 /0 1/0 2/0
3移动塔尖 /0 /0 1/2/0

TWO:塔尖移动: A->B->A->B

steps A B C
0 1/2/0 /0 /0
1移动塔尖 2/0 1/0 /0
2条件移动 /0 1/0 2/0
3移动塔尖 1/0 /0 2/0
4条件移动 1/0 2/0 0/
5移动塔尖 /0 1/2/0 0/

当然我们也可以这样:
THREE: 塔尖移动: A->C->B (循环左移)

steps A B C
0 1/2/0 /0 /0
1移动塔尖 2/0 /0 1/0
2条件移动 /0 2/0 1/0
3移动塔尖 /0 1/2/0 /0

FOUR: 塔尖移动: A->C->A->C

steps A B C
0 1/2/0 /0 /0
1移动塔尖 2/0 /0 1/0
2条件移动 /0 2/0 1/0
3移动塔尖 1/0 2/0 /0
4条件移动 1/0 /0 2/0
5移动塔尖 /0 /0 1/2/0

我们甚至可以这样:
FIVE: 塔尖移动: A->B->A->C->A

steps A B C
0 1/2/0 /0 /0
1移动塔尖 2/0 1/0 /0
2条件移动 /0 1/0 2/0
3移动塔尖 1/0 /0 2/0
4条件移动 1/0 2/0 /0
5移动塔尖 /0 2/0 1/0
6条件移动 2/0 /0 1/0
7移动塔尖 1/2/0 /0 /0

可以看到如果任意移动塔尖有可能回到原点

假设 n=3 即有三个盘子:

ONE: 塔尖移动:A->B->C->A->B (称为循环右移)

steps A B C
初始 1/2/3/0 /0 /0
1移动塔尖 2/3/0 1/0 /0
2条件移动 3/0 1/0 2/0
3移动塔尖 3/0 /0 1/2/0
4条件移动 /0 3/0 1/2/0
5移动塔尖 1/0 3/0 2/0
6条件移动 1/0 2/3/0 /0
7移动塔尖 /0 1/2/3/0 /0

TWO: 塔尖移动:A->C->B->A->C (称为循环左移)

steps A B C
初始 1/2/3/0 /0 /0
1移动塔尖 2/3/0 /0 1/0
2条件移动 3/0 2/0 1/0
3移动塔尖 3/0 1/2/0 /0
4条件移动 /0 1/2/0 3/0
5移动塔尖 1/0 2/0 3/0
6条件移动 1/0 /0 2/3/0
7移动塔尖 /0 0 1/2/3/0

THREE: 塔尖移动:A->B->A->C->B->C->A->B

steps A B C
初始 1/2/3/0 /0 /0
1移动塔尖 2/3/0 1/0 /0
2条件移动 3/0 1/0 2/0
3移动塔尖 1/3/0 /0 2/0
4条件移动 1/3/0 2/0 /0
5移动塔尖 3/0 2/0 1/0
6条件移动 2/3/0 /0 1/0
7移动塔尖 2/3/0 1/0 /0
8条件移动 3/0 1/0 2/0
9移动塔尖 3/0 /0 1/2/0
10条件移动 /0 3/0 1/2/0
11移动塔尖 1/0 3/0 2/0
12条件移动 1/0 2/3/0 /0
13移动塔尖 /0 1/2/3/0 /0

由上面的移动我们可以得到两个结论:

  1. 只有塔尖的移动方向是循环左移或循环右移时,我们所需要的步骤才是最小的,并且步骤数相同。即:不论是循环左移还是右移都能得到最优解。
  2. 循环左移和右移最终的结束点是不同的

<数学表述>
n = 2 时:
{B&lt;=塔尖循环左移C&lt;=塔尖循环右移 \left\{ \begin{aligned} B &amp; &lt;= &amp;塔尖循环左移 \\ C &amp; &lt;= &amp;塔尖循环右移 \\ \end{aligned} \right. {BC​<=<=​塔尖循环左移塔尖循环右移​
n = 3时:
{B&lt;=塔尖循环右移C&lt;=塔尖循环左移 \left\{ \begin{aligned} B &amp; &lt;= &amp;塔尖循环右移 \\ C &amp; &lt;= &amp;塔尖循环左移 \\ \end{aligned} \right. {BC​<=<=​塔尖循环右移塔尖循环左移​
经过类比和归纳我们可以得到n为偶数时的情况和n = 2时一样, n为奇数时和n = 3时的情况一样, 比如n=1时右移到B左移到C。
故有:
当目标点为B时:
B&lt;={塔尖循环右移,n为奇数塔尖循环左移,n为偶数 B&lt;=\left\{ \begin{aligned} 塔尖循环右移 &amp; , &amp;n为奇数 \\ 塔尖循环左移 &amp; , &amp;n为偶数 \\ \end{aligned} \right. B<={塔尖循环右移塔尖循环左移​,,​n为奇数n为偶数​
当目标点为C时:
C&lt;={塔尖循环左移,n为奇数塔尖循环右移,n为偶数 C&lt;=\left\{ \begin{aligned} 塔尖循环左移 &amp; , &amp;n为奇数 \\ 塔尖循环右移 &amp; , &amp;n为偶数 \\ \end{aligned} \right. C<={塔尖循环左移塔尖循环右移​,,​n为奇数n为偶数​
也就是说:如果我们确定了目标柱,并且知道盘子的数目,那么塔尖的移动方向就是固定的了。
到此我们就可以根据上面的结论写程序了, 先就这样,代码以后再贴。

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>

#define TIP             1
#define NUMEL(x)        (x->esp - x->ebp)               //calculate the number of element in stack x
#define POP(x)          (x->esp -= 1)                   //POP stack
#define PUSH(x, y)      {x->esp += 1; *x->esp = y;}     //PUSH y in stack
#define ISEMPTY(x)      (*x->esp == 0)                  //judge whether the stack is empty
#define VALUE(x)        (*x->esp)                       //get the top VALUE of the stack
#define LABEL(x)        (x->label)                      //get the label of the stack

int main()
{
int n;
void Hanoi_Iteration(int n);
printf("Enter the value of disks: ");
scanf("%d", &n);
if(n <= 0)
{
printf("Enter a number greater than 0\n\n");
return 0;
}
Hanoi_Iteration(n);
return 0;
}

typedef struct
{
int *esp;        //always point to the top of the stack
int *ebp;        //always point to the bottom of the stack
char label;      //store the Tower LABEL such as A, B, C
}ST,*PST;

// initialize structure
PST createTower(int n)
{
PST ptower = (PST)malloc(sizeof(ST));
if(ptower == NULL)
{
printf("Fail to allocate memory\n");
exit(0);
}
ptower->esp = ptower->ebp = (int*)malloc(sizeof(int)*(n+1));
if(ptower->esp == NULL)
{
printf("Fail to allocate memory\n");
exit(0);
}
*ptower->ebp = 0;   //we use the number zero to represent a empty tower
return ptower;
}

void freeTower(PST ptower)
{
free(ptower->ebp);
ptower->esp = ptower->ebp = NULL;
free(ptower);
}

void Hanoi_Iteration(int n)
{
int i = n;
PST current = createTower(n), next = createTower(n), other = createTower(n), temp;
void steps(PST current, PST next, PST other, int n);
if(n % 2 == 0)   //if even, rotate right
{
LABEL(current) = 'A'; LABEL(next) = 'B', LABEL(other) = 'C';
}
else             //if odd, rotate left
{
LABEL(current) = 'A'; LABEL(next) = 'C', LABEL(other) = 'B';
}
//initialize the original Tower/pole
for(;i>0;i--)
{
PUSH(current, i)
}
printf("The sequence of moves involved in the Tower of Hanoi are:\n\n");
do{
steps(current, next, other, n);    //move
temp = current; current = next; next = other; other = temp;           //set the pole which has the tip(next) as current, other as next, current as other.
}while(!(NUMEL(current) == n || NUMEL(other) == n || NUMEL(next) == n)); //when any tower regains all the disks, the game's over

freeTower(current);
freeTower(next);
freeTower(other);
current = next = other = NULL;
}

void steps(PST current, PST next, PST other, int n)
{
//1. move TIP to the next
PUSH(next, TIP);
POP(current);
printf("Move disk 1 from peg %c to peg %c\n", LABEL(current), LABEL(next));
if(NUMEL(current) == n || NUMEL(other) == n || NUMEL(next) == n) return;
//2. campare current and other and then move the smaller disk to their opposite or move directly to the empty pole.
if(!ISEMPTY(current) && VALUE(current) < VALUE(other) || ISEMPTY(other))
{
PUSH(other, VALUE(current));
POP(current);
printf("Move disk %d from peg %c to peg %c\n", VALUE(other), LABEL(current), LABEL(other));
}
else
{
PUSH(current, VALUE(other));
POP(other);
printf("Move disk %d from peg %c to peg %c\n",VALUE(current),  LABEL(other), LABEL(current));
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: