迷宫问题python

问题:

随机产生一个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)

运行结果:

猜你喜欢

转载自blog.csdn.net/noingw96/article/details/84647111