一、Floyd算法介绍?
Floyd‐Warshall算法(英语:Floyd‐Warshall algorithm或简写为Floyd algorithm),中文亦称弗洛伊德算法,是解决任意两点间的最短路径的一种算法,可以正确处理无向图或有向图(可以有负权重,但不可存在负权回路)的最短路径问题。
Floyd算法与迪杰斯特拉算法或贝尔曼福特算法相比,能够一次性的求出任意两点之间的最短路径,后两种算法运行一次只能计算出给定的起点和终点之间的最短路径。当然,Floyd算法计算的时间也要高于后两种算法O(n^3),算法核心思想:
- 如果某个节点(例如点8)位于从起点0到终点4的最短路径上,那么:
从0到4的最短路径的距离= 从0到8的最短路径的距离+从8到4的最短路径的距离 - 如果某个节点(例如点3)不在从起点0到终点4的最短路径上,那么:
从0到4的最短路径的距离≤ 从0到3的最短路径的距离+从3到4的最短路径的距离(注:这里写≤号是因为我们最终求出来的最短路径的走法可能不唯一)
- 动态过程参见视频(很好的一个视频)
- 其算法核心的步骤由三层循环构成:
for k=1:n % 中间节点k从1- n 循环
for i=1:n % 起始节点i从1- n 循环
for j=1:n % 终点节点j从1-n 循环
if dist(i,j)>dist(i,k)+dist(k,j) % 如果i,j两个节点间的最短距离大于i和k的最短距离+k和j的最短距离
dist(i,j)=dist(i,k)+dist(k,j); % 那么我们就令这两个较短的距离之和取代i,j两点之间的最短距离
path(i,j)=path(i,k); % 起点为i,终点为j的两个节点之间的最短路径要经过的节点更新为path(i,k)
% 注意,上面一行语句也能写成path(i,j) = k,不过遍历路径的时候就得倒过来,暂不讨论
end
end
end
end
path(i,j)=path(i,k)
对这一句不理解的,我提一下自己的看法:这句的意思就是把i->j的路径改为先到中间点k,那怎么到中间点k呢?那就是path(i,k)。
二、matlab实现
1. 主程序:
clc;clear
%% 初始化
n=5;%节点个数
D=ones(n)./zeros(n);%初始化距离矩阵为inf
for i=1:n
D(i,i)=0;%初始化对角线元素为0
end
%路径权重
D(1,2) = 3;
D(1,3) = 8;
D(1,5) = -4;
D(2,5) = 7;
D(2,4) = 1;
D(3,2) = 4;
D(4,3) = -5;
D(5,4) = 6;
D(4,1) = 2;
%% 求解最短路径
[dist,path]=self_Floyd_algorithum(D)
self_print_path(path,dist,1,5)
self_print_path(path,dist,1,4)
self_print_path(path,dist,3,1)
2. 函数
- 弗洛伊德函数
function [dist,path]=self_Floyd_algorithum(D)
%% 该函数用于求解一个权重邻接矩阵任意两个节点之间的最短路径
% 输入:
% D是权重邻接矩阵
% 输出:
% dist是最短距离矩阵,其元素dist_ij表示表示i,j两个节点的最短距离
% path是路径矩阵,其元素path_ij表示起点为i,终点为j的两个节点之间的最短路径要经过的节点
n=size(D,1);%计算节点的个数
%初始化dist矩阵
dist=D;
%初始化path矩阵
path=zeros(n);
for j=1:n
path(:,j)=j; %将第j列的元素变为j
end
for i=1:n
path(i,i)=-1;%将主对角线元素变为-1
end
%% 下面开始关键循环
for k=1:n %中间节点k从1- n 循环
for i=1:n % 起始节点i从1- n 循环
for j=1:n % 终点节点j从1-n 循环
if dist(i,j)>dist(i,k)+dist(k,j)% 如果i,j两个节点间的最短距离大于i和k的最短距离+k和j的最短距离
dist(i,j)=dist(i,k)+dist(k,j); % 那么我们就令这两个较短的距离之和取代i,j两点之间的最短距离
path(i,j)=path(i,k);% 起点为i,终点为j的两个节点之间的最短路径要经过的节点更新为path(i,k), 注意,也能写成path(i,j) = k,但是遍历路径方式倒过来了,暂不讨论;
end
end
end
end
end
- 打印函数
function []=self_print_path(path,dist,i,j)
%% 该函数的作用是打印从i到j经过的最短路径
% 输入:
% path是使用floyd算法求出来的路径矩阵
% dist是使用floyd算法求出来的最短距离矩阵
% i是起始节点的编号
% j是终点节点的编号
% 输出:无
if i==j
warning('七点和终点相同,请重新输入')
return;
end
if path(i,j)==j
if dist(i,j)==inf
disp('从',num2str(i),'到',num2str(j),'没有路径可以到达')
else
disp(['从',num2str(i),'到',num2str(j),'的最短路径为'])
disp([num2str(i),' ---> ',num2str(j)])
disp(['最短距离为',num2str(dist(i,j))])
end
else
k = path(i,j);
result = [num2str(i),' ---> ']; % 初始化要打印的这个字符串
while k ~= j % 只要k不等于j, 就一直循环下去
result = [result , num2str(k) , ' ---> ' ]; % i先走到k这个节点处
k = path(k,j);
end
result = [result , num2str(k)];
disp(['从',num2str(i),'到',num2str(j),'的最短路径为'])
disp(result)
disp(['最短距离为',num2str(dist(i,j))])
end
end
三、扩展
1、完备性证明
2、特殊情况
比如不经过某些点的时候只需要在Floyd函数遍历中去掉该点即可。
四、参考阅读
清风数学建模
参考如何证明弗洛伊德算法的完备性?