• 路径规划-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*寻路是一种广度优先搜索?

  • 相关阅读:
    CGAffineTransform的使用大概:
    CYLTabBarController的简单使用
    Masonry详解
    Swift学习 (四)
    《Advanced Bash-scripting Guide》学习(二):测试脚本调用的参数是否正确
    《Advanced Bash-scripting Guide》学习(一):对一个增强和广义的删除logfile的脚本的理解
    [JSOI2007]字符加密Cipher
    codevs 3160 最长公共子串
    codevs 1500 后缀排序
    浅谈后缀数组
  • 原文地址:https://www.cnblogs.com/yanshw/p/11176288.html
Copyright © 2020-2023  润新知