骑士游历

AI算法攻城狮
算法领域优质创作者
博客专家认证
2012-08-10 04:20:43
练习(6.24)对于国际象棋爱好者而言,最有趣的智力题之一就是骑士游历问题。这个问题最早是由数学家欧拉(Euler)提出来的。问题是这样的:被称为骑士的国际象棋棋子是否能够走遍空白棋盘上的64个格,每个格子只经过一次且仅仅经过一次。现在让我们深入地分析这个有趣的问题。
骑士按照一条“L形状”的路线移动(在一个方向上移动两格,然后转一个直角再移动一格)。这样,从空白棋盘中心的一格出发,其实就可以有如图6.25所示的八种不同的移动方案(方案0到方案7)。
a) 请在一张白纸上画一个8乘8的棋盘,然后,用手画出骑士游历的线路。在您进入的第一个格中写上1,第二个格中写上2,第三个格中写上3,依此类推。在开始前进之前,估计您能够走多远,别忘了整个旅行由64次移动组成。最后您走了多远?接近您的估计值吗?eaning
b) 现在让我们开发一个程序来在一个棋盘上移动骑士。棋盘用一个8乘8的双下标数组board表示,所有的数组元素(表示一个格子)被初始化成零。我们根据它们的水平和垂直分量来表示八种可能的移动方案中的一种。例如,如图6.25所示,0号方案是水平向右移动两格,然后再垂直向上移动一格,2号方案是水平向左移动一格,然后再垂直向上移动两格。水平向左和垂直向上移动用负数来表示。这八种不同的移动方案用如下的两个单下标数组horizontal和vertical表示:
horizontal[ 0 ] = 2
horizontal[ 1 ] = 1
horizontal[ 2 ] = -1
horizontal[ 3 ] = -2
horizontal[ 4 ] = -2
horizontal[ 5 ] = -1
horizontal[ 6 ] = 1
horizontal[ 7 ] = 2

vertical[ 0 ] = -1
vertical[ 1 ] = -2
vertical[ 2 ] = -2
vertical[ 3 ] = -1
vertical[ 4 ] = 1
vertical[ 5 ] = 2
vertical[ 6 ] = 2
vertical[ 7 ] = 1
用变量currentRow和currentColumn来表示骑士当前位置的行号和列号。用变量moveNumber来表示所采用的移动方案号,moveNumber是一个取值范围在0到7之间的整数。您的程序可以使用如下语句:
currentRow += vertical[ moveNumber ];
currentColumn += horizontal[ moveNumber ];
采用一个在1到64之间变化的计数器变量counter,来记录骑士是在第几步走到某一个格子中的。在每一次移动前,记住,别忘记测试即将进行的移动是否走进了一个已经走过的格子或者是否会走出棋盘。现在就开始编写在棋盘上移动骑士的程序吧。运行您的程序,看看这个骑士走了几步?

c)在编写并运行完骑士游历程序后,您也许会在其中发现一些有价值的东西。下面让我们利用这些东西来编写一个启发式(有策略的)程序来指导骑士的移动。启发并不能确保成功,但是一个精心设计的启发式策略却能够极大地提高成功的机会。您也许已经观察到:棋盘上远离中心的格子在某种意义上比靠近中心的格子更难处理。事实上,最难处理的,或者最不可达的格子就是位于四个边角的格子。
直觉告诉您:首先把骑士移到这些最难处理的格子中,那些容易处理的格子放到后面处理。按照这样的方法,当骑士游历了大多数格子的时候,成功的机会就越来越大。
通过将每一个格子按照它们易于达到的程度进行分类,我们可以设计一个“可达度启发式”策略:总是将骑士移进可达度最低的格子(当然是在骑士的L形状移动的可达范围内)。我们用一个双下标数组accessibility来记录棋盘上每个格子的可达度,一个特定格子的可达度,就是从这个格子出发能够进入其他格子的数目。在一个空白棋盘上,中央格子的可达度被定为8,边角格子的可达度被定为2,其他格子的可达度可能是3、4或6。棋盘格子的可达度分布如下:
2 3 4 4 4 4 3 2
3 4 6 6 6 6 4 3
4 6 8 8 8 8 6 4
4 6 8 8 8 8 6 4
4 6 8 8 8 8 6 4
4 6 8 8 8 8 6 4
3 4 6 6 6 6 4 3
2 3 4 4 4 4 3 2

现在,请编写一个采用“可达度启发式”策略的骑士游历程序。每次,骑士都选择可达度最低的格子进入。在出现平局时,骑士随机地选择可达度相同的一个格子进入。因此,骑士的游历总是从四个边角的格子开始。[注意:在骑士游历棋盘的过程中,随着越来越多的格子已经被游历,您的程序应该减少剩余格子的可达度。按照这种方式,在游历过程中的任意时刻,每一个可达格子的可达度总是恒等于从这个格子出发能够进入其他格子的数目。]运行新的程序。您是否实现了棋盘遍历?请修改您的程序来运行分别从棋盘上不同格子出发的64个游历,看看您能实现多少个棋盘遍历?
d) 进一步修改您的骑士游历程序,使其在遇到两个或者多个格子的平局时,骑士将从这些可达度相同的格子中“前瞻性地”选择一个格子进入。从这个格子出发,下一步能够进入一个可达度较低的格子。


6.25 (骑士游历:蛮力方法) 在练习6.24中我们提出了求解骑士游历问题的一种方法。这种被称为“可达度启发式”的方法可以派生出许多解决方法,它们的执行效率都很高。
随着计算机性能的不断增长,我们可以凭借计算机的强大计算能力、使用相对简单的算法来解决很多问题。我们称这样的问题求解方法叫做“蛮力方法”。

a) 利用随机数产生程序,使骑士在棋盘上随机游走(当然遵照骑士的L形状移动规则)。让您的程序运行一次游历,然后,打印出最后的棋盘结果。看看骑士到底走了多远?

b) 大多数情况下,刚才那个程序得到的是一个相对较短的游历。修改您的程序,让它进行1000次游历。用一个单下标数组来记录不同游历长度的游历次数。当您的程序完成1000次游历后,用表格形式打印出游历长度的分布信息。看看哪一个结果是最好的?

c) 大多数情况下,前面那个程序会给您一个“很可观的”游历结果,但是可能没有遍历整个棋盘。现在“拿掉所有的停止标记”,让您的程序一直运行直到产生一个遍历结果。 [注意:这个版本的程序可能会在一个高性能的计算机上运行好几个小时。]还是使用一个表格来保存不同游历长度的游历次数,并在程序找到第一个遍历结果时打印这个表格。在程序产生第一个遍历结果之前,您的程序已经尝试了多少次游历?运行了多少时间?

d) 比较骑士游历程序的“蛮力”版本和“可达度启发”版本。哪一个要求对问题研究得更仔细?哪一个算法更难设计?哪一个需要更高的计算机性能?我们能够(提前)确认使用可达度启发方法肯定能得到一个遍历结果吗?我们能够(提前)确认使用蛮力方法肯定能得到一个遍历结果吗?概括地说明蛮力问题求解方法的优缺点
...全文
249 回复 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

16,548

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • AIGC Browser
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧