米诺斯迷宫的传说来源于克里特神话,在希腊神话中也有大量的描述,号称世界四大迷宫之一。

  米诺斯是宙斯和欧罗巴的儿子,因智慧和公正而闻名,死后成为了冥国的判官。由于米诺斯得罪了海神波塞冬,波塞冬便以神力使米诺斯的妻子帕西法厄爱上了一头公牛,生下了一个牛首人身的怪物米诺陶洛斯。这个半人半牛的怪物不吃其他食物,只吃人肉,因此米诺斯把他关进一座迷宫中,令它无法危害人间。

  后来雅典人杀死了米诺斯的一个儿子,为了复仇,米诺斯恳求宙斯的帮助。宙斯给雅典带来了瘟疫,为了阻止瘟疫的流行,雅典从必须每年选送七对童男童女去供奉怪物米诺陶洛斯。

  当雅典第三次纳贡时,王子忒修斯自愿充当祭品,以便伺机杀掉怪物,为民除害。当勇敢的王子离开王宫时,他对自己的父亲说,如果他胜利了,船返航时便会挂上白帆,反之则还是黑帆。忒修斯到了米诺斯王宫,公主艾丽阿德涅对他一见钟情,并送他一团线球和一柄魔剑,叫他将线头系在入口处,放线进入迷宫。忒修斯在迷宫深处找到了米诺陶洛斯,经过一场殊死搏斗,终于将其杀死。

  忒修斯带着深爱他的艾丽阿德涅公主返回雅典,却在途中把她抛在一座孤岛上。由于他这一背信弃义的行为,他遭到了惩罚——胜利的喜悦冲昏了他的头脑,他居然忘记更换船上的黑帆!结果,站在海边遥望他归来的父亲看到那黑帆之后,认为儿子死掉了,便悲痛地投海而死。

  似乎我很小的时候就听过这个故事,随着时间的流逝,故事的梗概早已忘却,但那个神奇的迷宫却至今都记忆犹新。虽然不清楚当时的迷宫是怎样设计的,但是我们可以通过递归的方法让米诺斯的迷宫重现人间。

 1 # 迷宫矩阵2 maze = [3     [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],4     [0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1],5     [1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1],6     [1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1],7     [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1],8     [1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1],9     [1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1],
10     [1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1],
11     [1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1],
12     [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1],
13     [1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1],
14     [1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1],
15     [1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1],
16     [1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1],
17     [1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0],
18     [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
19 ]
20 def paint(maze):
21     ''' 打印迷宫 '''
22     for a in maze:
23         for i in a:
24             print('%4d' % i, end='')
25         print()
26
27 if __name__ == '__main__':
28     paint(maze)

  在矩阵中,用0表示通道,1表示墙壁,忒修斯王子可以在0之间任意穿行,矩阵迷宫的打印结果:

迷宫的数据结构

  虽然可以用0和1绘制出一个迷宫,但仍然属于手动编辑,我们的目标是寄希望于计算机,自动生成并绘制一个大型的迷宫:

  迷宫中有很多墙壁,再用0和1组成的简单矩阵就不合适了。如果将迷宫矩阵的每一个位置看作一个方块,则方块的上、下、左、右都可能有墙壁存在,这就需要对每个位置记录四面墙壁的信息:

  实际上没那么复杂,只要记录上墙和右墙就可以了,至于下墙和左墙,完全可以由相邻方块的上墙和右墙代替:

  当然,最后还要在四周套上一层边框:

  在生成迷宫时,每一个方块都需要记录三种信息:是否已经被设置、是否有右墙、是否有上墙。一个较为“面向对象”的方法是将方块信息设计成一个类结构,用三个布尔型属性来记录信息,但是这样做性价比并不高,一种更简单且高效的方式是用一个3位的二进制数来表示:

  矩阵中所有元素的初始值都设置为011,也就是方块未设置、有右墙和上墙;如果已经设置了某个方块,那么第3位被置为1,如此一来,每个方块可能会有5种状态:

  使用下面的代码设置一个8×8迷宫矩阵的初始状态:

 1 # 迷宫矩阵2 class MinosMaze:3     maze = []            # 迷宫矩阵4     n = 0                # 矩阵维度5     init_status = 0b011  # 初始状态,有上墙和右墙6     def __init__(self, n: int):7         ''' 初始化一个 n 维的迷宫 '''8         self.n = n9         # 初始化迷宫矩阵,所有方块未设置、有右墙、有上墙
10         self.maze = [([self.init_status] * n) for i in  range(n)]
11
12     def patin_maze(self):
13         for a in self.maze:
14             for i in a:
15                 print('%4d' % i, end='')
16             print()
17
18 if __name__ == '__main__':
19     m = MinosMaze(8)
20     m.patin_maze()

  我们使用拆墙法自动生成迷宫,这需要遍历迷宫矩阵中的每一个方格,设置是否拆除右墙或上墙。用递归的方法随机遍历上下左右四个方向,直到所有方向全部遍历完为止:

  四个的拆墙过程如下:

  1. 向上遍历,需要拆除当前方格的上墙;

  2. 向下遍历,需要拆除下侧方格的上墙;

  3. 向左遍历,需要拆除左侧方格的右墙;

  4. 向右遍历,需要拆除当前单元格的右墙。

1 class MinosMaze:
2     ……
3     def remove_wall(self, i, j, side):
4         ''' 拆掉maze[i][j] 的上墙或右墙 '''
5         if side == 'U':
6             self.maze[i][j] &= 0b110  # 拆掉上墙
7         elif side == 'R':
8             self.maze[i][j] &= 0b101  # 拆掉右墙

自动生成迷宫

  通过递归的方式遍历方格,迷宫矩阵的方格会逐一被设置:

 1 import random23 class MinosMaze:4     ...5     def create(self):6         ''' 自动创建迷宫 '''7         def auto_create(i, j):8             self.maze[i][j] |= 0b100    # maze[i][j] 已经被设置过9             # 当self.maze[i][j]的上下左右四个方向都是初始状态时,开始拆墙操作
10             while (i - 1 >= 0 and self.maze[i - 1][j] == self.init_status) \
11                     or (i + 1 < self.n and self.maze[i + 1][j] == self.init_status) \
12                     or (j - 1 >= 0 and self.maze[i][j - 1] == self.init_status) \
13                     or (j + 1 < self.n and self.maze[i][j + 1] == self.init_status):
14                 side = random.choice(['U', 'D', 'L', 'R'])   # 随机方向
15                 # 能够向↑走
16                 if side == 'U' and i - 1 >= 0 and self.maze[i - 1][j] == self.init_status:
17                     self.remove_wall(i, j, 'U')     # 拆除当前方格的上墙
18                     auto_create(i - 1, j)           # 向↑走
19                 # 能够向↓走
20                 elif side == 'D' and i + 1 < self.n and self.maze[i + 1][j] == self.init_status:
21                     self.remove_wall(i + 1, j, 'U') # 拆除下侧方格的上墙
22                     auto_create(i + 1, j)           # 向↓走
23                 # 能够向←走
24                 elif side == 'L' and j - 1 >= 0 and self.maze[i][j - 1] == self.init_status:
25                     self.remove_wall(i, j - 1, 'R') # 拆除左侧方格的右墙
26                     auto_create(i, j - 1)           # 向←走
27                 # 能够向→走
28                 elif side == 'R' and j + 1 < self.n and self.maze[i][j + 1] == self.init_status:
29                     self.remove_wall(i, j, 'R')     # 拆除当前单元格的右墙
30                     auto_create(i, j + 1)           # 向→走
31         auto_create(0, 0)   # 从入口位置开始遍历
32
33     def patin_maze(self):
34         ''' 打印迷宫数组 '''
35         for a in self.maze:
36             for i in a:
37                 print('%4d' % i, end='')
38             print()
39
40 if __name__ == '__main__':
41     m = MinosMaze(8)
42     m.create()
43     m.patin_maze()

  程序构造了一个8×8的迷宫,一种可能的结果是:

  矩阵元素的打印的结果是十进制整数,它和二进制的对应关系:

画出迷宫

  绘制迷宫的方法很简单,只需在坐标轴中画出每个方格的墙壁就好了:

 1 import random2 import matplotlib.pyplot as plt34 class MinosMaze:5     ……6     def paint(self):7         # 绘制迷宫内部8         for i in range(self.n):9             for j in range(self.n):
10                 # 有右墙
11                 if self.maze[i][j] & 0b010 == 0b010:
12                     # 右墙的坐标
13                     r_x, r_y = [j + 1, j + 1], [self.n - i, self.n - i - 1]
14                     plt.plot(r_x, r_y, color='black')
15                 # 有上墙
16                 if self.maze[i][j] & 0b001 == 0b001:
17                     # 上墙的坐标
18                     u_x, u_y = [j, j + 1], [self.n - i, self.n - i]
19                     plt.plot(u_x, u_y, color='black')
20
21         plt.axis('equal')
22         ax = plt.gca()
23         ax.spines['top'].set_visible(False)
24         ax.spines['right'].set_visible(False)
25         plt.show()

  看起来不那么像迷宫,这是由于没有添加边框,因此还需要在paint()方法中加上最后的完善工作:

 1 def paint(self):2     ……3     plt.plot([0, self.n], [self.n, self.n], color='black')   # 上边框4     plt.plot([0, self.n], [0, 0], color='black')             # 下边框5     plt.plot([0, 0], [0, self.n], color='black')             # 左边框6     plt.plot([self.n, self.n], [0, self.n], color='black')  # 右边框78     # 设置入口和出口9     entrance, exit = ([0, 0], [self.n, self.n - 1]), ([self.n, self.n], [0, 1])
10     plt.plot(entrance[0], entrance[1], color='white')
11     plt.plot(exit[0], exit[1], color='white')
12
13     plt.axis('equal')
14     ax = plt.gca()
15     ax.spines['top'].set_visible(False)
16     ax.spines['right'].set_visible(False)
17     plt.show()

  出口的位置在迷宫的右下角,由于创建迷宫时遍历了所有方格,因此出口方格一定是从它上侧或左侧的方格遍历而来的,这意味着它一定没有上墙或左墙,拆掉它的右边框一定能够成为出口。现在可以终于可以绘制出一个完整的迷宫了:

  米诺斯的迷宫复杂的多,也许一个32×32的设计图可以困住怪兽:

  


   作者:我是8位的

  出处:http://www.cnblogs.com/bigmonkey

  本文以学习、研究和分享为主,如需转载,请联系本人,标明作者和出处,非商业用途!

  扫描二维码关注公众号“我是8位的”

递归的逻辑(5)——米诺斯的迷宫相关推荐

  1. C++题目:米诺斯的迷宫

    米诺斯的迷宫 题目背景 传说米诺斯王在克里特岛为米诺陶洛斯(Minotaur,"米诺斯的牛")修建了一个迷宫(Labyrinth).而你想找到米诺陶洛斯,然后杀掉他,从而成为英雄. ...

  2. 周伯通的空明拳,米诺斯的星尘傀儡线,SAP Kyma的Serverless

    Jerry一直认为,金庸的<天龙八部>里的武学建模已经有点脱离传统武侠小说的范畴了,像已经走上玄幻道路的灵鹫宫"八荒六合唯我独尊功",以及杀伤力足够能被视为现代激光武器 ...

  3. 采用递归与栈结合的方式实现迷宫分析与走迷宫(python3)

    一.场景 1. General presentation 2.Examples The file named maze_1.txt has the following contents. 二.题目分析 ...

  4. 递归的逻辑(1)——递归关系模型

    查尔斯·巴贝奇是一名19世纪的英国发明家,也被说成是职业数学家.他曾经发明了差分机--一台能够按照设计者的意图,自动处理不同函数的计算过程的机器.这是一台硕大的.泛着微光的金属机器,包括数以千计加工精 ...

  5. 父亲节,来认识这些计算机领域的大佬们

    导读:父亲节,华章妹想说说几位被称为"之父"的大佬. 01 信息论之父:克劳德·香农 克劳德·香农(Claude Elwood Shannon,1916年4月30日-2001年2月 ...

  6. 欧洲5000年文明史,到底是怎么来的?

    近代以来,随着欧洲逐渐崛起之后,便开始寻找他们"辉煌的历史",由此证明欧洲文明的优越性,与领先其他文明.成为世界中心的深刻历史原因.十九世纪至二十世纪,经过百余年的考古探索,欧洲学 ...

  7. MIT的《深度学习》精读(2)

    看完了"皮格马利翁"和"伽拉泰亚(Galatea)"的故事,已经很让人感动,接着下来,又来看看"代达罗斯"和"塔罗斯(Talos) ...

  8. 如何交互可视化你的卡片式笔记网络?

    王树义 读完需要 13 分钟 速读仅需 5 分钟 会当凌绝顶,一览众山小. 1 痛点 在<如何高效实践卡片式写作?>和<如何用卡片法写论文?>两篇文章中,我为你详细介绍了卢曼的 ...

  9. java递归老鼠走迷宫_老鼠走迷宫----------递归问题

    老鼠走迷宫是一个典型的递归的问题,写几个这样的题才可以充分理解递归的过程. 写递归的过程有几点需要注意: (1)递归结束的条件 (2)递归过程的脉络,即逻辑要清晰. / // // 在迷宫中显示老鼠能 ...

最新文章

  1. PyTorch的十七个损失函数
  2. 人脸识别技术及其应用领域
  3. 前沿|Google AI提新型神经网络,对神经元进行高精度自动重建
  4. vs2017开发php,C#编写的可供PHP调用的com dll(Visual studio 2017)
  5. SVN钩子--hook
  6. 如何假装自己读懂了《时间简史》
  7. python 获取向上两级路径_Python学习第171课--相对路径和绝对路径
  8. jQuery的实现原理和核心
  9. OpenCV图像、矩阵、数组介绍
  10. 【小白学前端】化腐朽为神奇-Bootstrap实现表单美化(day02-6)
  11. ApacheCN 活动汇总 2019.8.9
  12. 老司机必备-安卓+PC磁链下载播放工具
  13. 2018年最值得投资的十大行业版图
  14. GetLastErr返回值ErrCode的宏定义以及含义
  15. 股票爆仓应该具体怎么办呢?股票爆仓常见的危险是什么?
  16. 除了支付宝,微信也能查询账单了!再也不担心钱花哪去了!
  17. Druid在有赞的实践
  18. 版本控制Git 黑马尚硅谷
  19. GIS系列(三)几种互联网地图服务背后的解读(WMS,WFS,WMTS,TMS)
  20. dll.a和lib 引用MinGW生成的dll.a后出现的问题

热门文章

  1. 在jsp页面如何获取servlet请求中的参数的办法
  2. 高标准,硬实力,vivo如何保卫移动终端数据安全?
  3. Photoshop滤镜打造水晶放射线
  4. javascript实现 早上好,中午好代码
  5. 关于C++模板函数声明与定义的问题
  6. QQ输入正确密码却验证错误的解决办法:)
  7. Android平台的一些常用命令
  8. Kotlin笔记(七)——委托属性(Delegated Properties)
  9. 下次不敢《童梦奇缘》片尾曲铃声 下次不敢《童梦奇缘》片尾曲...
  10. 浏览器console终端缓存清空