问题:
随机产生一个n阶的迷宫,障碍用 * 表示,通路使用 0 表示,给定起点和终点,要求给出起点到终点的通路(完整代码在最下方,如有需要可以直接拉至最下方)
解题思路:
第一步、需要产生一个n阶的迷宫maze,先构建一个n * n的二维数组,全赋值为0。之后我们可以考虑先给迷宫加上外围一圈的障碍,若 i 表示迷宫的行,j 表示迷宫的列,那么我们令i = 0 ,i = n-1,j = 0,j = n-1的情况下将数组中的值改为 * (注意下标是从0开始的),这样就产生了迷宫的围栏。
maze = [([0]*n) for i in range(n)] #初始化一个n阶二维数组
for i in range(n):
for j in range(n):
if i==0 or i==n-1 or j==0 or j==n-1: #迷宫周围设障碍
maze[i][j]="*"
接下来我们需要给迷宫设立障碍,这种障碍需要每次运行程序都应该是不一样的,我们需要用到Python的random函数。在遍历矩阵的同时,我们设置一个rate,如果随机的数大于rate,则将该矩阵元素的值改为 * 。
rate=0.7 #设置非障碍的概率
if random.random()>rate:
maze[i][j]="*"
如图,我们生成了一个10*10随机矩阵,可以调整rate的值,来改变障碍的出现的比率。
第二步、需要给迷宫设置起点和终点,我们选取偏左上角的点为起点,右下角的点为终点。以起点为例(起点与终点类似),可以从i = 1,j = 1开始遍历,如果该点不设障碍,我们就将该点设为起点。
for i in range(1,len(maze)-1): #从左上角开始寻找,若为非障碍则为起点
for j in range(1,len(maze[i])-1):
if maze[i][j]==0:
new_start=[i,j]
print("起点:",new_start)
return new_start
第三步、我们开始设计如何让程序模拟找寻迷宫出路的过程。
设立一个road作为栈,road[-1]里的元素即为栈顶元素,我们需要将走过的点的坐标进行压栈。每次对栈顶的元素进行分析,选定下一个行走的方向,共有上下左右四个方向,本程序中以右、下、左、上为序进行选择路向(即向右走不通就选择下,以此类推),道路不通的情况分为两种,我们举例的时候用point代替下一个要到达点的坐标。
(1)下一通路point设有障碍则无法进行,以向右走遇到障碍为例,找到向右走的坐标,对其进行判断是否存在障碍,如果该矩阵元素的值不等于 * ,则返回该点坐标,否则返回None。
a = road[-1][0] # 往右走的坐标
b = road[-1][1] + 1
if maze[a][b] != "*":
return [a, b]
return None
(2)下一通路已经走过,如果程序不进行这一项的判断,若程序存在环路,则为无限地运行下去。我们采取的方法是另外建立一个list名为old_road,将过程中已经走过的路进行保存。当下一步的路不是障碍(即不是第一种情况)再进行此判断,将point与old_road中的元素进行比对,如果存在,则说明该点已经走过,不能将这一点进行压栈。
def is_old(next_node,oldlist):
for i in range(len(oldlist)):
if next_node == oldlist[i]:
return 0
return 1
很明显,当我们将栈顶元素进行完四个方向的分析,若都没有可以到达的下一个点,说明无法从栈顶元素找到有效的通路,此时可以抛出栈顶元素(为了之后程序的方便,抛出的元素都更改为 * )。
最后,我们会将整个过程放在循环里,并设立了两种终止条件。第一种,当栈顶元素等于终点坐标时,说明我们的栈中已经存储了整个通路的坐标;第二种,栈为空,说明在整个回溯过程中,可以前进的通路都尝试过,未找到通路。
回溯思想:
从算法的实质来说,回溯法的核心仍然是蛮力法,准确的说是蛮力法的改进版,但是在搜索遍历过程中会针对已有的结果减少不必要的搜索过程,从而提高搜索效率。回溯思想有三个关键词:栈、序、剪枝。
所谓回溯,需要利用栈来存取每一步的过程,进行深度优先遍历过程。当栈顶的方案不可行时,我们仍然可以找寻到上一项的内容进行探索。序值得是我们在找寻可行性方案时的一个顺序,在迷宫问题中,我们的序是右、下、左、上。序人为设定的,以此来顾及到可行性方案中每一个点都被我们访问到。剪枝就是当遇到障碍时,以该点坐标为栈顶的所有方案都无需考虑,从而减少了程序的计算量。
完整代码:
最后进行封装,将部分内容进行完善,并加上注释,可执行程序如下,如有其它见解,欢迎分享!
# coding:utf-8
import random
def createMaze(n):
maze = [([0]*n) for i in range(n)] #初始化一个n阶二维数组
for i in range(n):
for j in range(n):
if i==0 or i==n-1 or j==0 or j==n-1: #迷宫周围设障碍
maze[i][j]="*"
else:
rate=0.7 #设置非障碍的概率
if random.random()>rate:
maze[i][j]="*"
return maze
def createStart(maze):
for i in range(1,len(maze)-1): #从左上角开始寻找,若为非障碍则为起点
for j in range(1,len(maze[i])-1):
if maze[i][j]==0:
new_start=[i,j]
print("起点:",new_start)
return new_start
def createEnd(maze):
for i in range(len(maze)-1,1,-1):#从右下角开始寻找,若为非障碍则为终点
for j in range(len(maze[i])-1,1,-1):
if maze[i][j]==0:
new_end=[i,j]
print("终点:",new_end)
return new_end
def nextNode(maze,road,i):
if i==1:
a = road[-1][0] # 往右走的坐标
b = road[-1][1] + 1
if maze[a][b] != "*":
return [a, b]
return None
if i==2:
a = road[-1][0] + 1 # 往下走的坐标
b = road[-1][1]
if maze[a][b] != "*":
return [a, b]
return None
if i==3:
a = road[-1][0] # 往左走的坐标
b = road[-1][1] - 1
if maze[a][b] != "*":
return [a, b]
return None
if i==4:
a = road[-1][0] - 1 # 往上走的坐标
b = road[-1][1]
if maze[a][b] != "*":
return [a, b]
return None
def is_old(next_node,oldlist):#判断该点是否走过(改进版 )
for i in range(len(oldlist)):
if next_node == oldlist[i]:
return 0
return 1
def is_old2(next_node,the_road):#判断该点是否走过
if len(the_road)>2 and next_node == the_road[-2]:
return 0
return 1
def findRoad(maze,begin,stop):
road=[] #将road作为栈,road[-1]代表栈顶
old_road=[]
road.append(begin) #将起点入栈
old_road.append(begin)
# if nextNode(maze,road)==None:#判断起点是否是孤立点
# return None
# road.append(nextNode(maze, road))
while road != []:
if road[-1] == stop:
return road
tag=1 #判断标识
while tag!=5:
next_node = nextNode(maze, road, tag) # 寻找下一个点
if next_node == None or is_old(next_node,old_road)==0:#若下一点走过了或要碰壁了尝试别的方向
tag+=1
else:
road.append(next_node)
old_road.append(next_node)
tag = 1
break
if tag == 5:#若路都不通,栈顶元素抛出
a=road[-1][0]
b=road[-1][1]
maze[a][b]="*"
road.pop(-1)
# next_node=nextNode(maze,road)#寻找下一个点
# if next_node == road[-2]:
# road.pop(-1)
# else:
# road.append(next_node)
return road
def printMaze(maze):
for i in range(len(maze)):
for j in range(len(maze[i])):
print(maze[i][j]," ",end="")
print("")
if __name__=="__main__": #注意:本程序设置的size是除去外围障碍的阶数
size=10
new_maze = createMaze(size+2) #创建迷宫
printMaze(new_maze) #打印迷宫
start = createStart(new_maze) #定义起点
end = createEnd(new_maze) #定义终点
the_road = findRoad(new_maze,start,end)#寻找出路
if the_road == []:
print("无路可走")
else:
print(the_road)
运行结果: