Python 中的图:Dijkstra 算法

这篇具有很好参考价值的文章主要介绍了Python 中的图:Dijkstra 算法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

介绍

  图是最有用的数据结构之一。它们可用于对几乎所有事物进行建模——对象关系和网络是最常见的。图像可以表示为网格状的像素图,句子可以表示为单词的图。图表被用于各个领域,从制图到社会心理学,当然它们在计算机科学中也被广泛使用。因此图搜索和遍历起着重要的计算作用。Dijkstra 算法旨在找到 图中节点 之间的最短路径。它是由荷兰计算机科学家 Edsger Wybe Dijkstra 于 1956 年在思考从鹿特丹到格罗宁根的最短路线时设计的。Dijkstra 算法 多年来一直在发生变化,并且存在各种版本和变体。最初用于计算两个节点之间的最短路径。由于它的工作方式 ,适用于计算起始节点和图中每个其他节点之间的最短路径。这种方式可用于生成 最短路径树,该树由两个节点以及所有其他节点之间的最短路径组成。然后你可以修剪掉你不感兴趣的树,得到两个节点之间的最短路径,但你不可避免地必须计算整个树,这是 Dijkstra 算法 的一个缺点,它不适合大型图。

Dijkstra 算法

  Dijkstra 算法 适用于无向、连接、加权图,Dijkstra 算法 可以求出源节点到其他所有节点的最短路径。
  一开始,我们要创建一组已访问顶点,以跟踪所有已分配正确最短路径的顶点。我们还需要设置图中所有顶点的“成本”(通向它的当前最短路径的长度)。开始时所有成本都将设置为 “无穷大(inf)” ,以确保我们可以与之比较的所有其他成本都小于起始成本。唯一的例外是第一个起始顶点的成本——这个顶点的成本为 0,因为它没有通往自身的路径——标记为 源节点 s。然后依据以下三个规则,直到遍历整个图:

  • 每次从未标记的节点中选择距离出发点最近的节点,标记,收录到最优路径集合中。
  • 对于当前节点 n 和其的邻居节点 m 如果 cheapestPath(s, n) + cheapestPath(n, m) < cheapestPath(s, m),则更新 sm 的最短路径为 cheapestPath(s, n) + cheapestPath(n, m)
  • 且每次更新最短路径时会保存该节点的前节点。

下面用一个无向、加权、连通图来解释上述规则原理:
Python 中的图:Dijkstra 算法
假设 Vertex 0 为起点(源节点)。我们将把图中顶点的初始成本设置为无穷大,除了起始顶点:

Python 中的图:Dijkstra 算法
我们选择成本最低的顶点——即 Vertex 0。我们将其标记为已访问并将其添加到我们的已访问顶点集。起始节点将始终具有最低成本,因此它将始终是第一个添加的节点,同时标记 Vertex 0,之后不用再进行访问:

Python 中的图:Dijkstra 算法
然后,我们将更新相邻顶点(1 和 6)的成本。由于 0 + 4 < infinity0 + 7 < infinity,我们更新这些顶点的成本:
Python 中的图:Dijkstra 算法
现在我们访问未标记顶点中的最小成本顶点。4 < 7,所以我们遍历到 Vertex 1:
Python 中的图:Dijkstra 算法
在遍历时,我们将其标记为已访问,然后观察并更新相邻顶点:2、6 和 7:

  • 因为 4 + 9 < infinity,Vertex 2 的新成本将是 13。
  • 因为 4 + 11 > 7,Vertex 6 的成本将保持为 7。
  • 因为 4 + 20 < infinity,Vertex 7 的新成本将是 24。

则新成本如下:
Python 中的图:Dijkstra 算法
此时在未标记的顶点中 Vertex 6的成本最小,因此将其作为出发点,将其标记为已访问并更新其相邻顶点的成本:
Python 中的图:Dijkstra 算法
Python 中的图:Dijkstra 算法

该过程继续到 Vertex 7:
Python 中的图:Dijkstra 算法
Python 中的图:Dijkstra 算法
再一次,到 Vertex 4:
Python 中的图:Dijkstra 算法
Python 中的图:Dijkstra 算法
再一次,到 Vertex 2:
Python 中的图:Dijkstra 算法
我们要考虑的唯一顶点是 Vertex 3。由于11 + 6 < 19,Vertex 3 的成本被更新。然后继续到 Vertex 8。
Python 中的图:Dijkstra 算法
Python 中的图:Dijkstra 算法

然后更新 Vertex 3:
Python 中的图:Dijkstra 算法
最后更新 Vertex 5:
Python 中的图:Dijkstra 算法
没有更多未访问的顶点可能需要更新。我们的最终成本表示从节点 0 到图中每个其他节点的最短路径:Python 中的图:Dijkstra 算法
动画演示如下:来自 https://www.youtube.com/watch?v=JLARzu7coEs
Python 中的图:Dijkstra 算法

Python 实现 Dijkstra 算法

为了对我们还没有访问过的顶点进行排序和跟踪——我们将使用 PriorityQueue(优先队列)

from queue import PriorityQueue

实现一个名为 Graph 的类的构造函数:

class Graph:
    def __init__(self, num_of_vertices):
        self.vertices = num_of_vertices
        #距离表
        self.edges = [[-1 for i in range(num_of_vertices)] for j in range(num_of_vertices)]
        #记录被访问过的节点
        self.visited = []

在这个简单的参数化构造函数中,将图中顶点的数量作为参数,并初始化了三个字段:

  • vertices:表示图中的顶点数。
  • edges:以矩阵的形式表示边的列表。对于节点 uvself.edges[u][v] = weight
  • visited:将包含访问的顶点的集合。

定义一个将向图形添加边的方法:

    def add_edge(self, u, v, weight):
        #记录u,v两节点之间的距离
        #要注意的是如果是有向图只需定义单向的权重
        #如果是无向图则需定义双向权重
        self.edges[u][v] = weight
        self.edges[v][u] = weight

定义 Dijkstra 算法

    def dijkstra(self, start_vertex):
        #开始时定义源节点到其他所有节点的距离为无穷大
        D = {v: float('inf') for v in range(self.vertices)}
        #源节点到自己的距离为0
        D[start_vertex] = 0
        #优先队列
        pq = PriorityQueue()
        pq.put((0, start_vertex))
        # 记录每个节点的前节点,便于回溯
        previousVertex = {}

        while not pq.empty():
            #得到优先级最高的节点,也就是前节点到其他节点距离最短的节点作为当前出发节点
            (dist, current_vertex) = pq.get()
            #标记已访问过的节点(最有路径集合)
            self.visited.append(current_vertex)

            for neighbor in range(self.vertices):
                #邻居节点之间距离不能为-1
                if self.edges[current_vertex][neighbor] != -1:
                    distance = self.edges[current_vertex][neighbor]
                    #已经访问过的节点不能再次被访问
                    if neighbor not in self.visited:
                        #更新源节点到其他节点的最短路径
                        old_cost = D[neighbor]
                        new_cost = D[current_vertex] + distance
                        if new_cost < old_cost:
                            #加入优先队列
                            pq.put((new_cost, neighbor))
                            D[neighbor] = new_cost
                            previousVertex[neighbor] = current_vertex
        return D, previousVertex

在这段代码中,首先创建了一个大小为 num_of_vertices 的字典 D,用于统计最短路径的成本。整个列表初始化为无穷大。这将是一个列表,我们在其中保留从 start_vertex 所有其他节点的最短路径。我们将起始顶点的值设置为 0,因为这是它与自身的距离。然后,我们初始化一个优先级队列,我们​​将使用它来快速将顶点从最远到最远排序。我们将起始顶点放入优先级队列中。再然后初始化previousVertex ,用于标记每个节点的前节点。现在,对于优先队列中的每个顶点,我们将首先将它们标记为已访问,然后我们将遍历它们的邻居。如果邻居没有被访问,我们将比较它的旧成本和新成本。旧代价是从起始顶点到邻居的最短路径的当前值,而新代价是从起始顶点到当前顶点的最短路径与当前顶点到相邻顶点的距离之和的值邻居。如果新成本低于旧成本,我们将邻居及其成本放入优先级队列,并相应地更新我们保留最短路径的列表,同时更新邻居节点的前节点。最后,在所有顶点都被访问并且优先级队列为空之后,我们返回字典 DpreviousVertex

实现之前的例子:

def Dijkstra() :
    g = Graph(9)
    g.add_edge(0, 1, 4)
    g.add_edge(0, 6, 7)
    g.add_edge(1, 6, 11)
    g.add_edge(1, 7, 20)
    g.add_edge(1, 2, 9)
    g.add_edge(2, 3, 6)
    g.add_edge(2, 4, 2)
    g.add_edge(3, 4, 10)
    g.add_edge(3, 5, 5)
    g.add_edge(4, 5, 15)
    g.add_edge(4, 7, 1)
    g.add_edge(4, 8, 5)
    g.add_edge(5, 8, 12)
    g.add_edge(6, 7, 1)
    g.add_edge(7, 8, 3)

    D, previousVertex = g.dijkstra(0)
    #每个节点的前节点,可通过回溯得到最短路径
    print(previousVertex)
    for vertex in range(len(D)):
        print(f"distance from vertex {0} to vertex {vertex} is {D[vertex]}")

结果:
Python 中的图:Dijkstra 算法

测试

得到节点s和t之间的最短路径。

Python 中的图:Dijkstra 算法
上图是一个有向图,而我们在第一个例子中定义的是无向图,因此要修改代码。在代码中与无向图的差异只是一行,即定义权重时只定义一边,只需注释掉 self.edges[v][u] = weight 即可。为了实现方便,图中节点的名称并未使用字符表示,而是使用数字进行表示,在搜索完之后根据字典再对应回去。

from queue import PriorityQueue

class Graph:
    def __init__(self, num_of_vertices):
        self.vertices = num_of_vertices
        #距离表
        self.edges = [[-1 for i in range(num_of_vertices)] for j in range(num_of_vertices)]
        #记录被访问过的节点
        self.visited = []

    def add_edge(self, u, v, weight):
        #记录u,v两节点之间的距离
        #要注意的是如果是有向图只需定义单向的权重
        #如果是无向图则需定义双向权重
        self.edges[u][v] = weight
        # self.edges[v][u] = weight

    def dijkstra(self, start_vertex):
        #开始时定义源节点到其他所有节点的距离为无穷大
        D = {v: float('inf') for v in range(self.vertices)}
        #源节点到自己的距离为0
        D[start_vertex] = 0
        #优先队列
        pq = PriorityQueue()
        pq.put((0, start_vertex))
        # 记录每个节点的前节点,便于回溯
        previousVertex = {}

        while not pq.empty():
            #得到优先级最高的节点,也就是前节点到其他节点距离最短的节点作为当前出发节点
            (dist, current_vertex) = pq.get()
            #标记已访问过的节点(最有路径集合)
            self.visited.append(current_vertex)

            for neighbor in range(self.vertices):
                #邻居节点之间距离不能为-1
                if self.edges[current_vertex][neighbor] != -1:
                    distance = self.edges[current_vertex][neighbor]
                    #已经访问过的节点不能再次被访问
                    if neighbor not in self.visited:
                        #更新源节点到其他节点的最短路径
                        old_cost = D[neighbor]
                        new_cost = D[current_vertex] + distance
                        if new_cost < old_cost:
                            #加入优先队列
                            pq.put((new_cost, neighbor))
                            D[neighbor] = new_cost
                            previousVertex[neighbor] = current_vertex
        return D, previousVertex

def Dijkstra():
    g = Graph(6)
    g.add_edge(0, 1, 6)
    g.add_edge(0, 3, 3)
    g.add_edge(0, 2, 5)
    g.add_edge(1, 4, 3)
    g.add_edge(1, 3, 1)
    g.add_edge(2, 3, 1)
    g.add_edge(2, 5, 2)
    g.add_edge(3, 4, 7)
    g.add_edge(3, 5, 9)
    g.add_edge(5, 4, 5)

    D, previousVertex = g.dijkstra(0)
    # print(previousVertex)

    #节点名字与数字的对应表
    dic = {0 : 's', 1 : 'v', 2 : 'u', 3 : 'w', 4 : 't', 5 : 'z'}
    path = []
    cheapest_path = []
    key = 4
    #回溯,得到源节点到目标节点的最佳路径
    while True:
        if key == 0 :
            path.append(0)
            break
        else :
            path.append(key)
            key = previousVertex[key]
    #节点名字由数字转成字符
    for point in path[ : : -1] :
        cheapest_path.append(dic[point])
    cheapest_path = "->".join(cheapest_path)

    print(f"distance from vertex {dic[0]} to vertex {dic[4]} is {D[4]},"
            f"the cheapest path is {cheapest_path}")


if __name__ == '__main__':
    Dijkstra()

结果:
Python 中的图:Dijkstra 算法文章来源地址https://www.toymoban.com/news/detail-475108.html

到了这里,关于Python 中的图:Dijkstra 算法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【路径规划】全局路径规划算法——Dijkstra算法(含python实现 | c++实现)

    路径规划与轨迹跟踪系列算法学习 最短路径算法-迪杰斯特拉(Dijkstra)算法 迪杰斯特拉dijkstra算法的python实现 Python实现迪杰斯特拉算法 迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家狄克斯特拉于1959 年提出的,因此又叫狄克斯特拉算法。是从一个节点遍历其余各节点的最短路

    2024年02月08日
    浏览(48)
  • 【路径规划】(1) Dijkstra 算法求解最短路,附python完整代码

    好久不见,我又回来了, 这段时间把路径规划的一系列算法整理一下 ,感兴趣的点个关注。今天介绍一下机器人路径规划算法中最基础的 Dijkstra 算法,文末有 python 完整代码,那我们开始吧。 1959 年,荷兰计算机科学家 ·EdsgerWybe·Dijkstra 发表了论文《 A note on two problems in c

    2023年04月08日
    浏览(48)
  • ROS:move_base路径规划介绍、更换全局路径规划算法(A star、Dijkstra、DWA,测试当前是哪种算法,效果展示图)

    前提: 需要安装navigation包 ,才可以运行move_base。 move_base包默认算法: 全局路径规划:Dijkstra; 局部路径规划:航迹推算; A*、Dijkstra属于全局路径规划、DWA属于局部路径规划。 move_base.launch文件需要 添加以下内容 : 整体的move_base.launch文件内容如下(其中 turtlebot3_navigati

    2024年02月08日
    浏览(225)
  • 数据结构与算法 —— 最短路径Dijkstra算法(迪杰斯特拉)详细图解以及python实现

    目录 前言 1. 介绍 2. 加权图 2.1 概念 3. 最短路径 -- Dijkstra 算法 3.1 历史 3.2 Dijkstra 算法的基本思路 3.3 Dijkstra 算法图解 4.  python中dijkstra算法的实现 5. 总结  前两章我们讲到了关于图的基本知识,和广度/深度优先搜索。 本章,我们将介绍 加权图 和 最短路径 的相关知识。 最

    2024年02月12日
    浏览(52)
  • python实现Dijkstra算法求解最短路径问题(Shortest Path Problem)

    最短路问题(Shortest Path Problem,SPP)是 图论 的一个经典问题,其目的在于寻找网络中任意两个节点间的最短路径,这里的最短可以衍生为距离最短、费用最小、时间最短等一系列度量。当前的涌现了很多最短路径的变种,如带资源约束的最短路径问题(Shortest Path Problem wit

    2024年02月09日
    浏览(43)
  • Python中的命名元组(namedtuple)到底是什么东西?干嘛用的?

    Python中有一种特殊的元组叫做命名元组,英文名叫namedtuple。 为什么要用命名元组呢? 思考下面的问题: 如何设计数据结构承载一个五维的数据,如一个学生的基本信息? 方法有二: 1. Python是面向对象语言,可以使用class,定义一个学生类,将五维信息作为属性,这是一个

    2024年02月10日
    浏览(52)
  • 【华为OD机试】最小传输时延I(Dijkstra 算法—Java&Python&C++&JS实现)

    本文收录于专栏:算法之翼 本专栏所有题目均包含优质解题思路,高质量解题代码(JavaPythonC++JS分别实现),详细代码讲解,助你深入学习,深度掌握!

    2024年04月25日
    浏览(30)
  • 294.【华为OD机试】路口最短时间问题( Dijkstra 算法Java&Python&C++&JS实现)

    🚀点击这里可直接跳转到本专栏,可查阅顶置最新的华为OD机试宝典~ 本专栏所有题目均包含优质解题思路,高质量解题代码(JavaPythonC++JS分别实现),详细代码讲解,助你深入学习,深度掌握!

    2024年03月13日
    浏览(78)
  • Stable Diffusion如何生成高质量的图-prompt写法介绍

    Stable Diffusion是一个开源的图像生成AI系统,由Anthropic公司开发。它基于 Transformer模型架构,可以通过文字描述生成高质量的图像。 Stable Diffusion的主要特点包括: 强大的图像生成能力。它可以根据文本描述生成非常逼真的图像,包括人物、风景、动物等各种主题。 对文本的理解能

    2024年02月16日
    浏览(39)
  • 图论:一文教你读懂常见的图遍历算法

    深度优先搜索(DFS): 从一个起始节点开始,访问该节点并将其标记为已访问。 递归地访问所有与当前节点直接相连且未被访问过的节点。 重复上述步骤,直到所有节点都被访问过或没有未访问的节点。 广度优先搜索(BFS): 从一个起始节点开始,将其放入队列中,并标

    2024年04月25日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包