Flyod算法


一、Floyd算法介绍?

Floyd‐Warshall算法(英语:Floyd‐Warshall algorithm或简写为Floyd algorithm),中文亦称弗洛伊德算法,是解决任意两点间的最短路径的一种算法,可以正确处理无向图有向图可以有负权重,但不可存在负权回路)的最短路径问题。
Floyd算法与迪杰斯特拉算法或贝尔曼福特算法相比,能够一次性的求出任意两点之间的最短路径,后两种算法运行一次只能计算出给定的起点和终点之间的最短路径。当然,Floyd算法计算的时间也要高于后两种算法O(n^3)算法核心思想:

  1. 如果某个节点(例如点8)位于从起点0到终点4的最短路径上,那么:
    从0到4的最短路径的距离= 从0到8的最短路径的距离+从8到4的最短路径的距离
  2. 如果某个节点(例如点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函数遍历中去掉该点即可。

参考如何证明弗洛伊德算法的完备性?

四、参考阅读

清风数学建模
参考如何证明弗洛伊德算法的完备性?

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值