A*算法及迪杰斯特拉算法求解最短路径问题

A*算法及迪杰斯特拉算法求解最短路径问题
一、题目

最短路径问题:求各节点到Bucharest的最小距离。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UMugchyR-1641633409771)(C:\Users\Jackson\AppData\Roaming\Typora\typora-user-images\image-20220108122828222.png)]

二、数据表示

在表示图的数据结构中,需要涵盖节点的相连关系相连节点间的距离,以下为python数据结构表示

  1. 记录图中的全部节点,用于循环遍历:
vertex_list_ = ['Oradea', 'Zerind', 'Arad', 'Sibiu', 'Fagaras', 'Timisoara', 'Rimnicu Vilcea', 'Lugoj', 'Mechadia', 'Pitesti', 'Craiova', 'Drobeta', 'Giurgiu', 'Bucharest', 'Urziceni', 'Vaslui', 'Hirsova', 'Iasi', 'Neamt', 'Eforie']
  1. 记录相连的节点—Map字典

    一个元素的键为图中的节点,值为与键相连的节点列表。可以直接通过键值得到与该节点相连的节点。

# 采用字典记录节点间相连的关系,其中键为节点,值为与之相连的节点列表
Map = {'Oradea': ['Zerind', 'Sibiu'], 'Zerind': ['Arad', 'Oradea'],
       'Arad': ['Zerind', 'Sibiu', 'Timisoara'],
       'Sibiu': ['Fagaras', 'Oradea', 'Arad', 'Rimnicu Vilcea'],
       'Fagaras': ['Sibiu', 'Bucharest'],
       'Timisoara': ['Arad', 'Lugoj'],
       'Rimnicu Vilcea': ['Pitesti', 'Sibiu', 'Craiova'],
       'Lugoj': ['Timisoara', 'Mechadia'],
       'Mechadia': ['Lugoj', 'Drobeta'],
       'Pitesti': ['Rimnicu Vilcea', 'Craiova', 'Bucharest'],
       'Craiova': ['Drobeta', 'Pitesti', 'Rimnicu Vilcea'],
       'Drobeta': ['Mechadia', 'Craiova'],
       'Giurgiu': ['Bucharest'],
       'Bucharest': ['Fagaras', 'Pitesti', 'Giurgiu', 'Urziceni'],
       'Urziceni': ['Vaslui', 'Bucharest', 'Hirsova'],
       'Vaslui': ['Iasi', 'Urziceni'],
       'Hirsova': ['Urziceni', 'Eforie'],
       'Iasi': ['Neamt', 'Vaslui'],
       'Neamt': ['Iasi'],
       'Eforie': ['Hirsova']
       }
  1. 记录节点间的距离–disMap字典

    该字典的键为相连节点组成的元组,值为两节点之间的距离。

disMap = {
    ('Oradea', 'Oradea'): 0, ('Oradea', 'Zerind'): 71, ('Oradea', 'Sibiu'): 151, ('Zerind', 'Zerind'): 0,
    ('Zerind', 'Arad'): 75, ('Zerind', 'Oradea'): 71, ('Arad', 'Arad'): 0, ('Arad', 'Zerind'): 75,
    ('Arad', 'Sibiu'): 140, ('Arad', 'Timisoara'): 118, ('Sibiu', 'Sibiu'): 0, ('Sibiu', 'Fagaras'): 99,
    ('Sibiu', 'Oradea'): 151, ('Sibiu', 'Arad'): 140, ('Sibiu', 'Rimnicu Vilcea'): 80, ('Fagaras', 'Fagaras'): 0,
    ('Fagaras', 'Sibiu'): 99, ('Fagaras', 'Bucharest'): 211, ('Timisoara', 'Timisoara'): 0,
    ('Timisoara', 'Arad'): 118, ('Timisoara', 'Lugoj'): 111, ('Rimnicu Vilcea', 'Rimnicu Vilcea'): 0,
    ('Rimnicu Vilcea', 'Pitesti'): 97,
    ('Rimnicu Vilcea', 'Sibiu'): 80, ('Rimnicu Vilcea', 'Craiova'): 146, ('Lugoj', 'Lugoj'): 0,
    ('Lugoj', 'Timisoara'): 111, ('Lugoj', 'Mechadia'): 70, ('Mechadia', 'Mechadia'): 0, ('Mechadia', 'Lugoj'): 70,
    ('Mechadia', 'Drobeta'): 75, ('Pitesti', 'Pitesti'): 0, ('Pitesti', 'Rimnicu Vilcea'): 97,
    ('Pitesti', 'Craiova'): 138, ('Pitesti', 'Bucharest'): 101, ('Craiova', 'Craiova'): 0, ('Craiova', 'Drobeta'): 120,
    ('Craiova', 'Pitesti'): 138, ('Craiova', 'Rimnicu Vilcea'): 146, ('Drobeta', 'Drobeta'): 0,
    ('Drobeta', 'Mechadia'): 75, ('Drobeta', 'Craiova'): 120, ('Giurgiu', 'Giurgiu'): 0, ('Giurgiu', 'Bucharest'): 90,
    ('Bucharest', 'Bucharest'): 0, ('Bucharest', 'Fagaras'): 211, ('Bucharest', 'Pitesti'): 101,
    ('Bucharest', 'Giurgiu'): 90, ('Bucharest', 'Urziceni'): 85, ('Urziceni', 'Urziceni'): 0,
    ('Urziceni', 'Vaslui'): 142, ('Urziceni', 'Bucharest'): 85, ('Urziceni', 'Hirsova'): 98, ('Vaslui', 'Vaslui'): 0,
    ('Vaslui', 'Iasi'): 92, ('Vaslui', 'Urziceni'): 142, ('Hirsova', 'Hirsova'): 0, ('Hirsova', 'Urziceni'): 98,
    ('Hirsova', 'Eforie'): 86, ('Iasi', 'Iasi'): 0, ('Iasi', 'Neamt'): 87, ('Iasi', 'Vaslui'): 92,
    ('Neamt', 'Neamt'): 0, ('Neamt', 'Iasi'): 87, ('Eforie', 'Eforie'): 0, ('Eforie', 'Hirsova'): 86
}
三、A*算法
1. 简介

(1) A*算法是一种有序搜索算法,其特点在于对估价函数的定义上。

(2)估价函数:A*算法中根据估价函数选择下一个节点,估价函数f的值等于当前节点与下一个节点的距离加上下一个节点到目标节点的估计距离。当前节点选择最小代价值的下一个节点继续遍历。

2. 估价函数的实现

(1)各节点到目标节点Bucharest估计代价值字典。

estimate_table = {"Oradea": 380, "Zerind": 374, "Arad": 366, "Sibiu": 253, "Fagaras": 176, "Timisoara": 329,
                  "Rimnicu Vilcea": 193, "Lugoj": 244, "Mechadia": 241, "Pitesti": 100, "Craiova": 160,
                  "Drobeta": 242, "Giurgiu": 77, "Bucharest": 0, "Urziceni": 80, "Vaslui": 199, "Hirsova": 151,
                  "Iasi": 226, "Neamt": 234, "Eforie": 161, }

def leastCostNode(cur_node_: str) -> list:
    """
    :param cur_node_: 当前节点
    :return: 返回与cur_node相连的,根据代价函数降序的节点列表
    """
    return sorted(Map[cur_node_], key=lambda x: disMap[cur_node_, x] + estimate_table[x])
3. 主函数及注解
if __name__ == '__main__':

    target = "Bucharest"  # 目的节点
    dis_list = []
    for start in vertex_list_:  # 遍历图中的全部节点,计算各节点到目的节点最短距离
        cur_dis = 0  # 当前距离,初始设为0
        cur_node = start  # 当前节点,初始设为开始节点
        path = [start]  # 路径列表,记录最短路径
        Close = [start]  # Close表,用于记录以及访问的节点,避免死循环
        while cur_node != target:  # 循环直至到达目的节点
            nextNodeList = leastCostNode(cur_node)  # 获得与当前节点相连的,经降序排序的节点列表
            nextNode = nextNodeList[0]  # 取估价函数最小的节点
            while nextNode in Close:  # 判断该节点是否已访问
                nextNodeList.remove(nextNode)  # 已访问后清除
                nextNode = nextNodeList[0]  # 再次获取当前估价函数最小的节点
            Close.append(nextNode)  # 更新Close表
            path.append(nextNode)  # 更新路径
            cur_dis += disMap[(cur_node, nextNode)]  # 计算当前距离
            cur_node = nextNode  # 继续遍历下一个节点
        dis_list.append(cur_dis)
        print(f"The shortest distance from {start} to {target} is {cur_dis}.", 
              end=" ")  # 打印最短路径距离
        print(f"The route is ", end='')  # 打印最短路径
        for node in path:
            print(node, end=' - ')
        print("END")

4. 运行结果

![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tloFYXbQ-1641633409773)(C:\Users\Jackson\AppData\Roaming\Typora\typora-user-images\image-20220108123635605.png)]](https://img-blog.csdnimg.cn/9d11091811de4bc9a07624e066ff76fc.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAVml2YSBQeXRob24=,size_20,color_FFFFFF,t_70,g_se,x_16

​ 结果为各节点到目的节点Bucharest的最短路径的值路径的走法

四、迪杰斯特拉算法
1. 简介

(1)迪杰斯特拉特算法采用动态规划的算法,从起始点开始,遍历子节点并计算距离,选择最小的距离,更新数据后继续遍历。(2)适用于权为正数的简单图。

(2) 图数据结构的表示与A*算法相同

2. 引入工具库
from heapdict import heapdict

heapdict.heapdict: 堆字典,通过该类实现对字典管理。

3. Dijkstra函数及注释
def Dijkstra(target: str, vertx_list: list, graph: dict, dis_map_: dict) -> dict:
    """
    :param target: 目的地节点
    :param vertx_list: 节点列表,包含图中的全部节点
    :param graph: 节点相连的关系字典
    :param dis_map_: 字典类型,记录节点间的距离
    :return: 字典类型,返回各节点与目的地节点target的最短距离,值为各节点与target
    """
    hd = heapdict()  # 构建堆字典,用于记录与更新两点(起始点与目的节点)间的距离
    distance_map = {}  # 记录起始节点到其它所有节点的最短路径
    inf = float("inf")
    for v in vertx_list:  # 将起始节点与其它节点的距离加入小堆
        if v == target:
            # 节点自身与自身的距离设为0
            hd[(target, v)] = 0
            distance_map[(target, v)] = 0
        else:
            # 节点间的距离设为无穷大
            hd[(target, v)] = inf
            distance_map[(target, v)] = inf
    edge = hd.peekitem()  # 初始节点取出堆顶的元素,元素的类型为((node, node), int)
    while edge is not None:
        point_in_edge = edge[0]  # ('', '') 相连的两个点
        second_point = point_in_edge[1]  # 第二个节点
        update = False
        for connect_point in graph[second_point]:  # connect_point(连接点): 与第二个节点有相连的点
            edge_len = dis_map_[(second_point, connect_point)]  # 两个新的连接点边长
            # 如果新的连接点更短,更新最短距离
            if hd[(target, second_point)] + edge_len < distance_map[(target, connect_point)]:
                hd[(target, connect_point)] = hd[(target, second_point)] + edge_len
                distance_map[(target, connect_point)] = hd[(target, second_point)] + edge_len
                update = True
        if not update:  # 一旦堆元素有更新时不能把顶部元素弹出,因为被更新后的元素可能恰好处于堆顶。
            # 如果没有更新,已经是最短距离,删去堆顶元素
            hd.popitem()
        if hd.__len__() == 0:  # 对遍历完毕后,将edge设为None,退出循环
            edge = None
        else:
            edge = hd.peekitem()  # 否则取出堆顶项目
    return distance_map
4. 主函数
if __name__ == '__main__':
    distance = Dijkstra("Bucharest", vertex_list_, Map, disMap)
    for key in distance.keys():
        print("The shortest distance from {0} to {1} is {2}"
              .format(key[1], key[0], distance[key]))
5. 运行结果
The shortest distance from Oradea to Bucharest is 429
The shortest distance from Zerind to Bucharest is 493
The shortest distance from Arad to Bucharest is 418
The shortest distance from Sibiu to Bucharest is 278
The shortest distance from Fagaras to Bucharest is 211
The shortest distance from Timisoara to Bucharest is 536
The shortest distance from Rimnicu Vilcea to Bucharest is 198
The shortest distance from Lugoj to Bucharest is 504
The shortest distance from Mechadia to Bucharest is 434
The shortest distance from Pitesti to Bucharest is 101
The shortest distance from Craiova to Bucharest is 239
The shortest distance from Drobeta to Bucharest is 359
The shortest distance from Giurgiu to Bucharest is 90
The shortest distance from Bucharest to Bucharest is 0
The shortest distance from Urziceni to Bucharest is 85
The shortest distance from Vaslui to Bucharest is 227
The shortest distance from Hirsova to Bucharest is 183
The shortest distance from Iasi to Bucharest is 319
The shortest distance from Neamt to Bucharest is 406
The shortest distance from Eforie to Bucharest is 269

进程已结束,退出代码为 0
五、对比分析
1. 结果比较

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jPgrkOKx-1641633409774)(C:\Users\Jackson\AppData\Roaming\Typora\typora-user-images\image-20220108124558867.png)]

2. 结果分析

​ 可以看出,A算法的结果不总是最优解,因为A算法总是选择f值最小的节点作为拓展节点,f值与估计代价有关,而估计代价总不一定等于实际的最小代价,所以A*算法所求不一定是最优解。

(1) 迪杰斯特拉算法采用动态规划,对所有节点进行遍历,得到最优解。但迪杰斯特拉算法实现起来较复杂,时间复杂度较高,不便于记录路径,且不适用于边为负值的图。

(2) A算法实现简单,时间复杂度低,但受估计代价等的约束,所求结果不一定是最优解;且A算法需要提前知道估计代价,在某些条件下不适用。A*算法默认各节点均存在路线抵达目标节点。

总是选择f值最小的节点作为拓展节点,f值与估计代价有关,而估计代价总不一定等于实际的最小代价,所以A*算法所求不一定是最优解。

(1) 迪杰斯特拉算法采用动态规划,对所有节点进行遍历,得到最优解。但迪杰斯特拉算法实现起来较复杂,时间复杂度较高,不便于记录路径,且不适用于边为负值的图。

(2) A算法实现简单,时间复杂度低,但受估计代价等的约束,所求结果不一定是最优解;且A算法需要提前知道估计代价,在某些条件下不适用。A*算法默认各节点均存在路线抵达目标节点。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值