A星算法浅析

                                                                                                                                                           

此文章仅供学习使用,为离散数学学习任务,部分图片为其他平台转载,侵删

一、广度优先搜索(Breadth-first search (BFS))

 BFS是一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。
它并不考虑结果的可能位址,彻底地搜索整张图,直到找到结果为止

1、 时间复杂度是:T(n) = O(n^2),效率底下
2、每个顶点之间没有权值,无法定义优先级,不能找到最优路线。比如遇到水域需要绕过行走,在宽度算法里面无法涉及。 

二、贪婪最佳优先搜索(Greedy Best-First Search)

 从上图中我们可以明显看到右边的算法(贪婪最佳优先搜索 )寻找速度要快于左侧,虽然它的路径不是最优和最短的,但障碍物最少的时候,他的速度却足够的快。
这就是贪心算法的优势,基于目标去搜索,而不是完全搜索

三、A星寻路算法

A*算法结合了基于目标搜索和基于成本的优点

先看看A*算法的函数表达式

A*的核心关键词是代价,代价意为某次行动之后需要支付的成本,算法每次将从队列中选取总代价最小的节点作为下一个代遍历的节点。其中总代价为未来代价和当今代价之和。

在公式中

  • f(n)是节点n的代价。当我们选择下一个要遍历的节点时,我们总会选取总代价最高(值最小)的节点。
  • g(n) 是节点n距离起点的代价。
  • h(n)是节点n距离终点的未来代价,这也就是A*算法的启发函数。
  • 关于h(n)我们在下面详细讲解。
1.节点

在定义节点的时候,我给了节点以下几个属性:坐标(x,y),父节点,是否为墙,代价,共5个属性

并提供了一个函数声明

class Point:
    def __init__(self, x, y, isObstacle=False):
        self.x = x
        self.y = y
        self.parent = None  # 上一个点
        self.isObstacle = isObstacle  # 是否是墙
        self.cost = sys.maxsize

 

A*算法使用两个集合来表示待遍历的节点,与已经遍历过的节点,这通常称之为open_setclose_set。 

2、启发函数

启发函数的意义在于对于未来代价的估计,不同的启发函数可以有不同精度的代价计算

2.1欧几里得距离

欧几里得距离是指两个节点之间的直线距离,因此其计算方法也是我们比较熟悉的:

 

 2.2曼哈顿距离

如果图形中只允许朝上下左右四个方向移动,则启发函数可以使用曼哈顿距离,它的计算方法如下图所示:

 # 到起点的曼哈顿距离
    def BaseCost(self, p):
        x_dis = p.x
        y_dis = p.y
        return x_dis + y_dis

 示例采用曼哈顿距离

3、算法过程

* 初始化open_set和close_set;
* 将起点加入open_set中,并设置优先级为0(优先级最高);
* 如果open_set不为空,则从open_set中选取优先级最高的节点n:
    * 如果节点n为终点,则:
        * 从终点开始逐步追踪parent节点,一直达到起点;
        * 返回找到的结果路径,算法结束;
    * 如果节点n不是终点,则:
        * 将节点n从open_set中删除,并加入close_set中;
        * 遍历节点n所有的邻近节点:
            * 如果邻近节点m在close_set中,则:
                * 跳过,选取下一个邻近节点
            * 如果邻近节点m也不在open_set中,则:
                * 设置节点m的parent为节点n
                * 计算节点m的优先级
                * 将节点m加入open_set中

 3.1初始化迷宫
# 读入迷宫文件并初始化
path = input("路径: ")
map = open(path, "r")
row = map.readlines()
list_map = []
for line in row:
    line = list(line. strip().split(' '))
    s = []
    for i in line:
        s = list(i)
        list_map.append(s)
3.2判断墙壁
# 判断墙
for i in range(len(list_map)):
    for j in range(len(list_map[i])):
        if list_map[i][j] == "0":
            list_map[i][j] = Point(i, j)
        else:
            list_map[i][j] = Point(i, j, True)
3.3寻找终点坐标
def findEndPoint(map, x, y):
    rowCount = len(map)
    columnCount = len(map[0])

    # 第一行
    for i in range(columnCount):
        if not map[0][i].isObstacle and (i != y or x != 0):
            return [0, i]

    # 最后一行
    for i in range(columnCount):
        if not map[rowCount-1][i].isObstacle and (i != y or x != rowCount-1):
            return [rowCount - 1, i]

    # 第一列
    for i in range(rowCount):
        if not map[i][0].isObstacle and (i != x or y != 0):
            return [i, 0]

    # 最后一列
    for i in range(rowCount):
        if not map[i][columnCount - 1].isObstacle and (i != x or y != columnCount - 1):
            return [i, columnCount - 1]
3.4定义起点
# 定义起点
startPoint = [0, 1]
3.5Astar主体
class AStar:
    def __init__(self, map, startPoint, endPoint):
        self.map = map  # 迷宫地图
        self.mapsize = len(map)  # 迷宫大小

        self.startPoint = self.map[startPoint[0]][startPoint[1]]  # 迷宫起点
        self.startPoint.cost = 0

        self.endPoint = self.map[endPoint[0]][endPoint[1]]  # 迷宫终点

        self.open_list = []  # 要去的点
        self.close_list = []  # 去过的点

    # 到起点的曼哈顿距离
    def BaseCost(self, p):
        x_dis = p.x
        y_dis = p.y
        return x_dis + y_dis

    # 到中点的曼哈顿距离
    def HeuristicCost(self, p):
        x_dis = self.mapsize - 1 - p.x
        y_dis = self.mapsize - 1 - p.y
        return x_dis + y_dis

    # 总共的曼哈顿距离
    def TotalCost(self, p):
        return self.BaseCost(p) + self.HeuristicCost(p)

    # 是否有效

    def IsValidPoint(self, x, y):
        if x < 0 or y < 0:
            return False
        if x >= self.mapsize or y >= self.mapsize:
            return False
        return not self.map[x][y].isObstacle

    # 是否在列表里

    def IsInPointList(self, p, point_list):
        for point in point_list:
            if point.x == p.x and point.y == p.y:
                return True
        return False

    def IsInOpenList(self, p):
        return self.IsInPointList(p, self.open_list)

    def IsInCloseList(self, p):
        return self.IsInPointList(p, self.close_list)

    def IsStartPoint(self, p):
        return p.x == self.startPoint.x and p.y == self.startPoint.y

    def IsEndPoint(self, p):
        return p.x == self.endPoint.x and p.y == self.endPoint.y

    def Run(self):
        self.open_list.append(self.startPoint)

        while True:
            index = self.SelectPointInOpenList()
            if index < 0:
                return
            p = self.open_list[index]

            del self.open_list[index]
            self.close_list.append(p)

            x = p.x
            y = p.y
            self.ProcessPoint(x-1, y, p)
            self.ProcessPoint(x, y-1, p)
            self.ProcessPoint(x+1, y, p)
            self.ProcessPoint(x, y+1, p)

    # 标记上一个点,并将其放入open_list

    def ProcessPoint(self, x, y, parent):
        if not self.IsValidPoint(x, y):
            return
        p = self.map[x][y]
        if self.IsInCloseList(p):
            return
        if not self.IsInOpenList(p):
            p.parent = parent
            p.cost = self.TotalCost(p)
            self.open_list.append(p)

    # 寻找代价最小的点
    def SelectPointInOpenList(self):
        index = 0
        selected_index = -1
        min_cost = sys.maxsize
        for p in self.open_list:
            cost = self.TotalCost(p)
            if cost < min_cost:
                min_cost = cost
                selected_index = index
            index += 1
        return selected_index
3.6输出结果
# 寻找终点
endPoint = findEndPoint(list_map, startPoint[0], startPoint[1])

astar = AStar(list_map, startPoint, endPoint)
astar.Run()

# 输出
ret = []
for i in range(len(list_map)):
    line = ["□ "]*len(list_map[i])
    ret.append(line)

point = astar.endPoint
count = 0
while point:
    ret[point.x][point.y] = "■ "
    point = point.parent
    count += 1

for i in range(len(ret)):
    ret[i] = "".join(ret[i])

ret = "\n".join(ret)
print(ret)
print("一共需要"+str(count)+"步")
3.7示例迷宫

3.8示例结果
 

参考文献

Introduction to the A* Algorithm (redblobgames.com)

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值