A*算法解决迷宫寻址问题

最近在想作一个迷宫小游戏看成本身的期末大做业,其中有一部分电脑敌人须要自动寻找玩家所在位置,思来想去想了几种方法,可是因为都不够好,因此一直被卡住了。后来上了人工智能导论这门课,了解到A*算法对于实现电脑机器人自动寻址很是的巧妙,所以查阅大量资料,写下这篇文章。

1、什么是A*算法?node

在解决迷宫寻址问题以前,我先带你们了解一下A*算法:

A*算法是一种启发式搜索算法,它不需遍历全部节点,只是利用包含问题启发式信息的评价函数对节点进行排序,使搜索方向朝着最有可能找到目标并产生最优解的方向。它的独特之处是检查最短路径中每一个可能的节点时引入了全局信息,对当前节点距终点的距离作出估计,并做为评价节点处于最短路径上的可能性度量。python

A*算法中引入了评估函数,评估函数为:f(n)=g(n)+h(n),其中:n是搜索中所处的任意状态。g(n)是从起点到任意一个n节点的移动耗费。h(n)是对n到目标状态代价的启发式估计。即评估函数f(n)是从初始节点到达节点n处已经付出的代价与节点n到达目标节点的接近程度估价值的总和。咱们一般会定义n点到目标点的最小实际距离为h(n)*,A*算法要知足的条件为:h(n)<=h(n)*。算法

2、为何使用A*算法?数组

举个简单的例子来讲明一下为何要使用A*算法。假设有一个这样的地图:app

0dom

0函数

0学习

0阿里云

0人工智能

0

0

0

0

0

0

1

0

0

0

0

0

A

0

1

0

B

0

0

0

0

0

1

0

0

0

0

0

0

0

0

0

0

0

0

0表明能够经过,1表明障碍物,你如今须要从A点到达B点。若是采用迪杰斯特拉算法的话,首先会把A周围能够走的四个方格所有遍历一遍,而后又重复第一步的步骤把前一步骤遍历过的格子周围的格子遍历一遍,一直这样重复,最后能够遍历到B点,找到一条到达B点最短的路径,可是因为遍历的次数太多,消耗的时间和资源也会增长,虽然地图简单的话不会又很明显的影响,可是若是地图变得很复杂,那么这种感受尤其明显。看一下下面下图就会有这种感受了(图片来自阿里云栖社区paulquei)


再看一下最佳优先搜索算法(BFS),他是一种启发式算法,他会始终朝着距离终点最近得点走下去,而不像迪杰斯特拉算法把全部可能的点都遍历一遍,就拿本例来讲,由于B点相对于A点在右边,因此右方向的优先度更高,而后它就直接往右走,当碰到障碍物它就往下或者上走,绕过障碍物后继续沿着B的方向走,最后它能够找到一条到达B的路径,就本例来讲,它能够找到一条最短路径且时间比迪杰斯特拉算法短了不少,可是若是把地图变得复杂一点,它就可能没法找到最短路径。看一下下面下图就会有这种感受了(图片来自阿里云栖社区paulquei)



最后再说一下A*算法的思路,相比于迪杰斯特拉算法和最佳优先搜索(BFS)算法,A*算法更像是它们的结合体。A*算法首先会遍历它周围能够走的方格,而后再从已经遍历的方格中找出一个距离目标点最近的方格,而后重复前一步骤。就本例题而言,咱们先假设有f(n)=g(n)+h(n),该公式在上文中已经提到过。A首将本身标记为已走过的方格并先判断它周围的四个格子都可以走,而后把这四个格子标记为已遍历,同时计算出他们的f(n),g(n),h(n)的值,以第一步为例,四个方向的g(n)=1,往上的h(n)=5,f(n)=6,往下的h(n)=5,f(n)=6,往右的h(n)=5,f(n)=6,往左的h(n)=3,f(n)=4。而后选择f(n)最小也就是距离终点最短的点标记为走过的路径,而后重复这一步骤,直到找到一条最短路径。A*算法相对于前面两种算法,不只能够准确的找到最短路径,并且还能使搜索时间大大缩短。看一下下面下图就会有这种感受了(图片来自阿里云栖社区paulquei)


3、A*算法解决迷宫寻址问题的思路?

本实验以人工智能导论(王万良 第四版)后面实验五为对象,讲解A*算法的实现迷宫寻路思路:

假设地图为map[[0,0,0,0,],[1,0,1,0,1],[0,0,1,1,1],[0,1,0,0,0],[0,0,0,1,0]]。首先,咱们须要定义未判断过的数组openList,判断过的数组closeList,存储当前节点可达邻居节点的数组neighbors,变量F,G,H。F为地点到终点曼哈顿距离,G为起点到任意一点n的距离,H为n到终点的估价距离。而后开始咱们的算法步骤,首先将起始节点加入到openList中,而后循环判断openList中距离终点的曼哈顿距离最小的节点(即F值最小的结点,F=G+H),而后将其移出openList并放进closeList,表明此时这个格子已经检查过了。而后再找出当前格子四周能够到达的格子,用neighbors数组存放当前节点的邻居节点并判断邻居节点是否已存在于openList或closeList中,若是不存在则将它们放入openList,而后计算出相应的F,G,H值,并将当前节点记录为它们的父节点。而后再比较当前节点子节点的F值,把F值最小的那个移出openList,同时放入closeList。以实验五为例,当走到这步时候,openList包含了(0,1),closeList包含了(0,0),(0,1)。这时咱们再继续重复前面的步骤,直到最后openList中包含了终点位置,这时咱们会找到一条最短的可达路径:

(0,0)->(0,1)->(1,1)->(2,1)->(2,0)->(3,0)->(4,0)->(4,1)->(4,2)->(3,2)->(3,3)->(3,4)->(4,4)

固然,这是理想状态下的迷宫,它的路线包含在colseList中,还有一种无解的迷宫,无解的判断条件是当openList中全部节点用尽时,若是在colseList中没法找到终点节点,则返回迷宫无解。

4、A*算法实现迷宫寻址具体实现代码?

#定义随机生成地图 实验五未用到此方法 故注释
# def creatMap(x):
# print('hello')
# map = []
# print("Map is here:")
# for i in range(x):
# map.append([])
# for j in range(x):
# map[i].append(random.randint(0,1))
# if j==x-1:
# print(map[i][j])
# else:
# print(map[i][j],end="")
# map[0][0] = 0
# map [x-1][x-1] = 0
# return map
#生成节点class Node(object): 
#初始化节点
    def __init__(self, now, begin, end):
        super(Node, self).__init__()
        self.m_own = now
        self.m_g = 0
        self.m_h = abs( end[1] - now[1] ) + abs( end[0] - now[0] )#计算H值
        self.m_f = self.m_g + self.m_h
        self.m_begin = begin
        self.m_end = end
        self.m_flag = str( now )
        self.m_neighbors = []#用来存储当点节点的邻居节点
        self.m_parent = None
        #返回当前节点的字符串形式
    def get_flag(self):
        return self.m_flag
    #获得F值
    def get_fvalue(self):
        return self.m_f
    # 获得当前节点
    def get_position(self):
        return self.m_own
    #把邻居节点设为当前节点的父节点
    def set_parent(self, parentN):
        self.m_parent = parentN
    #获得父节点
    def get_parent(self):
        return self.m_parent
    #调用方法获得父节点
    parentN = property( get_parent, set_parent )
     # 设置G值
    def set_gvalue(self, g):
        self.m_g = g
    #获得G值
    def get_gvalue(self):
        return self.m_g
    #调用上面方法设置并得G值
    gvalue = property( get_gvalue, set_gvalue )
    #获得当前格子的相邻的格子
    def get_neighbors(self):
        #neb为上下左右四个方向,数组值为当前节点移动距离,例如[0,1]说明当前节点向右移动一格
        neb = [[0,1],[0,-1],[1,0],[-1,0]]
        #循环遍历当前节点四周节点
        for item in neb:
            x = item[0] + self.m_own[0]
            y = item[1] + self.m_own[1]
            if (x >= 0 and x < map_size) and (y >= 0 and y < map_size):
                if map[x][y] == 0:
                    self.m_neighbors.append( Node( (x, y), self.m_begin, self.m_end ) )
        return self.m_neighbors
#寻路函数
def aStarSearch( begin, end ):
    # 定义 未判断数组openList 已判断数组colseList
    openlist = []
    closelist = []
    #把起点加入open list
    begin_node = Node( begin, begin, end )
    begin_node.gvalue = 0
    openlist.append( begin_node )
    #主循环,每一轮检查一个当前方格节点
    while len(openlist) > 0:
        #在openlist中查找F值最小的节点做为当前方格节点
        _min = findMinNode( openlist )
        #当前方格节点从openlist中移除,进入closelist,表明这个格子已到达并检查过了。
        openlist.remove( _min )
        closelist.append( _min )
        #找到当前格上下左右全部可到达的格子,看它们是否在Openlist和Closelist中,若是不在,加入Openlist,计算出相应的G、H、F值,并把当前格子做为它们的“父节点”
        neighbors = _min.get_neighbors()
        for item in neighbors:
            if not isContains( openlist, item ) and not isContains( closelist, item ):
                item.parentN = _min
                item.gvalue = _min.gvalue + 1
                openlist.append( item )
        #若是终点在openlist中,直接返回终点格子
        for x in openlist:
            if x.get_flag() == str(end):
                return x
    #openlist用尽,仍然找不到终点,说明终点不可到达,返回空
    return None
#找到并返回openList中起点距离终点最近的点并返回
def findMinNode( _list ):
    _min = _list[0]
    for x in _list:
        if x.get_fvalue() < _min.get_fvalue():
            _min = x
    return _min
#判断节点是否在openList或closeList中
def isContains( _list, _node ):
    for item in _list:
        if item.get_flag() == _node.get_flag():
            return True
    return False
# 定义地图
map = [
[0,0,0,0,0],
[1,0,1,0,1],
[0,1,1,1,1],
[0,1,0,0,0],
[0,0,0,1,0]
]
map_size = 5
dst= aStarSearch((0,0),(4,4))
# 异常处理 若是迷宫无解则dst最后一个节点为None,它将不会有get_flag方法,发生异常,此时对异常进行处理
try:
    path = [dst.get_flag()]
    while dst.parentN:
        dst = dst.parentN
        path.insert( 0, dst.get_flag() )
    print( path )
except AttributeError:
    print('该迷宫无解!')
    pass复制代码


代码运行结果示例:

地图:map = [

[0,0,0,0,0],
[1,0,1,0,1],
[0,1,1,1,1],
[0,1,0,0,0],
[0,0,0,1,0]]


地图:map = [

[0,0,0,0,0],
[1,0,1,0,1],
[0,0,1,1,1],
[0,1,0,0,0],
[0,0,0,1,0]]


第一次写文章,但愿你们一块儿相互学习,共同进步,有不对的地方请各位大佬悉心指导!

相关文章
相关标签/搜索