Floyd算法能够较快地计算出任意两节点间的最短距离。
一般构造距离矩阵,先输入矩阵上三角节点间的距离,再通过转换填好下三角,(需要完整地填完距离矩阵,不然算法运行不了)。
距离矩阵对角线代表该节点到自己的距离,设置为0;两节点间没有直接通过路线的,设置为一个极大数,matlab中可以设置为inf。
Floyd的总体思想是将每个节点都可以看作是一个中转点。对于任意两节点i、j,如果节点i到中转点的距离+中转点到节点j的距离<节点i,j间的距离,就用节点i到中转点的距离+中转点到节点j的距离,代替原先节点i,j间的距离储存入距离矩阵。当遍历中转点后,距离矩阵中储存的即为两节点的最短距离。
我们还可以通过FLoyd算法同时求出任意两节点间最短距离的路径。先构造出初始的记录路径的矩阵R,称之为路由矩阵。如果两节点i,j间有直接路径,即在距离矩阵中不为极大值,那么设置R(i,j)=j,代表从节点i到节点j只需经过节点j就可到达。如果两节点i,j间无直接路径,则设置R(i,j)=0。
设中转点为k,当节点i到中转点k的距离+中转点k到节点j的距离<节点i,j间的距离时,设置R(i,j)=R(i,k)。
可以这样理解,比如当Floyd算法运行完后,我们想查看节点1到节点4的最短路径。点开路由矩阵R,发现R(1,4)=2,表示第一个中转点为2。那么查看矩阵第2行,第4列,R(2,4)=7,第二个中转点7。查看R(7,4)=3,第三个中转点为3。查看R(3,4)=4,第四个中转点为4。也即通过1,2,7,3,4,我们以最短路径从节点1抵达节点4。
floyd_my.m Floyd算法求任意两节点最短路径
function[D,R]=floyd_my(A) % A为原距离矩阵,返回D任意两节点间最短路径,R储存最短路径的路线
%Floyd—Warshall算法计算任意两节点间最短路径
n=size(A);
D=A; %初始化距离矩阵
D=triu(D)+triu(D)';
for i=1:n
D(i,i)=0; %距离矩阵对角线置0
end
for i=1:n %初始化路由矩阵,记录路径
for j=1:n
if D(i,j)==inf %两节点无直接路径,路由设置为0
R(i,j)=0;
else
R(i,j)=j; %两节点有直接路径,设置为j,代表可以到达节点j
end
end
end
for k=1:n %K中转点
for i=1:n %i,j表示任意两节点
for j=1:n
if D(i,k)+D(k,j)<D(i,j)
D(i,j)=D(i,k)+D(k,j);
R(i,j)=R(i,k);
end
end
end
end
DisplayPath.m 打印出任意两点之间的最短路径
% DisplayPath.m 打印路径函数
function DisplayPath(route, start, dest)
% 打印出任意两点之间的最短路径
% route : 路由表
% start : 起点index
% dest : 终点index
while 1 %表达式为真,一直循环直到break
if(route(start,dest)~=dest)
fprintf('%s,', num2str(start));
start = route(start,dest);
else
fprintf('%s,',num2str(start));
fprintf('%s', num2str(dest));
break;
end
end
数模7.8钢管的铁路与运输
先用matlab计算出最小费用矩阵。
clc,clear
%% 铁路子网络
rail=ones(39,39)*inf; %距离矩阵,floyd_my中可对矩阵对角线置0
n=size(rail);
rail(1,11)=202;rail(1,15)=20;rail(2,11)=1200;rail(3,16)=690;rail(4,19)=690;rail(5,18)=462;rail(6,23)=70;
rail(7,24)=30;rail(8,9)=450;rail(9,10)=1150;rail(9,12)=80;rail(10,11)=1100;rail(11,16)=720;rail(13,14)=306;
rail(14,15)=195;rail(16,17)=520;rail(17,19)=170;rail(18,19)=88;rail(19,21)=160;rail(20,21)=70;rail(21,22)=320;
rail(22,23)=160;rail(23,24)=290;%输入各节点间的距离
[rail_D,rail_R]=floyd_my(rail);%Floyd—Warshall算法,返回D任意两节点间最短路径,R储存最短路径的路线
% 计算铁路运费矩阵rail_C
rail_price=[20 23 26 29 32 37 44 50 55 60];%单位钢管的铁路运价
len=size(rail_price,2); %获取矩阵列数
price_bound=[300 350 400 450 500 600 700 800 900 1000];
for i=1:n
for j=1:n
for k=1:len
if rail_D(i,j)==inf
rail_C(i,j)=inf;
elseif rail_D(i,j)>1000
rail_C(i,j)=60+ceil((rail_D(i,j)-1000)/100)*5;% 1000km以上每增加1km至100km运价增加5万
elseif rail_D(i,j)<=price_bound(k)
rail_C(i,j)=rail_price(k);
break
end
end
end
end
%% 公路子网络
road=ones(39,39)*inf; %距离矩阵
road(1,31)=31;road(6,38)=110;road(7,39)=20;road(8,26)=3;road(10,28)=600;road(11,32)=12;road(12,27)=2;
road(13,29)=10;road(14,30)=5;road(15,31)=10;road(16,33)=42;road(17,34)=70;road(18,35)=10;
road(20,36)=10;road(22,37)=62;road(23,38)=30;road(24,39)=20;
road(25,26)=104;road(26,27)=301;
road(27,28)=750;road(28,29)=606;road(29,30)=194;road(30,31)=205;road(31,32)=201;road(32,33)=680;
road(33,34)=480;road(34,35)=300;road(35,36)=220;road(36,37)=210;road(37,38)=420;road(38,39)=500;
[road_D,road_R]=floyd_my(road);%Floyd—Warshall算法,返回D任意两节点间最短路径,R储存最短路径的路线
road_C=0.1*road_D; %公路的运费矩阵
%% 计算总最小运费矩阵
for i=1:n %先将铁路子网络和公路子网络组合成一个网络,取其中运费较小的为权
for j=1:n
if rail_C(i,j)<=road_C(i,j)
C(i,j)=rail_C(i,j);
else
C(i,j)=road_C(i,j);
end
end
end
[Cmin,Rmin]=floyd_my(C);
C_need=Cmin(1:7,25:39);
R_need=Rmin(1:7,25:39);
xlswrite('数模7.8最小运费矩阵.xlsx',C_need,1,'A1')
再用lingo 解出运输和订购钢管的全部费用最小时,钢管的运输、订购方案。
model:
sets:
station/1..15/:y,z,l; !y向左铺的钢管数量,z向右铺的钢管数,l需要铺设的钢管数量;
steel/1..7/:s,p,f; !s该钢厂钢管最大生产量,p钢管生产单位成本;
link(steel,station):c,x; !c最小运费矩阵,x从钢厂i订购并运输到节点j的钢管数量;
endsets
data:
c=@ole('C:\Users\烟雨潇潇\Desktop\新建文件夹\CSDN\数模7.8最小运费矩阵.xlsx',cmin);!读取最小运费矩阵;
l=104 301 750 606 194 205 201 680 480 300 220 210 420 500 0;
s=800 800 1000 2000 2000 2000 3000;!各钢厂钢管最大生产量;
p=160 155 155 160 155 150 160;!钢管生产单位成本;
enddata
min=@sum(link(i,j):(p(i)+c(i,j))*x(i,j))+0.05*@sum(station(j):y(j)*(y(j)+1)+z(j)*(z(j)+1));!总购运成本;
@for(steel(i):@sum(station(j):x(i,j))>500*f(i));
@for(steel(i):@sum(station(j):x(i,j))<s(i)*f(i));!每个钢厂钢管产量约束;
@for(steel(i):@bin(f(i)));!f=0该钢厂不生产,f=1生产;
@for(station(j):@sum(steel(i):x(i,j))=y(j)+z(j));!订的量等于铺的量;
@for(station(j)|j#ne#15:z(j)+y(j+1)=l(j)); !j不等于15;
@for(station(j):@gin(y(j)));
y(1)=0;z(15)=0;
end
结果出来还是挺快的。
补充: 用这段代码可以在excel中储存从各钢厂到各铺设节点的最短路径
for i=1:7
for j=1:15
start=i;%铁路起点
dest_rail=R_need(i,j);%铁路终点
%输出铁路路径
line=[]; %储存最短路径
while 1 %表达式为真,一直循环直到break
if(rail_R(start,dest_rail)~=dest_rail)
line=[line,start];
start = rail_R(start,dest_rail);
if start==0 %如果start=0,说明钢厂直接通过公路连接到铺设点,
start=dest_rail; %需要置为dest_rail,不然会出错
break
end
else
line=[line,start];
break
end
end
%输出公路路径
start=dest_rail;%公路起点
dest_road=j+24;%公路终点
while 1 %表达式为真,一直循环直到break
if (road_R(start,dest_road)~=dest_road)
line=[line,start];
start = road_R(start,dest_road);
else
line=[line,start,dest_road];
k=15*(i-1)+j %该路径储存在excel的k行
name=cell(1,1);
name{1,1}=[int2str(i),',',int2str(dest_road)]; %表示从节点i到节点j的最短路径,cell{}访问cell储存的内容
A_name=['A',num2str(k)];
xlswrite('7.8最短路径.xlsx',name(1,1),1,A_name)
cellnames=['B',num2str(k)];
xlswrite('7.8最短路径.xlsx',line,1,cellnames)
break;
end
end
end
end
效果如图: