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)
,则更新s
到m
的最短路径为cheapestPath(s, n)
+cheapestPath(n, m)
。 - 且每次更新最短路径时会保存该节点的前节点。
下面用一个无向、加权、连通图来解释上述规则原理:
假设 Vertex 0 为起点(源节点)。我们将把图中顶点的初始成本设置为无穷大,除了起始顶点:
我们选择成本最低的顶点——即 Vertex 0。我们将其标记为已访问并将其添加到我们的已访问顶点集。起始节点将始终具有最低成本,因此它将始终是第一个添加的节点,同时标记 Vertex 0,之后不用再进行访问:
然后,我们将更新相邻顶点(1 和 6)的成本。由于 0 + 4 < infinity
和 0 + 7 < infinity
,我们更新这些顶点的成本:
现在我们访问未标记顶点中的最小成本顶点。4 < 7
,所以我们遍历到 Vertex 1:
在遍历时,我们将其标记为已访问,然后观察并更新相邻顶点:2、6 和 7:
- 因为
4 + 9 < infinity
,Vertex 2 的新成本将是 13。 - 因为
4 + 11 > 7
,Vertex 6 的成本将保持为 7。 - 因为
4 + 20 < infinity
,Vertex 7 的新成本将是 24。
则新成本如下:
此时在未标记的顶点中 Vertex 6的成本最小,因此将其作为出发点,将其标记为已访问并更新其相邻顶点的成本:
该过程继续到 Vertex 7:
再一次,到 Vertex 4:
再一次,到 Vertex 2:
我们要考虑的唯一顶点是 Vertex 3。由于11 + 6 < 19
,Vertex 3 的成本被更新。然后继续到 Vertex 8。
然后更新 Vertex 3:
最后更新 Vertex 5:
没有更多未访问的顶点可能需要更新。我们的最终成本表示从节点 0 到图中每个其他节点的最短路径:
动画演示如下:来自 https://www.youtube.com/watch?v=JLARzu7coEs
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
:以矩阵的形式表示边的列表。对于节点u
和v
,self.edges[u][v] = weight
。visited
:将包含访问的顶点的集合。
定义一个将向图形添加边的方法:
def add_edge(self, u, v, weight):#记录u,v两节点之间的距离#要注意的是如果是有向图只需定义单向的权重#如果是无向图则需定义双向权重self.edges[u][v] = weightself.edges[v][u] = weight
定义 Dijkstra 算法:
def dijkstra(self, start_vertex):#开始时定义源节点到其他所有节点的距离为无穷大D = {v: float('inf') for v in range(self.vertices)}#源节点到自己的距离为0D[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):#邻居节点之间距离不能为-1if 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] + distanceif new_cost < old_cost:#加入优先队列pq.put((new_cost, neighbor))D[neighbor] = new_costpreviousVertex[neighbor] = current_vertexreturn D, previousVertex
在这段代码中,首先创建了一个大小为 num_of_vertices
的字典 D
,用于统计最短路径的成本。整个列表初始化为无穷大。这将是一个列表,我们在其中保留从 start_vertex
所有其他节点的最短路径。我们将起始顶点的值设置为 0,因为这是它与自身的距离。然后,我们初始化一个优先级队列,我们将使用它来快速将顶点从最远到最远排序。我们将起始顶点放入优先级队列中。再然后初始化previousVertex
,用于标记每个节点的前节点。现在,对于优先队列中的每个顶点,我们将首先将它们标记为已访问,然后我们将遍历它们的邻居。如果邻居没有被访问,我们将比较它的旧成本和新成本。旧代价是从起始顶点到邻居的最短路径的当前值,而新代价是从起始顶点到当前顶点的最短路径与当前顶点到相邻顶点的距离之和的值邻居。如果新成本低于旧成本,我们将邻居及其成本放入优先级队列,并相应地更新我们保留最短路径的列表,同时更新邻居节点的前节点。最后,在所有顶点都被访问并且优先级队列为空之后,我们返回字典 D
和 previousVertex
。
实现之前的例子:
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]}")
结果:
测试
得到节点s和t之间的最短路径。
上图是一个有向图,而我们在第一个例子中定义的是无向图,因此要修改代码。在代码中与无向图的差异只是一行,即定义权重时只定义一边,只需注释掉 self.edges[v][u] = weight
即可。为了实现方便,图中节点的名称并未使用字符表示,而是使用数字进行表示,在搜索完之后根据字典再对应回去。
from queue import PriorityQueueclass 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] = weightdef dijkstra(self, start_vertex):#开始时定义源节点到其他所有节点的距离为无穷大D = {v: float('inf') for v in range(self.vertices)}#源节点到自己的距离为0D[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):#邻居节点之间距离不能为-1if 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] + distanceif new_cost < old_cost:#加入优先队列pq.put((new_cost, neighbor))D[neighbor] = new_costpreviousVertex[neighbor] = current_vertexreturn D, previousVertexdef 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)breakelse :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 算法相关推荐
- 数据结构课程设计——项目2:校园导游咨询(图 Dijkstra算法寻找最短路径)
数据结构课程设计--项目2:校园导游咨询(图 Dijkstra算法寻找最短路径) 一.问题描述和项目要求 1.1 问题描述 1.2 基本要求 二.问题分析 2.1 程序功能设计分析 2.2 程序实现分 ...
- [转载] Python中的Phyllotaxis模式| 算法植物学的一个单位
参考链接: Python中的Phyllotaxis模式| 算法植物学的单位 简介| 叶底 Phyllotaxis / phyllotaxy是植物茎上叶子的排列,Phyllotactic螺旋形成自然 ...
- chatgpt赋能python:Python中的图中图:什么是图中图?如何使用?
Python中的图中图:什么是图中图?如何使用? 在Python中的数据可视化中,图中图是一种非常有用的工具,它可以帮助我们以一种清晰而有用的方式展示更多的信息.从字面上看,图中图意味着图形中包含另一 ...
- 选取最优模型--scikit-learn的Python中抽查回归机器学习算法
抽查是一种发现哪种算法在机器学习问题上表现良好的方法. 无法事先知道哪种算法是最适合的,必须尝试多种方法并将注意力集中在那些证明最有效果的方法上. ------------------- 概述 本文将 ...
- python中加减乘除_Python基础算法综合:加减乘除四则运算方法
#!usr/bin/env python # -*- coding:utf-8 -*- #python的算法加减乘除用符号:+,-,*,/来表示 #以下全是python2.x写法,3.x以上请在pyt ...
- Python中Tkinter 图形化界面设计(详细教程)
Python-Tkinter 图形化界面设计(详细教程) 一.图形化界面设计的基本理解 二. 窗体控件布局 2.1 根窗体呈现示例 2.2 tkinter 常用控件 2.2.1常用控件 2.2.2 控 ...
- 中国最大的python社区-python中的最小最大算法
在minmax算法中,如何确定函数何时到达树尾并中断递归调用. 我做了一个max函数,其中我调用了min函数.在min函数中,我应该做什么?对于最大功能,我只是返回最佳成绩. def maxAgent ...
- python中e-r图_E-R图基本步骤
以自底向上设计概念结构的方法为例,它通常分为两步: 第一步:首先要根据需求分析的结果(数据流图.数据字典等)对现实世界的数据进行抽象, 设计各个局部视图即分E-R图. 第二步:集成局部视图. 概念结构 ...
- 在python中调用双模拟退火算法dual_annealing
原理 模拟退火算法(CSA)中最重要的概念就是Metrospolis准则,表示的是接收新解的概率,一般表示为 p=e−f(x2)−f(x3)Tp=e^{-\frac{f(x_2)-f(x_3)}{T} ...
最新文章
- C#使用CDO发送邮件
- JAVA入门到精通-网络编程补充和QQ案例
- find函数matlab_从零开始的matlab学习笔记——(39)find函数
- 神经网络与深度学习——TensorFlow2.0实战(笔记)(四)(python文件)
- 虚无鸿蒙哪个厉害,【图说鸿蒙】鸿蒙设定之七柱神(五)
- Invitation Cards(建反图 + 跑两遍SPFA)
- mysql数据库文件上传大小控制_[mysql数据库文件大小限制]mysql导入数据库文件最大限制设置...
- 如何通过路由器日志查看路由器是否断网过_如何正确判断宽带问题还是路由器问题?...
- 《统计学习方法》——k近邻法
- TCP/IP学习笔记12-- IP协议基础
- Mmap的实现原理和应用
- 一般线性模型和混合线性模型_从零开始的线性混合模型
- 社群裂变营销活动如何提升用户信任度?
- 安卓短信软件_【安卓清理君】极度舒适的垃圾清理工具
- 如何彻底禁用谷歌Chrome更新
- 点上并集符号的解释,并集符号上有一点,这是什么符号?文中解答
- php网站设计文档,企业门户网站设计与实现(word文档)最新版
- Python网络爬虫简单教程——第一部
- linux 配置 Apache mysql php最新版
- 【117期分享】教学说课PPT模板免费下载
热门文章
- C#如何连接wifi和指定IP
- 新闻稿怎么发,小编告诉你
- 一般是指用计算机绘制的画面,()一般指用计算机绘制的画面,如直线、圆、圆弧、矩形、任意曲线和图表等。A、图形B、图像C、动画D、图...
- 如何在r9000k 2021H上安装ubuntu和nvidia驱动
- html5的首行代码是什么,有关首行首字下沉的实现原理及代码
- ps2021神经网络滤镜不可用,ps神经网络滤镜用不了
- 毕业后进国企,你没事吧?
- 读《正见 佛陀的证悟》
- 给大家推荐一些精品内容
- 2021-2027全球及中国可吸收生物材料行业研究及十四五规划分析报告