当前位置:   article > 正文

最短路径 Dijkstra算法的Matlab代码实现_dijkstra算法matlab代码

dijkstra算法matlab代码

 说明:

2024年6月9日更新代码

主要修改:

  1. 增加了对输入参数的判断
  2. 简化了部分变量的大小
  3. 明确了数据的使用范围
  1. % 文件名:dijkstra.m
  2. % 时间:2020年9月12日
  3. % 来源:https://blog.csdn.net/lishan132/article/details/108527271
  4. % 功能:利用dijkstra算法计算两点间的最短路径
  5. % dist:起点与终点之间的最短距离值
  6. % path:最短路径索引
  7. % Dist:最短路径下的距离值
  8. % A:邻接矩阵
  9. % strat:起点编号
  10. % dest:终点编号
  11. %{
  12. 测试数据
  13. A =[0,12,inf,inf,inf,16,14;
  14. 12,0,10,inf,inf,7,inf;
  15. inf,10,0,3,5,6,inf;
  16. inf,inf,3,0,4,inf,inf;
  17. inf,inf,5,4,0,2,8;
  18. 16,7,6,inf,2,0,9;
  19. 14,inf,inf,inf,8,9,0];
  20. start = 1;
  21. dest = 4;
  22. [dist, path, Distance] = dijkstra(A, start, dest);
  23. %}
  24. function [dist, path, Dist] = dijkstra(A, start, dest)
  25. if size(A, 1) ~= size(A, 2)
  26. error('邻接矩阵A的行列数不一致');
  27. else
  28. for i = 1:size(A, 1)
  29. for j = 1:size(A, 1) - i + 1
  30. if A(i, j) ~= A(j, i)
  31. error('邻接矩阵A不是对称矩阵');
  32. end
  33. end
  34. end
  35. if (start < 1 || start > size(A, 1))
  36. error('起点start超出了节点范围');
  37. end
  38. if (start ~= round(start))
  39. error('起点start不是整数');
  40. end
  41. if (dest < 1 || dest > size(A, 1))
  42. error('终点dest超出了节点范围');
  43. end
  44. if (dest ~= round(dest))
  45. error('终点dest不是整数');
  46. end
  47. end
  48. % 计算程序运行时间
  49. tic %开始计时
  50. % 初始化操作
  51. p = size(A, 1); %计算顶点数目
  52. S = (dest); %初始化集合S, 已加入到路径中的顶点编号
  53. U = setdiff(1:p, S); %初始化集合U, 未加入到路径中的顶点编号
  54. Dist(1, 1:p) = A(dest, 1:p); %初始化所有顶点到终点dest的距离
  55. D(1, 1:p) = Dist(1, 1:p); %初始化所有顶点到当前顶点再到终点dest的距离
  56. path(1, 1:p) = 0; %初始化最短路径节点连接记录表
  57. path(1, D~=inf) = dest; %距离值不为无穷大时,将两顶点相连
  58. % 寻找最短路径
  59. while ~isempty(U) %判断U中元素是否为空
  60. D(1, S) = inf; %忽略已处理顶点的距离值
  61. k = find(D(1:p) == min(D(1:p))); %剩余顶点中距离终点最近的顶点编号
  62. %更新顶点
  63. S = [S, k]; %将顶点k添加到S中
  64. U(U == k) = []; %从U中删除顶点k
  65. %计算距离
  66. D(1, 1:p) = A(k, 1:p) + D(1, k); %先通过结点k,再到终点的距离值
  67. D(1, 1:p) = min(D(1, 1:p), Dist(1, 1:p)); %取最小距离
  68. %更新路径
  69. path(1, D(1, 1:p) ~= Dist(1, 1:p)) = k; %更改连接关系,连接到结点k上
  70. %更新距离
  71. Dist(1, 1:p) = D(1, 1:p); %更新距离表为所有点到终点的最小值
  72. end
  73. dist = Dist(1, start); %取出指定起点到终点的距离值
  74. toc %计时结束
  75. % 输出结果
  76. fprintf('找到的最短路径为:');
  77. while start ~= dest %到达终点时结束
  78. fprintf('%d-->', start); %打印当前点编号
  79. next = path(1, start); %与当前点相连的下一顶点
  80. start = next; %更新当前点
  81. end
  82. fprintf('%d\n', dest);
  83. fprintf('最短路径对应的距离为:%d\n', dist);
  84. end

前言

Dijkstra是一种用于计算最短路径的常用算法,由荷兰科学家Dijkstra在1956年提出

该算法主要适用于单源非负权边的无向图,其中:

(1)单源表示只有一个源节点,然后计算其它所有节点到该源节点的最短路径和距离

(2)非负权表示两个节点之间的权重是非负数

(3)无向表示节点之间没有方向的区别,从节点A到节点B与从节点B到A的权重值相同

求解示例

假如有A、B、C、D这4个快递站点,它们之间的距离如下:

  • 从A到B和从B到A距离相同,为3
  • 从A到C和从C到A均不可直接到达,距离为无穷大,记为inf
  • 从A到D和从D到A距离相同,为4
  • 从B到C和从C到B距离相同,为7
  • 从C到D和从D到C距离相同,为1
  • 各站点到自身的距离记为0

现在求各站点到C站点的最短路径

以站点为节点,以站点间的距离值为连接这两个节点的边的权重,可得到如下的无向图

  

用邻接矩阵表示就是

ABCD
A03inf4
B3072
Cinf701
D4210

可以看到,对于无向图而言,以邻接矩阵的对角线分界,左下三角和右上三角是完全对称的

现在指定源节点为C,求其他节点到C的最短路径,解决方法如下:

初始状态

设置一个数组s,记录已经处理过的节点,因为C为源节点,所以初始状态为:{C}

设置一个数组u,记录还未处理的节点,因为C已经确定为源节点,所以初始状态为{A,B,D}

设置一个数组dist,依次记录各节点到源节点C的距离(inf,7,0,1)

当前处理节点设置为源节点

设置一个数组d,记录各节点到当前处理节点,再到源节点的距离值(inf,7,0,1)

设置一个最短路径记录表path,记录各节点的最短路径连接关系{0,C,C,C}

结合数组dist,有:

  • A->C=inf
  • B->C=7
  • C->C=0
  • D->C=1

第1轮处理

当前处理节点为C,dist为(inf,7,0,1)

寻找:在数组u{A,B,D}中寻找距离C最近的节点,从邻接矩阵中或状态图中可以看到,节点D距离C最短,距离值为1

ABCD
A03inf4
B3072
Cinf701
D4210

更新:

(1)更新数组u为{A,B}

(2)更新数组s为{C,D}

(3)更新数组d为:计算各节点到D,加上dist中D到C的距离1

ABCD
4+1=52+1=31+1=20+1=1

(4)更新数组dist为:取dist(inf,7,0,1)和d的最短距离值

ABCD
min(inf,5)=5min(7,3)=3min(0,2)=0min(1,1)=1

(5)dist中的A、B发生变化,更新各节点的最短路径连接表path:{D,D,C,C},

结合数组dist,有:

  • A->D->C=5
  • B->D->C=3
  • C->C=0
  • D->C=1

 第2轮处理

当前处理节点更新为D,dist为(5,3,0,1)

寻找:在数组u{A,B}中寻找距离D最近的节点,从邻接矩阵中或状态图中可以看到,节点B距离D最短,距离值为2

ABCD
A03inf4
B3072
Cinf701
D4210

更新:

(1)更新数组u为{A}

(2)更新数组s为{C,D,B}

(3)更新数组d为:计算各节点到B,加上dist中B到C的距离3

ABCD
3+3=60+3=37+3=102+3=5

(4)更新数组dist为:取dist(5,3,0,1)和d的最短距离值

ABCD
min(5,6)=5min(3,3)=3min(0,10)=0min(1,5)=1

(5)dist未变化,因此path最短路径记录表也就不变化,path:{D,D,C,C},

  • A->D->C=5
  • B->D->C=3
  • C->C=0
  • D->C=1

  第3轮处理

当前处理节点更新为B,dist为(5,3,0,1)

寻找:在数组u{A}中寻找距离B最近的节点,从邻接矩阵中或状态图中可以看到,只剩下节点A,自然是节点A距离B最短,距离值为3

ABCD
A03inf4
B3072
Cinf701
D4210

更新:

(1)更新数组u为{}

(2)更新数组s为{C,D,B,A}

(3)更新数组d为:计算各节点到A,加上dist中A到C的距离5

ABCD
0+5=53+5=8inf+5=inf4+5=9

(4)更新数组dist为:取dist(5,3,0,1)和d的最短距离值

ABCD
min(5,5)=5min(3,8)=3min(0,inf)=0min(1,9)=1

(5)dist未变化,因此path最短路径记录表也就不变化,path:{D,D,C,C},

  • A->D->C=5
  • B->D->C=3
  • C->C=0
  • D->C=1

代码实现

为了搞清楚最短路径的算法过程,自己编写代码实现dijkstra算法寻找路径

  1. % 文件名:dijkstra.m
  2. % 时间:2020年9月12日
  3. % 来源:https://blog.csdn.net/lishan132/article/details/108527271
  4. % 功能:利用dijkstra算法计算两点间的最短路径
  5. % dist:起点与终点之间的最短距离值
  6. % path:最短路径索引
  7. % Distance:最短路径下的距离值
  8. % A:邻接矩阵
  9. % strat:起点编号
  10. % dest:终点编号
  11. function [dist,path,Distance] = dijkstra(A,start,dest)
  12. % 测试数据 A =[0,12,inf,inf,inf,16,14;12,0,10,inf,inf,7,inf;inf,10,0,3,5,6,inf;inf,inf,3,0,4,inf,inf;inf,inf,5,4,0,2,8;16,7,6,inf,2,0,9;14,inf,inf,inf,8,9,0];
  13. % 测试数据 start = 1;
  14. % 测试数据 dest = 4;
  15. % 计算程序运行时间
  16. tic %开始计时
  17. % 初始化操作
  18. p = size(A,1); %计算顶点数目
  19. S(1) = dest; %初始化集合S,已加入到路径中的顶点编号
  20. U = 1:p; %初始化集合U,未加入到路径中的顶点编号
  21. U(dest) = []; %删除终点编号
  22. Distance = zeros(2,p); %初始化所有顶点到终点dest的距离
  23. Distance(1,:) = 1:p; %重赋值第一行为各顶点编号
  24. Distance(2,1:p) = A(dest,1:p); %重赋值第二行为邻接矩阵中各顶点到终点的距离
  25. new_Distance = Distance;
  26. D = Distance; %初始化U中所有顶点到终点dest的距离
  27. D(:,dest) = []; %删除U中终点编号到终点编号的距离
  28. path = zeros(2,p); %初始化路径
  29. path(1,:) = 1:p; %重赋值第一行为各顶点编号
  30. path(2,Distance(2,:)~=inf) = dest; %距离值不为无穷大时,将两顶点相连
  31. % 寻找最短路径
  32. while ~isempty(U) %判断U中元素是否为空
  33. index = find(D(2,:)==min(D(2,:)),1); %剩余顶点中距离最小值的索引
  34. k = D(1,index); %发现剩余顶点中距离终点最近的顶点编号
  35. %更新顶点
  36. S = [S,k]; %将顶点k添加到S中
  37. U(U==k) = []; %从U中删除顶点k
  38. %计算距离
  39. new_Distance(2,:) = A(k,1:p)+Distance(2,k); %计算先通过结点k,再从k到达终点的所有点距离值
  40. D = min(Distance,new_Distance); %与原来的距离值比较,取最小值
  41. %更新路径
  42. path(2,D(2,:)~=Distance(2,:)) = k; %出现新的最小值,更改连接关系,连接到结点k上
  43. %更新距离
  44. Distance = D; %更新距离表为所有点到终点的最小值
  45. D(:,S) = []; %删除已加入到S中的顶点
  46. end
  47. dist = Distance(2,start); %取出指定起点到终点的距离值
  48. toc %计时结束
  49. % 输出结果
  50. fprintf('找到的最短路径为:');
  51. while start ~= dest %到达终点时结束
  52. fprintf('%d-->',start); %打印当前点编号
  53. next = path(2,start); %与当前点相连的下一顶点
  54. start = next; %更新当前点
  55. end
  56. fprintf('%d\n',dest);
  57. fprintf('最短路径对应的距离为:%d\n',dist);
  58. end

此函数共有3个输入参数,3个输出参数

输入参数说明

A:邻接矩阵,存储各顶点之间的距离值,是一个大小为顶点个数的方阵,对角线元素为0
strat:起点编号
dest:终点编号

输出参数说明

dist:指定起点与终点之间的最短距离值
path:最短路径索引,一共两行,第一行的值依次为各顶点编号,第二行的值为与第一行顶点相连的顶点编号
Distence:最短路径下的距离值,一共两行,第一行的值依次为各顶点编号,第二行的值为对应顶点到终点的最小距离值

算法测试

算法有效性的测试如下:

 根据上图,想计算A点到D点的最短路径和距离,经过理论分析,其最短路径应为A-->F-->E-->D,最短距离为16+2+4=22

下面输入代码进行验证

输入代码

  1. A =[0,12,inf,inf,inf,16,14;12,0,10,inf,inf,7,inf;inf,10,0,3,5,6,inf;inf,inf,3,0,4,inf,inf;inf,inf,5,4,0,2,8;16,7,6,inf,2,0,9;14,inf,inf,inf,8,9,0];
  2. start = 1;
  3. dest = 4;
  4. [dist,path,Distance] = dijkstra(A,start,dest)

时间已过 0.005424 秒。
找到的最短路径为:1-->6-->5-->4
最短路径对应的距离为:22

dist =

    22


path =

     1     2     3     4     5     6     7
     6     3     4     4     4     5     5


Distance =

     1     2     3     4     5     6     7
    22    13     3     0     4     6    12

 输入其他任意两个点,换一个距离矩阵,依然能正确输出最短路径和相应的距离值,算法的有效性得到验证

 输入以下代码可生成最终的最短路径图,输出结果与起点值无关,任意点到D点的最短路径均可从图中找到

  1. A =[0,12,inf,inf,inf,16,14;12,0,10,inf,inf,7,inf;inf,10,0,3,5,6,inf;inf,inf,3,0,4,inf,inf;inf,inf,5,4,0,2,8;16,7,6,inf,2,0,9;14,inf,inf,inf,8,9,0];
  2. start = 1;
  3. dest = 4;
  4. [~,path,Distance] = dijkstra(A,start,dest)
  5. path(:,dest) = [];
  6. Distance(:,dest) = [];
  7. s = path(1,:);
  8. t = path(2,:);
  9. weights = Distance(2,:);
  10. names = {'A' 'B' 'C' 'D' 'E' 'F' 'G'};
  11. g = digraph(s,t,weights,names);
  12. plot(g,'EdgeLabel',g.Edges.Weight)

 可以看到,图中所有点均向D点聚集,且显示了每一个点到D点的最短距离

对比分析

下面,使用matlab图论工具箱的函数寻找最短路径,再进行一个对比验证

图论工具箱中求最短路径的函数有以下3个,本文使用shortestpath,matlab命令窗口中输入doc shortestpath即可查看用法

shortestpath    两个单一节点之间的最短路径
shortestpathtree    从节点的最短路径树
distances    所有节点对组的最短路径距离

  1. % 文件名:shortpath.m
  2. % 时间:2020年9月12日
  3. % 来源:https://blog.csdn.net/lishan132/article/details/108527271
  4. % 功能:利用matlab自带的shortestpath函数计算两点间的最短路径
  5. % dist:起点与终点之间的最短距离值
  6. % path:最短路径
  7. function [dist,path] = shortpath(A,start,dest)
  8. %使用matlab自带的函数计算最短路径
  9. tic
  10. A(A==inf) = 0; %将无穷大值变为0
  11. [t,s,weights] = find(A); %邻接矩阵中非零值的列、行号索引,及对应值
  12. G = digraph(s,t,weights); %生成一幅带权值的有向图
  13. [path,dist] = shortestpath(G,start,dest); %计算最短路径
  14. toc
  15. %展示结果
  16. plot(G,'EdgeLabel',G.Edges.Weight)
  17. fprintf('找到的最短路径为:');
  18. fprintf('%d ',path);
  19. fprintf('\n');
  20. fprintf('最短路径对应的距离为:%d\n',dist);
  21. end

 命令窗口输入以下代码验证结果

  1. A =[0,12,inf,inf,inf,16,14;12,0,10,inf,inf,7,inf;inf,10,0,3,5,6,inf;inf,inf,3,0,4,inf,inf;inf,inf,5,4,0,2,8;16,7,6,inf,2,0,9;14,inf,inf,inf,8,9,0];
  2. start = 1;
  3. dest = 4;
  4. [dist,path] = shortpath(A,start,dest)

时间已过 0.002112 秒。
找到的最短路径为:1 6 5 4 
最短路径对应的距离为:22

dist =

    22


path =

     1     6     5     4

两者结果一致,再次验证算法的有效性,而且自己写的Dijkstra算法的代码还能够一次输出所有点到终点的距离及路径表

仅一次测试以及少量的数据规模N不足以说明算法的解决效率,为了对两个算法性能进行一个比较,特地写了一个测试程序,输入的数据规模N从10到2000变化,并注释dijkstra.m、shortpath.m两个文件中的计时和输出结果部分的代码,程序如下

  1. % 文件名:compar1.m
  2. % 时间:2020年9月12日
  3. % 来源:https://blog.csdn.net/lishan132/article/details/108527271
  4. % 功能:比较自己实现的dijkstra算法与matlab图论工具箱函数的效率性能
  5. % 说明:请先将dijkstra.m、shortpath.m文件与本文件放在同一目录下
  6. clear
  7. close
  8. clc
  9. iter = 200; %测试次数
  10. t1 = zeros(1,iter); %算法1时间
  11. t2 = zeros(1,iter); %算法2时间
  12. for i = 1:iter
  13. %% 第一步:生成测试数据,距离矩阵A,起点start,终点dest
  14. clearvars -except iter i t1 t2 %清空除iter,i,t1,t2外的所有变量
  15. N = i*10; %输入数据规模
  16. ub = 15; %输入数据距离上限
  17. A = unifrnd (0, ub, N, N); %生成一个服从均匀分布的矩阵,数值范围[0,ub],矩阵大小n×n
  18. A = A - A';
  19. A(A<0) = inf;
  20. start = round(rand(1,1)*(N-1))+1;
  21. dest = round(rand(1,1)*(N-1))+1;
  22. while start == dest
  23. dest = round(rand(1,1)*(N-1))+1;
  24. end
  25. %% 第二步:计算自己编写的dk算法的运行时间
  26. tic %开始计时
  27. dijkstra(A,start,dest);
  28. t1(i) = toc; %计时结束
  29. %% 第三步:计算使用matlab自带图论工具箱算法的运行时间
  30. tic %开始计时
  31. shortpath(A,start,dest);
  32. t2(i) = toc; %计时结束
  33. end
  34. %% 第四步:绘制算法所需时间图
  35. plot(1:iter,t1);
  36. hold on
  37. plot(1:iter,t2);
  38. legend("文中dijkstra算法","图论工具shortestpath函数")
  39. title("算法耗费时间比较")
  40. saveas(gcf, "result.png")

惊喜地发现,随着数据规模的增大,自己写的Dijkstra算法与图论工具函数shortestpath相比,耗时更低,而且差距越来越大。

当然,此处还是有些不严谨,因为我在使用图论工具函数shortestpath解决问题时,前面自己还写了三条参数的处理语句,这部分语句的处理过程也是算入时间的

结论

本文实现了dijkstra算法的Matlab代码,并封装成一个函数,给定一个邻接矩阵,以及指定一个终点,可以直接输出任意点到终点的最短路径和相应的距离值,相对matlab自带的图论工具箱函数,其运算速度快,输出数据全,方便二次开发,提高效率。但自己写的程序中每次只寻找一个新的结点加入,有多少个结点,while中的循环体就要执行多少次,每一次循环均要更新一次所有结点到终点的距离值,当结点数据非常大时,时间复杂度为O(N^2),因此还有继续优化的空间,根据相关文献,可用堆进行优化,也可从能否一次加入多个新结点,而不是一个来考虑加快搜索速度。

参考文章:

[1] 数据结构--Dijkstra算法最清楚的讲解 数据结构--Dijkstra算法最清楚的讲解_heroacool的博客-CSDN博客

[2] 通俗易懂理解——dijkstra算法求最短路径 (七)通俗易懂理解——dijkstra算法求最短路径 - 知乎

[3] Dijkstra算法及其matlab实现 Dijkstra算法及其matlab实现_Anonymoushi的博客-CSDN博客

[4] 李建东,盛敏编著. 通信网络基础. 北京:高等教育出版社, 2004.08.

[5] 迪杰斯特拉 & 堆优化 迪杰斯特拉 & 堆优化_ExRoc的博客-CSDN博客

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/891220
推荐阅读
相关标签
  

闽ICP备14008679号