路径规划-A*算法及其优化

在学习 A* 之前,建议先学习下 Dijkstra 算法

A* 原理

详见参考资料

算法原理没有什么难度,静下心来,你肯定能看懂,时间关系,我就简写了

A* 进阶

A* 算法大概包含两个基础算法:

基础1-启发式搜索

在 已知 起点 s 到 所有当前点(openlist)的距离 g 时,如何选择哪个当前点作为行走目标,用到了启发式搜索(或者叫 贪心策略),即

F = g + h

这个选择 并不能保证路径 全局最优,因为 h 仅仅是估计

基础2-动态规划

动态规划保证了 g 是最优的;      

假设 s_1,s_2,s_3...s_n-1,s_n 为最短路径,那么 s_1,s_2,s_3...s_n-1 也一定是最短路径,因为如果存在 s_x 使得 s_1,s_2,s_x...s_n-1 更短,那么 s_1,s_2,s_3...s_n-1,s_n 就不是最短路径;

也就是说 A* 保证了每走一步,从起点到当前点的路径是最短的;

照这个思路,从 起点 走到 终点 的路径 是 全局最优 的;

启发函数

启发函数会影响A*算法的行为。

  • 在极端情况下,当启发函数h(n)始终为0,则将由g(n)决定节点的优先级,此时算法就退化成了Dijkstra算法。
  • 如果h(n)始终小于等于节点n到终点的代价,则A*算法保证一定能够找到最短路径。但是当h(n)的值越小,算法将遍历越多的节点,也就导致算法越慢。
  • 如果h(n)完全等于节点n到终点的代价,则A*算法将找到最佳路径,并且速度很快。可惜的是,并非所有场景下都能做到这一点。因为在没有达到终点之前,我们很难确切算出距离终点还有多远。
  • 如果h(n)的值比节点n到终点的代价要大,则A*算法不能保证找到最短路径,不过此时会很快。
  • 在另外一个极端情况下,如果h(n)相较于g(n)大很多,则此时只有h(n)产生效果,这也就变成了最佳优先搜索。

由上面这些信息我们可以知道,通过调节启发函数我们可以控制算法的速度和精确度。因为在一些情况,我们可能未必需要最短路径,而是希望能够尽快找到一个路径即可。这也是A*算法比较灵活的地方。

A* 是一种带有启发性质的深度优先搜索,同时具有 死路回退 的性质,总体来讲,它是 广度优先+深度优先

算法优化

其实有很多小操作,有空再补充吧;

这里主要记录几点:

1. 最小二叉堆

2. Lazy Theta    【详见参考资料】

示例代码

tm = [
'############################################################',
'#..........................................................#',
'#.............................#............................#',
'#.............................#............................#',
'#.............................#............................#',
'#.......S.....................#............................#',
'#.............................#............................#',
'#.............................#............................#',
'#.............................#............................#',
'#.............................#............................#',
'#.............................#............................#',
'#.............................#............................#',
'#.............................#............................#',
'#######.#######################################............#',
'#....#........#............................................#',
'#....#........#............................................#',
'#....##########............................................#',
'#..........................................................#',
'#..........................................................#',
'#..........................................................#',
'#..........................................................#',
'#..........................................................#',
'#...............................##############.............#',
'#...............................#........E...#.............#',
'#...............................#............#.............#',
'#...............................#............#.............#',
'#...............................#............#.............#',
'#...............................###########..#.............#',
'#..........................................................#',
'#..........................................................#',
'############################################################']


class Node(object):
    def __init__(self, x, y, parent):
        self.x = x
        self.y = y
        self.parent = parent    # xy坐标,parent父节点


class Astar(object):
    def __init__(self, x1, y1, x2, y2):
        self.x1, self.y1 = x1, y1   # 起点坐标
        self.x2, self.y2 = x2, y2   # 终点坐标
        self.openlist = []      # 下一步可以走的路
        self.closelist = []     # 已经走过的路

    def isinlist(self, nodelist, node):
        for i in nodelist:
            if i.x == node.x and i.y == node.y:
                return i
        return

    def dist(self, x1, y1, x2, y2):
        if x1 == x2 or y1 == y2:
            return 1
        else:
            return 1.4

    def t(self):
        s = Node(self.x1, self.y1, None)    # 起点
        s.dist = 0

        while True:
            # 当前节点的邻接点
            sx = (-1, 0, 1, -1, 1, -1, 0, 1)
            sy = (-1, -1, -1, 0, 0, 1, 1, 1)
            for x, y in zip(sx, sy):
                new_x, new_y = s.x + x, s.y + y

                # 判断邻接点是否可走:不是障碍物; 不在关闭列表内。
                # 如果可走,加入开放列表
                if tm[new_y][new_x] == '#': continue                    # 障碍物不做任何处理

                new_node = Node(new_x, new_y, s)
                new_node.dist = s.dist + self.dist(s.x, s.y, new_node.x, new_node.y)

                if self.isinlist(self.closelist, new_node): continue    # 在关闭列表内不做任何处理

                # 可行就加入开放列表
                isin = self.isinlist(self.openlist, new_node)
                if isin:                            # 已经在开放列表,检测更新g h 父节点
                    if new_node.dist < isin.dist:
                        isin.dist = new_node.dist
                        isin.parent = new_node.parent
                else:
                    # 不在开放列表直接添加
                    self.openlist.append(new_node)

            # 结束循环
            if not self.openlist: return
            if self.isinlist(self.openlist, Node(self.x2, self.y2, s)):
                return s

            # 找出F最小的节点
            ### 注意 F 最小的节点 和 当前节点 s 并无关系
            minf = None
            min_node = None
            for i in self.openlist:
                value = i.dist + (abs(i.x - self.x2) + abs(i.y - self.y2))
                if minf == None:
                    minf = value
                    min_node = i
                elif value < minf:
                    minf = value
                    min_node = i
                else:
                    pass
            print(min_node.parent.x, min_node.parent.y, s.x, s.y)

            # 将F最小的节点从开放列表移除,并加入关闭列表
            self.openlist.remove(min_node)
            self.closelist.append(min_node)

            # 起点更新为该节点
            s = min_node

    def search(self):
        s = []
        for i in self.openlist:
            s.append((i.x, i.y))
        for i in self.closelist:
            s.append((i.x, i.y))
        return s

def path(s):
    mypath = []
    while s.parent:
        mypath.insert(0, (s.x, s.y))
        s = s.parent
    return mypath

def find_point(s):
    for indy, row in enumerate(tm):
        for indx, column in enumerate(row):
            if column == s:
                return indx, indy

def print_path(path, search):
    lenx = len(tm[0])
    leny = len(tm)
    newtm = []
    for i in range(leny):
        row = ''
        for j in range(lenx):
            s = tm[i][j]
            if (j, i) in search:
                s = ' '
            if (j, i) in path:
                s = 'X'
            row += s
        newtm.append(row)
    return '
'.join(newtm)


if __name__ == '__main__':
    x1, y1 = find_point('S')
    x2, y2 = find_point('E')
    print(x1, y1, x2, y2)
    astar = Astar(x1, y1, x2, y2)
    s = astar.t()
    print(s.x, s.y)

    search = astar.search()

    mypath = path(s)
    print(mypath)

    print(print_path(mypath, search))

输出

############################################################
#                             X                   .........#
#                            X#X                  .........#
#                           X # X                 .........#
#                          X  #  X                .........#
#        XXXXXXXXXXXXXXXXXX   #   X                ........#
#                             #    X               ........#
#                             #     X              ........#
#                             #      X              .......#
#                             #       X             .......#
#                             #        X            .......#
#                             #         X            ......#
#                             #          XXXXXX      ......#
####### #######################################X     ......#
#....#        #....................           X       .....#
#....#        #....................           X       .....#
#....##########....................           X       .....#
#..................................           X       .....#
#.................................            X        ....#
#.................................            X        ....#
#.................................            X        ....#
#.................................            X        ....#
#...............................##############X        ....#
#...............................#.......   ..#X        ....#
#...............................#....... X  .#X        ....#
#...............................#.......  X  #X      ......#
#...............................#........  X #X     .......#
#...............................########### X#X    ........#
#..........................................  X   ..........#
#...........................................   ............#
############################################################

参考资料:

https://zhuanlan.zhihu.com/p/54510444  路径规划之 A* 算法

http://www.cppblog.com/christanxw/archive/2006/04/07/5126.html  A* 寻路算法

https://blog.csdn.net/h348592532/article/details/44421753  最短路径A*算法原理及java代码实现

https://www.cnblogs.com/yangrouchuan/p/6373285.html  A*算法改进——Any-Angle Path Planning的Theta*算法与Lazy Theta*算法

https://blog.csdn.net/qq_42549774/article/details/103979874?utm_medium=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~default-14.control&dist_request_id=&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~default-14.control      人工智能导论 A*算法推导证明

https://www.zhihu.com/question/39866705/answer/1672207891    为什么A*算法一定能找到最优解?

https://www.zhihu.com/question/436975755/answer/1650953540  在启发式允许的范围内,为什么A*算法是最优的?

https://www.zhihu.com/question/35311316  A*寻路是一种广度优先搜索?

原文地址:https://www.cnblogs.com/yanshw/p/11176288.html