A 星路径搜索 算法 (python版)

本文详细介绍了A星路径搜索算法的工作原理,通过比喻生活中的小叶榕树根须生长来形象解释其寻找最短路径的过程。算法使用openlist和closelist数据结构,以及F值计算来确定节点的探索顺序。通过Python实现展示了A星算法的具体步骤,包括节点探索、路径成本计算、节点添加与更新等。在示例中,算法成功找到了从起点到终点的最短路径,并输出了路径信息和总代价。
摘要由CSDN通过智能技术生成

A 星路径搜索 算法

  • 算法描述

用白话讲,这个过程就是 从A点发出的光在经过很多 蜿蜒曲折的过程,最终有一个最先到达的光束。这个光束就是最短路径信息;


生活中见过小叶榕树吧?每每下雨之后就会长出很多根须。如果类比就是从某一点 长出了很多很多的根须,他的目标是到达土里进行养分汲取。 在这个过程中可能会遇到墙就只有转弯等绕过;其中最先到达地面的就是一条最短路径

PS:

上述2个描述,为个人总结。为便于理解所构思的内容

数据结构描述

  1. 开辟openlist closelist 用于记录;
    openlist 表示 当前可以访问到的节点,其中节点已经计算出F
    closelist 表示 搜索路径中已经到过的节点

  2. 从某一节点出发,分别 上 下 左 右 左上 右上 左下 右下 共8个位置可以进行探索。就类比与九宫格从中心点出发到各个点所需花费的路径为 (10-上 下 左 右) (14 左上 右上 左下 右下)10 和 14 是为方便计算和表示的一个近似值,准确的应该是 1 和 根号2(约等于1.4…) 的值 。这个从某一节点出发的值 称为 G 值

  3. 某一节点 X 距离 终点 可以横冲直撞所需的最小 花费值为 H 值。这里的横冲直撞就是不考虑所经过的节点是否是不能通过等其它限制因素。通常 H 值为 节点X到终点的 坐标差值的绝对值.例如:X(1,2) 终点 (8,8) 则 H值为 H = abs(8-1) + abs(8-2) = 7+6=13

  4. 某一点的所需要耗费的 总值F 等于 G+H 的数值,若某一点的F值很小说明从起点出发到该点的花费很小反之则大。我们应选择F值小的节点进行探索

程序执行步骤

  • A . 将起点加入到openlist中

  • B. 若openlist 中存在节点,则取出openlist中F值最小的一个节点M 开始探索

  • C. 若取出该节点M为终点则跳出 循环,处理路径信息

  • D. 开始从该节点M探索该节点M可达周围节点进行 探索;

    a. 计算节点M到达探索节点所需耗费的F值信息

    b.

    b1. 设置探索节点的父节点为 M,并将该节点 添加到openlist中去

    b2. 若该探索的节点已经存在openlist中时,若该节点的F值小于 当前探索的F值。则更新F值为旧有的F值,并设置探索节点的父节点为 M

  • E.开始从B步骤循环执行

    循环结束后打印路径信息

  • F. 溯源 路径信息,从终点追溯父节点信息。就是到达终点所需的最小路径信息

下饭菜,即将登场 A星算法(python版)

# 如下 标记,为一个py文件分隔。
#------------------------------------------------------------------------

# 坐标 
class Coord:

    def __init__(self,x,y):
        self.x = x
        self.y = y
    
    def equals(self,someone):
        if(someone is not None):
            # 不为空
            if(self.x == someone.x and self.y == someone.y):
                return True
            else:
                return False
        else:
            return False

#------------------------------------------------------------------------

from Coord import Coord

# 节点
class Node:

    def __init__(self,coord,parent,G,H):
        self.coord = coord
        self.parent = parent
        self.G = G
        self.H = H

    def __init2__(self,x,y):
        self.coord = Coord(x,y)
        self.G = 0
        self.H = 0

    def toString(self):
        aStr = '{x=' + str(self.coord.x)+',y='+str(self.coord.y)+';F='+str(self.G+self.H)+'G='+str(self.G)+'H='+str(self.H)+'}'
        return aStr

    def toString2(self):
            aStr = '{x=' + str(self.coord.x)+',y='+str(self.coord.y)+'};'
            return aStr


#-----------------------------------------------------------------------------
# 描述一个待搜索的矩阵(二维数组)信息
class MapInfo:

    def __init__(self,maps,width,height,start,end):
        self.maps = maps
        self.width = width
        self.height = height
        self.start = start
        self.end = end

#-----------------------------------------------------------------------------

from Coord import Coord
from Node import Node
# A星算法 
class Astar:
    BAR = 1 # 障碍值
    _PATH = 2 # 路径信息
    DIRECT_VALUE = 10  # 横竖移动代价
    OBLIQUE_VALUE = 14 # 斜移动代价

    openlist = [] # openglist
    closelist = [] # clsoelist

    closemap = {} # 记录已经探索过的节点信息,方便打印探索过程信息


    def __init__(self):
        return
        


    def start(self,mapInfo):
        self.openlist = []
        self.closelist = []
        self.closemap = {}
        # 加入起点到openlist
        self.openlist.append(mapInfo.start)
        # 开始探索
        self.moveNodes(mapInfo)

    
    def moveNodes(self,mapInfo):
        steps = 1 # 步数
        while (len(self.openlist) > 0):

            currentNode = self.openlist.pop(0) # 由于openlist 已经排好序了,直接取出第一个 即为F值最小的节点
            # if(currentNode.coord.x==5 and currentNode.coord.y==5):
            #     print("温馨提示:==============================================到达终点了哦")
            print(steps,"次从", currentNode.coord.x,currentNode.coord.y,"节点,探索周围")
            self.closelist.append(currentNode)
            # 获取map key信息
            key = self.get_map_key2(currentNode.coord.x,currentNode.coord.y)
            self.closemap[key] = currentNode
            # 探索周围
            self.add_neighbor_node_in_open(mapInfo,currentNode)
            self.debug_print_proccess_info(mapInfo)
            steps = steps+1
            if(self.is_coord_in_close(mapInfo.end.coord)):
                # print("标记路径---")
                self.draw_path(mapInfo.maps,mapInfo.end)
                break           
    
    
    #标记 路径信息
    def draw_path(self,maps,end):
        if(maps is None or end is None):
             return
        
        print('总代价为',end.G)
        while (end is not None and end.coord is not None):
            coord = end.coord
            maps[coord.y][coord.x] = self._PATH
            end = end.parent
        
        # print('=========================',maps)
        

    # node是否在closelist中
    def is_coord_in_close(self,coord):
        # print("查找是否在close中",coord.x,coord.y)
        if(self.closelist is not None):
            _length = len(self.closelist)
            for i in range(0,_length):
                node = self.closelist[i]
                # print(node.coord.x,node.coord.y)
                if(node.coord.x == coord.x and node.coord.y == coord.y):
                    # print("=====在close中")
                    return True
            # print("222不在close中")
            return False
        else:
            # print("不在close中")
            return False


    def add_neighbor_node_in_open(self,mapInfo , current):
        
        x = current.coord.x
        y = current.coord.y
        # print("探索", x,y,"节点")
        # 左
        self.add_one_node_in_open(mapInfo,current,x-1,y,self.DIRECT_VALUE)
        # 右
        self.add_one_node_in_open(mapInfo,current,x+1,y,self.DIRECT_VALUE)
        # 上
        self.add_one_node_in_open(mapInfo,current,x,y+1,self.DIRECT_VALUE)
        # 下
        self.add_one_node_in_open(mapInfo,current,x,y-1,self.DIRECT_VALUE)
        # 左上
        self.add_one_node_in_open(mapInfo,current,x-1,y-1,self.OBLIQUE_VALUE)
        # 左下
        self.add_one_node_in_open(mapInfo,current,x-1,y+1,self.OBLIQUE_VALUE)
        # 右上
        self.add_one_node_in_open(mapInfo,current,x+1,y+1,self.OBLIQUE_VALUE)
        # 右下
        self.add_one_node_in_open(mapInfo,current,x+1,y-1,self.OBLIQUE_VALUE)


    def add_one_node_in_open(self,mapInfo , current, x, y , costV):
        
        if(self.can_add_to_open(mapInfo,x,y)):
            # print(x,y)
            end_node = mapInfo.end
            coord = Coord(x,y)
            temp_G = current.G + costV

            child = self.find_node_in_open(coord)
            if(child is not None): # 已经存在open中时
                if(child.G > temp_G):
                    child.G = temp_G
                    child.parent = current
                    self.openlist.append(child)

            else: # 没有在open中找到
                # 计算H值
                temp_H = self.calcH(end_node.coord,coord)
                if(self.is_end_node(end_node.coord,coord)):
                    child = end_node
                    child.parent = current
                    child.G = temp_G
                    child.H = temp_H
                else:
                    child = Node(coord,current,temp_G,temp_H)
                self.openlist.append(child)
            
            # 给openlist 排个序
            self.sort_open_list()
            
    def sort_open_list(self):
        self.openlist.sort(key=self.getComparteValue, reverse=False)
        # self.debug_open_list()

    def debug_print_proccess_info(self,mapInfo):
        maps = mapInfo.maps
        y_length = len(maps[0])
        x_length = len(maps)
        for x in range(0,x_length):
            a_row = ""
            for y in range(0,y_length):
                item = maps[x][y]
                if(item == self.BAR):
                    a_row = a_row + "#" + "  "
                    continue
                key = str(y) + "__" + str(x)
                # print(self.closemap)
                obj_in_map = self.closemap.get(key)
                is_in_map = obj_in_map is not None
                str_item = ""
                if(is_in_map):
                    str_item = "@  "
                else:
                    str_item = "-  "
                a_row = a_row + str_item
            print(a_row)
        print("=============================================")
        return

    
    def debug_open_list(self):
        _length = len(self.openlist)
        aStr = ''
        for i in range(0,_length):
            item = self.openlist[i]
            #print(item,item.coord.x,item.coord.y,item.G,item.H)
            aStr = aStr + item.toString2() + "  "
        
        print(aStr)


    def getComparteValue(self,item):
        _F = item.G + item.H
        return _F

    def is_end_node(self,end,coord):
        return (end is not None and end.equals(coord))
    # 计算 H值
    def  calcH(self,end,coord):
        _x = abs(end.x - coord.x)
        _y = abs(end.y - coord.y)
        return _x + _y


    # 查找open中的信息
    def find_node_in_open(self,coord):
        if(coord is None or self.openlist is None):
            return None
        _length = len(self.openlist)
        for i in range(0,_length):
            node = self.openlist[i]
            if(node.coord.equals(coord)):
                return node            
        return None
        

    
    # 是否能加入到openlist中去
    def can_add_to_open(self,mapInfo, x, y):
        if(x < 0 or x >= mapInfo.width  or y < 0 or y >= mapInfo.height):
            return False
        #if(mapInfo.maps[x][y] == self.BAR):
        if(mapInfo.maps[y][x] == self.BAR):
            return False
        if(self.is_in_close_list(x,y)):
            return False        
        return True
    

    # 是否在 close 的list中已经存在
    def is_in_close_list(self,x,y):
        # if(self.closelist is None):
        #     return False
        _length = len(self.closelist)
        if(_length <= 0):
            return False
        for i in range(0, _length):
            node_item = self.closelist[i]
            if(node_item.coord.x == x and node_item.coord.y == y):
                return True          
        
        return False

    def get_map_key(self,node):
        aStr = str(node.coord.x)  + "__" + str(node.coord.y)
        return aStr
    def get_map_key2(self,x,y):
        aStr = str(x)  + "__" + str(y)
        return aStr

#-----------------------------------------------------------------------------

from MapInfo import MapInfo
from Astar import Astar
from Node import Node
from Coord import Coord

def printMap(maps):
    # print(maps)
    y_length = len(maps[0])
    x_length = len(maps)
    for x in range(0,x_length):
        a_row = ""
        for y in range(0,y_length):
            item = maps[x][y]
            a_row = a_row + str(item) + "  "
        print(a_row)
        
# 调用A星算法 入口
if __name__ == '__main__':
    maps = [
        [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
		[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
		[ 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0 ],
		[ 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 ],
		[ 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 ],
		[ 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 ],
		[ 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 ]
    ]
    start_coord = Coord(1,1)
    start = Node(start_coord,None,0,0)
    end_coord = Coord(5,5)
    end = Node(end_coord,None,0,0)
    info = MapInfo(maps,len(maps[0]), len(maps),start, end)
    a_start = Astar()
    a_start.start(info)
    printMap(maps)

运行结果

1 次从 1 1 节点,探索周围
-  -  -  -  -  -  -  -  -  -  -  -  -  -  -
-  @  -  -  -  -  -  -  -  -  -  -  -  -  -
-  -  #  #  -  -  -  -  -  -  #  #  #  -  -
-  -  -  #  -  -  -  -  -  #  #  -  -  -  -
-  -  -  #  #  #  #  #  #  #  -  -  -  -  -
-  -  -  #  -  -  #  -  -  -  -  -  -  -  -
-  -  -  #  -  -  -  -  #  -  -  -  -  -  -
=============================================
2 次从 2 1 节点,探索周围
-  -  -  -  -  -  -  -  -  -  -  -  -  -  -
-  @  @  -  -  -  -  -  -  -  -  -  -  -  -
-  -  #  #  -  -  -  -  -  -  #  #  #  -  -
-  -  -  #  -  -  -  -  -  #  #  -  -  -  -
-  -  -  #  #  #  #  #  #  #  -  -  -  -  -
-  -  -  #  -  -  #  -  -  -  -  -  -  -  -
-  -  -  #  -  -  -  -  #  -  -  -  -  -  -
=============================================
3 次从 1 2 节点,探索周围
-  -  -  -  -  -  -  -  -  -  -  -  -  -  -
-  @  @  -  -  -  -  -  -  -  -  -  -  -  -
-  @  #  #  -  -  -  -  -  -  #  #  #  -  -
-  -  -  #  -  -  -  -  -  #  #  -  -  -  -
-  -  -  #  #  #  #  #  #  #  -  -  -  -  -
-  -  -  #  -  -  #  -  -  -  -  -  -  -  -
-  -  -  #  -  -  -  -  #  -  -  -  -  -  -
=============================================
4 次从 0 1 节点,探索周围
-  -  -  -  -  -  -  -  -  -  -  -  -  -  -
@  @  @  -  -  -  -  -  -  -  -  -  -  -  -
-  @  #  #  -  -  -  -  -  -  #  #  #  -  -
-  -  -  #  -  -  -  -  -  #  #  -  -  -  -
-  -  -  #  #  #  #  #  #  #  -  -  -  -  -
-  -  -  #  -  -  #  -  -  -  -  -  -  -  -
-  -  -  #  -  -  -  -  #  -  -  -  -  -  -
=============================================
5 次从 1 0 节点,探索周围
-  @  -  -  -  -  -  -  -  -  -  -  -  -  -
@  @  @  -  -  -  -  -  -  -  -  -  -  -  -
-  @  #  #  -  -  -  -  -  -  #  #  #  -  -
-  -  -  #  -  -  -  -  -  #  #  -  -  -  -
-  -  -  #  #  #  #  #  #  #  -  -  -  -  -
-  -  -  #  -  -  #  -  -  -  -  -  -  -  -
-  -  -  #  -  -  -  -  #  -  -  -  -  -  -
=============================================
6 次从 0 2 节点,探索周围
-  @  -  -  -  -  -  -  -  -  -  -  -  -  -
@  @  @  -  -  -  -  -  -  -  -  -  -  -  -
@  @  #  #  -  -  -  -  -  -  #  #  #  -  -
-  -  -  #  -  -  -  -  -  #  #  -  -  -  -
-  -  -  #  #  #  #  #  #  #  -  -  -  -  -
-  -  -  #  -  -  #  -  -  -  -  -  -  -  -
-  -  -  #  -  -  -  -  #  -  -  -  -  -  -
=============================================
7 次从 2 0 节点,探索周围
-  @  @  -  -  -  -  -  -  -  -  -  -  -  -
@  @  @  -  -  -  -  -  -  -  -  -  -  -  -
@  @  #  #  -  -  -  -  -  -  #  #  #  -  -
-  -  -  #  -  -  -  -  -  #  #  -  -  -  -
-  -  -  #  #  #  #  #  #  #  -  -  -  -  -
-  -  -  #  -  -  #  -  -  -  -  -  -  -  -
-  -  -  #  -  -  -  -  #  -  -  -  -  -  -
=============================================
8 次从 0 0 节点,探索周围
@  @  @  -  -  -  -  -  -  -  -  -  -  -  -
@  @  @  -  -  -  -  -  -  -  -  -  -  -  -
@  @  #  #  -  -  -  -  -  -  #  #  #  -  -
-  -  -  #  -  -  -  -  -  #  #  -  -  -  -
-  -  -  #  #  #  #  #  #  #  -  -  -  -  -
-  -  -  #  -  -  #  -  -  -  -  -  -  -  -
-  -  -  #  -  -  -  -  #  -  -  -  -  -  -
=============================================
9 次从 3 1 节点,探索周围
@  @  @  -  -  -  -  -  -  -  -  -  -  -  -
@  @  @  @  -  -  -  -  -  -  -  -  -  -  -
@  @  #  #  -  -  -  -  -  -  #  #  #  -  -
-  -  -  #  -  -  -  -  -  #  #  -  -  -  -
-  -  -  #  #  #  #  #  #  #  -  -  -  -  -
-  -  -  #  -  -  #  -  -  -  -  -  -  -  -
-  -  -  #  -  -  -  -  #  -  -  -  -  -  -
=============================================
10 次从 1 3 节点,探索周围
@  @  @  -  -  -  -  -  -  -  -  -  -  -  -
@  @  @  @  -  -  -  -  -  -  -  -  -  -  -
@  @  #  #  -  -  -  -  -  -  #  #  #  -  -
-  @  -  #  -  -  -  -  -  #  #  -  -  -  -
-  -  -  #  #  #  #  #  #  #  -  -  -  -  -
-  -  -  #  -  -  #  -  -  -  -  -  -  -  -
-  -  -  #  -  -  -  -  #  -  -  -  -  -  -
=============================================
...

80 次从 7 5 节点,探索周围
@  @  @  @  @  @  @  @  @  @  @  @  @  @  @
@  @  @  @  @  @  @  @  @  @  @  @  @  @  @
@  @  #  #  @  @  @  @  @  @  #  #  #  @  @
@  @  @  #  @  @  @  @  @  #  #  @  @  @  @
@  @  @  #  #  #  #  #  #  #  @  @  @  @  @
@  @  @  #  -  -  #  @  @  @  @  @  @  @  @
@  @  @  #  -  -  -  -  #  @  @  @  @  @  @
=============================================
81 次从 7 6 节点,探索周围
@  @  @  @  @  @  @  @  @  @  @  @  @  @  @
@  @  @  @  @  @  @  @  @  @  @  @  @  @  @
@  @  #  #  @  @  @  @  @  @  #  #  #  @  @
@  @  @  #  @  @  @  @  @  #  #  @  @  @  @
@  @  @  #  #  #  #  #  #  #  @  @  @  @  @
@  @  @  #  -  -  #  @  @  @  @  @  @  @  @
@  @  @  #  -  -  -  @  #  @  @  @  @  @  @
=============================================
82 次从 6 6 节点,探索周围
@  @  @  @  @  @  @  @  @  @  @  @  @  @  @
@  @  @  @  @  @  @  @  @  @  @  @  @  @  @
@  @  #  #  @  @  @  @  @  @  #  #  #  @  @
@  @  @  #  @  @  @  @  @  #  #  @  @  @  @
@  @  @  #  #  #  #  #  #  #  @  @  @  @  @
@  @  @  #  -  -  #  @  @  @  @  @  @  @  @
@  @  @  #  -  -  @  @  #  @  @  @  @  @  @
=============================================
83 次从 5 6 节点,探索周围
@  @  @  @  @  @  @  @  @  @  @  @  @  @  @
@  @  @  @  @  @  @  @  @  @  @  @  @  @  @
@  @  #  #  @  @  @  @  @  @  #  #  #  @  @
@  @  @  #  @  @  @  @  @  #  #  @  @  @  @
@  @  @  #  #  #  #  #  #  #  @  @  @  @  @
@  @  @  #  -  -  #  @  @  @  @  @  @  @  @
@  @  @  #  -  @  @  @  #  @  @  @  @  @  @
=============================================
84 次从 5 5 节点,探索周围
@  @  @  @  @  @  @  @  @  @  @  @  @  @  @
@  @  @  @  @  @  @  @  @  @  @  @  @  @  @
@  @  #  #  @  @  @  @  @  @  #  #  #  @  @
@  @  @  #  @  @  @  @  @  #  #  @  @  @  @
@  @  @  #  #  #  #  #  #  #  @  @  @  @  @
@  @  @  #  -  @  #  @  @  @  @  @  @  @  @
@  @  @  #  -  @  @  @  #  @  @  @  @  @  @
=============================================
总代价为 224
0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
0  2  2  2  2  2  2  2  2  2  2  2  2  0  0
0  0  1  1  0  0  0  0  0  0  1  1  1  2  0
0  0  0  1  0  0  0  0  0  1  1  2  2  0  0
0  0  0  1  1  1  1  1  1  1  2  0  0  0  0
0  0  0  1  0  2  1  2  2  2  0  0  0  0  0
0  0  0  1  0  0  2  0  1  0  0  0  0  0  0

片尾

笔者能力不足,本来很简单的算法。花了3天时间,才吃透 并结合java版本并将算法用python实现。
在自己写代码的过程中,也出现了bug。在调试时吃了 不细心的亏。望各位同僚切不可自大

若有想要java版的,请留言私信

喜欢记得 点赞 / 收藏 / 评论~~~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值