%注意:程序中調用了Dijkstras算法,若需要運行,請自行將其放在同目錄下。
function [W_Section,Line_Section_01Mat,Mxf]=KSP(Road_Net,Line_Station)
%% 問題描述
%已知路網數據和線路節點數據,求指定點之間的K短路。
%% 程序功能
%路段自動編號(上下行作爲2個路段分別對待)
%將路徑-節點轉換爲路徑-路段
%求出一般定義的K短路
%求出各條路徑的換乘點
%求出各條路徑的換乘費用
%求出各條路徑的總費用(不換乘&換乘)
%求出路徑與路段的關係矩陣(作爲Frank_Wolfe算法的已知條件)
%本程序中的換乘僅指一次換乘,本研究假定不會選擇三次以及以上換乘次數
%***********************************************************************
%程序完成時間:2015-12-30
%運行測試環境:MATLAB 8.3.0.532
%製作人:蘭州交通大學 劉志祥
%***********************************************************************
%% 變量說明
% KPaths: K短路路徑集合
% KCosts-: K短路費用集合
% Kmax: 指定的K短路路徑數量(若大於全部路徑數,多餘的部分會自動捨去)
% a: 路段編號
% P: 保存當前已找到的路徑
% X: P的子集,表示一個路徑
% path_number: 路徑編號
% current_P: 當前路徑編號(P_)
% size_X: X的大小
% w_index_in_path: 偏差節點在當前路徑中的位置
% KSP.: 結構體,爲了集中展現結果
% Road_Net: 路網矩陣,帶權的鄰接方陣
% Line_Station: 路網路線集合
% Section_Station: 路段(Section)是由哪兩個節點(Station)組成的
% W_Section: 路段權重
% W_Line_Section: 路線上各路段的權重
% Original: 起點
% Destination: 終點
% path_0: 直達路線(一條直達路線上可能有不只一個的直達路徑)
% Transfer_Station: 可能的換乘點
% Cost_of_Transfer: 換乘一次要增加的費用
% H: 可容忍的繞路倍數
% path_k1: 途經起始點到第一個換乘點的所有點的直達路線集合
% path_k2: 途經所有換乘點的路線集合
% path_k3: 途經最後一個換乘點到終點的所有點路徑集合
% Irrespective_of_the_Transfer_KPaths_number: 不考慮換乘的K短路按升序排列編號
% Irrespective_of_the_Transfer_KPaths: 不考慮換乘的K短路路徑
% Irrespective_of_the_Transfer_KCosts: 不考慮換乘的K短路費用
% Transfer_Station_of_Path: 路徑上的換乘點
% Times_of_Transfer: 換乘次數
% KCosts_of_Transfer: 換乘的額外費用
% Consider_the_Transfer_KCosts: 考慮換乘的費用
% Consider_not_consider_KPaths_number: 考慮換乘後的路徑排序(對應於不考慮換乘的排序)
% Consider_the_Transfer_KPaths: 考慮換乘後升序的K短路費用
%% 模塊1:輸入並處理相關的路網數據
%例
% Road_Net =[
% 0 2 Inf 1 Inf Inf Inf Inf Inf
% 2 0 2 Inf 3 Inf Inf Inf Inf
% Inf 3 0 Inf Inf 2 Inf Inf Inf
% 1 Inf Inf 0 3 Inf 5 Inf Inf
% Inf 3 Inf 3 0 1 Inf 2 Inf
% Inf Inf 2 Inf 1 0 Inf Inf 3
% Inf Inf Inf 5 Inf Inf 0 2 Inf
% Inf Inf Inf Inf 2 Inf 2 0 4
% Inf Inf Inf Inf Inf 3 Inf 4 0];
% Line_Station={[1 2 3 6 9 8 7 4],[2 5 8 9],[1 4 5 6]};
Original=input('Original=');
Destination=input('Destination=');
tic
%% 定義變量及常量
Cost_of_Transfer=2.5; %預定義換乘時間,本文的換乘時間指平均換乘時間,只要發生換乘就會多話費這麼多時間
H=1.5; %預定義的最大繞路倍數
a=0; %路段編號初始化
Kmax=100; %預定義的路徑數量,這裏儘可能取大,因爲繞路倍數能夠自動過濾不需要計算的k(啓發式算法)
%% 根據路網對路段進行自動標號並求出每個路段的距離
for i=1:length(Road_Net)
for j=1:length(Road_Net)
if Road_Net(i,j)~=0&Road_Net(i,j)~=inf
a=a+1;
[Section_Station{a},W_Section{a}]=dijkstra(Road_Net,i,j);
end
end
end
%% 根據路段標號和路線-節點關係,求出路線-路段的0-1矩陣
Line_Section_01Mat=zeros(length(Line_Station),length(Section_Station)); %線路-路徑0-1矩陣,表示某線路是否包含該路段
m=1;
while m<=length(Line_Station)
for i=2:length(Line_Station{m})
for k=1:length(Section_Station)
if isequal(Section_Station{k},Line_Station{m}([i-1 i]))
Line_Section_01Mat(m,k)=1;
end
end
end
m=m+1;
end
%% 路線-節點改爲路線-路段
for i=1:length(Line_Station)
a=0;
for j=2:length(Line_Station{i})
for k=1:length(Section_Station)
if Line_Station{i}([j-1,j])==Section_Station{k}
a=a+1;
Line_Section{i}(a)=k;
W_Line_Section{i}(a)=W_Section(k);
end
end
end
end
%% 模塊2:K短路算法
%% Step_0判斷可行性
if Original > size(Road_Net,1)|| Destination > size(Road_Net,1)
warning('起點或終點不在指定路網中!');
KPaths=[];
KCosts=[];
else
%% Step_1調用Dijkstra算法求出最短路路徑及費用
k=1;
[path costs]=dijkstra(Road_Net, Original, Destination);
if isempty(path)
KPaths=[];
KCosts=[];
else
%% Step_2初始化
path_number = 1;
P{path_number,1}= path;
P{path_number,2}= costs;
current_P = path_number;
size_X=1;
X{size_X}={path_number; path; costs};
S(path_number)= path(1);
KPaths{k}= path;
KCosts{k}= costs;
while (k<Kmax && size_X ~=0)
%% 刪除超出的路徑和值
if KCosts{k}>(H+1)*costs %此處(H+1)表示即便是直達線路,只要超過最短線路的(H+1)倍,也應該放棄,說明該條直達線路設置不合理,該步驟是爲了終止過多無用的K路徑。
k=k-1;
KCosts=KCosts(1:k);
KPaths=KPaths(1:k);
break
end
%% Step_3關閉已搜索的路徑
for i=1:length(X)
if X{i}{1}== current_P
size_X = size_X - 1;
X(i)=[];
break;
end
end
P_= P{current_P,1};
%% Step_4找偏差節點w的位置i
w = S(current_P);
for i=1:length(P_)
if w==P_(i)
w_index_in_path=i;
end
end
%% Step_5更新路網矩陣
for index_dev_vertex= w_index_in_path:length(P_)- 1
temp_luwangjuzhen = Road_Net;
for i = 1: index_dev_vertex-1
v = P_(i);
temp_luwangjuzhen(v,:)=inf;
temp_luwangjuzhen(:,v)=inf;
end
SP_sameSubPath=[];
index =1;
SP_sameSubPath{index}=P_;
for i=1:length(KPaths)
if length(KPaths{i})>= index_dev_vertex
if P_(1:index_dev_vertex)== KPaths{i}(1:index_dev_vertex)
index = index+1;
SP_sameSubPath{index}=KPaths{i};
end
end
end
v_ = P_(index_dev_vertex);
for j = 1: length(SP_sameSubPath)
next=SP_sameSubPath{j}(index_dev_vertex+1);
temp_luwangjuzhen(v_,next)=inf;
end
%% Step_6計算偏差頂點前的子路徑費用
sub_P=P_(1:index_dev_vertex);
costs_sub_P=0;
for i=1:length(sub_P)-1
costs_sub_P=costs_sub_P+Road_Net(sub_P(i),sub_P(i+1));
end
%% Step_7計算偏差頂點到終點的路徑及費用
[dev_p c]= dijkstra(temp_luwangjuzhen, P_(index_dev_vertex), Destination);
if ~isempty(dev_p)
path_number=path_number+1;
P{path_number,1}=[sub_P(1:end-1) dev_p]; %連接起點到終點的路徑
P{path_number,2}= costs_sub_P + c ; %計算子路徑及偏差定點到終點費用的和(最終費用)
S(path_number)= P_(index_dev_vertex);
size_X = size_X + 1;
X{size_X}={path_number; P{path_number,1};P{path_number,2}};
% 更新當前數據(路徑編號,路徑,路徑費用)
end
end
%% Step_8防錯處理,如果指定路徑數目大於路網窮舉數目,防錯,否則最後的結果會發生重複。
if size_X > 0
shortestXCosts= X{1}{3}; %路徑費用
shortestX= X{1}{1}; %判定路徑
for i=2:size_X
if X{i}{3}< shortestXCosts
shortestX= X{i}{1};
shortestXCosts= X{i}{3};
end
end
current_P=shortestX;
k=k+1;
KPaths{k}= P{current_P,1};
KCosts{k}= P{current_P,2};
else
k=k+1;
end
end
end
end
%% 模塊3:換乘算法
%% Step_0找直達線路
if isempty(KPaths)==0
for i=1:length(Line_Station)
pq(i)=ismember(Original,Line_Station{i}); %起點是否是路線i上的節點
pz(i)=ismember(Destination,Line_Station{i}); %終點是否是路線i上的節點
end
S=find(pq==1); %經過起點的線路
T=find(pz==1); %經過終點的線路
path_0=intersect(S,T); %直達線路
if isempty(path_0)==0
disp(['起點和終點間有直達線路:',num2str(path_0)]);
else
disp('無直達線路,請選擇換乘方案');
end
%% Step_1找路網上的換乘節點
n=0;
for i=1:length(Line_Station)
for j=1:length(Line_Station)
if i>j
n=n+1;
Transfer_Station{n}=intersect(Line_Station{i},Line_Station{j});
end
end
end
Transfer_Station=setdiff(unique(cell2mat(Transfer_Station)),[Original,Destination]);
if isempty(Transfer_Station)
disp('提示:無一次換乘點.');
else
disp(['提示:若選擇換乘,可能的換乘站有:',num2str(Transfer_Station)]);
end
%% Step_2確定路徑上換乘節點
for i=1:length(KPaths)
Transfer_Station_of_Path{i}=intersect(intersect(intersect([Line_Station{S}],[Line_Station{T}]),Transfer_Station),KPaths{i});
end
%% Step_3初始化路徑的前段、中段、末段路徑,並標記前中末段位置
path_k1=cell(1,Kmax);
path_k2=cell(1,Kmax);
path_k3=cell(1,Kmax);
index_first_Trasnfer=cell(1,length(KPaths));
index_Last_Trasnfer=cell(1,length(KPaths));
for i=1:length(KPaths)
k1{i}=1;
k2{i}=length(KPaths{i});
end
%標記第一個換乘點
for i=1:length(KPaths)
num_HCD=0;
if isempty(Transfer_Station_of_Path{i})==0
while k1{i}<length(KPaths{i})
k1{i}=k1{i}+1;
for j=1:length(Line_Station)
if ismember(KPaths{i}(k1{i}),Transfer_Station_of_Path{i})&all(ismember(KPaths{i}(1:k1{i}),Line_Station{j}))
index_first_Trasnfer{i}=k1{i};
end
end
end
else
index_first_Trasnfer{i}=k1{i};
end
end
%標記第二個換乘點
k1=index_first_Trasnfer;
for i=1:length(KPaths)
if isempty(Transfer_Station_of_Path{i})==0
while k2{i}>=k1{i}
for j=1:length(Line_Station)
if ismember(KPaths{i}(k2{i}),Transfer_Station_of_Path{i})&all(ismember(KPaths{i}(k2{i}:end),Line_Station{j}))
index_Last_Trasnfer{i}=k2{i};
end
end
k2{i}=k2{i}-1;
end
else
index_Last_Trasnfer{i}=k2{i};
end
end
%% Step_4求每個路徑的前中後段路徑集合
for i=1:length(KPaths)
n1=0;n2=0;n3=0;
for j=1:length(Line_Station)
if all(ismember(KPaths{i}(1:index_first_Trasnfer{i}),Line_Station{j}))
n1=n1+1;
path_k1{i}(n1)=j;
end
if all(ismember(KPaths{i}(index_first_Trasnfer{i}:index_Last_Trasnfer{i}),Line_Station{j}))
n2=n2+1;
path_k2{i}(n2)=j;
end
if all(ismember(KPaths{i}(index_Last_Trasnfer{i}:end),Line_Station{j}))
n3=n3+1;
path_k3{i}(n3)=j;
end
end
end
%% Step_5求換乘次數和換乘費用
for i=1:length(KPaths)
if isempty(Transfer_Station_of_Path{i})
Times_of_Transfer{i}=0;
KCosts_of_Transfer{i}=0;
Transfer_Station_of_Path{i}=[];
else
One_time_Line{i}=union(intersect(path_k1{i},path_k2{i}),intersect(path_k2{i},path_k3{i}));
Direct_Line{i}=intersect(intersect(path_k1{i},path_k2{i}),path_k3{i});
if isempty(Direct_Line{i})==0
Times_of_Transfer{i}=0;
KCosts_of_Transfer{i}=0;
Transfer_Station_of_Path{i}=[];
elseif isempty(One_time_Line{i})==0
Times_of_Transfer{i}=1;
KCosts_of_Transfer{i}=Cost_of_Transfer*1;
Transfer_Station_of_Path{i}=KPaths{i}(index_first_Trasnfer{i});
else
if isempty(path_k2{i})
Times_of_Transfer{i}=3;
KCosts_of_Transfer{i}=inf;
Transfer_Station_of_Path{i}=intersect(KPaths{i}(index_first_Trasnfer{i}:index_Last_Trasnfer{i}),Transfer_Station);
else
Times_of_Transfer{i}=2;
KCosts_of_Transfer{i}=Cost_of_Transfer*2;
Transfer_Station_of_Path{i}=[KPaths{i}(index_first_Trasnfer{i}),KPaths{i}(index_Last_Trasnfer{i})];
end
end
end
end
%% Step_6計算總費用
for i=1:length(KCosts)
Consider_the_Transfer_KCosts{i}=KCosts_of_Transfer{i}+KCosts{i};
end
%% Step_7數據結構體化
KSP.Road_Net={Road_Net};
KSP.Line_Station=Line_Station;
KSP.Section_Station=Section_Station;
KSP.W_Section=W_Section;
KSP.Line_Section=Line_Section;
KSP.Line_Section_01Mat=Line_Section_01Mat;
KSP.W_Line_Section=W_Line_Section;
KSP.Original={Original};
KSP.Destination={Destination};
KSP.Kmax={Kmax};
KSP.Cost_of_Transfer={Cost_of_Transfer};
KSP.H={H};
KSP.Transfer_Station={Transfer_Station};
KSP.Irrespective_of_the_Transfer_KPaths=KPaths;
KSP.Irrespective_of_the_Transfer_KCosts=KCosts;
KSP.Transfer_Station_of_Path=Transfer_Station_of_Path;
KSP.Times_of_Transfer=Times_of_Transfer;
KSP.KCosts_of_Transfer=KCosts_of_Transfer;
KSP.Consider_the_Transfer_KCosts=Consider_the_Transfer_KCosts;
[feiyong,paixu]=sort(cell2mat(KSP.Consider_the_Transfer_KCosts));
Direct_path =find(cell2mat(Times_of_Transfer)==0);
KSP.Consider_not_consider_KPaths_number={paixu((feiyong<=H*min(feiyong)) | (ismember(paixu,Direct_path )==1))};
KSP.Consider_not_consider_KPaths_Costs={feiyong((feiyong<=H*min(feiyong))| (ismember(paixu,Direct_path )==1))};
KSP.Consider_the_Transfer_KPaths=KPaths(paixu((feiyong<=H*min(feiyong)) | (ismember(paixu,Direct_path )==1)));
for i=1:length(KSP.Irrespective_of_the_Transfer_KCosts)
KSP.Consider_the_Transfer_KCosts{i}=KSP.Irrespective_of_the_Transfer_KCosts{i}+KSP.KCosts_of_Transfer{i};
end
Last_KPaths=KSP.Consider_the_Transfer_KPaths;
Last_KCosts=KSP.Consider_the_Transfer_KCosts;
%% Step_8路徑路段矩陣的自動鋪畫
Mxf=zeros(length(Last_KPaths),length(Section_Station));
m=1;
while m<=length(Last_KPaths)
for i=2:length(Last_KPaths{m})
for k=1:length(Section_Station)
if isequal(Section_Station{k},Last_KPaths{m}([i-1 i]))
Mxf(m,k)=1;
end
end
end
m=m+1;
end
KSP.Mxf={Mxf};
%% 模塊4:數據輸出及提示
%% ——》條件輸出
if Kmax>length(KPaths)
fprintf('提示:滿足距離要求的路徑最多隻有%d條,其中直達線路%d條,1次換乘可達的%d條,2次換乘可達的%d條,3次及以上換乘可達的%d條(已捨去)。\n',...
length(Times_of_Transfer),sum(cell2mat(Times_of_Transfer)==0),sum(cell2mat(Times_of_Transfer)==1),...
sum(cell2mat(Times_of_Transfer)==2),sum(cell2mat(Times_of_Transfer)==3));
end
%% ——》保存數據
% tic
% disp('正在保存數據,請耐心等待(Ctrl+C放棄保存)...');
% warning off
% save_to_excel
% disp('保存完成!');
% open('E:\MATLAB\自己的算法\Kduanlu_shuju.xls');
% disp('保存數據耗時:')
% toc
else
KSP=[];
Mxf=[];
end
KSP
disp('計算耗時:')
toc
end
算例分析:
路網如圖(線路數據見程序輸入):
>> Road_Net =[
0 2 Inf 1 Inf Inf Inf Inf Inf
2 0 3 Inf 3 Inf Inf Inf Inf
Inf 3 0 Inf Inf 2 Inf Inf Inf
1 Inf Inf 0 3 Inf 5 Inf Inf
Inf 3 Inf 3 0 1 Inf 2 Inf
Inf Inf 2 Inf 1 0 Inf Inf 3
Inf Inf Inf 5 Inf Inf 0 2 Inf
Inf Inf Inf Inf 2 Inf 2 0 4
Inf Inf Inf Inf Inf 3 Inf 4 0];
Line_Station={[1 2 3 6 9 8 7 4],[2 5 8 9],[1 4 5 6]};
>> [W_Section,Line_Section_01Mat,Mxf]=KSP(Road_Net,Line_Station)
Original=1
Destination=9
起點和終點間有直達線路:1
提示:若選擇換乘,可能的換乘站有:2 4 5 6 8
提示:滿足距離要求的路徑最多隻有10條,其中直達線路2條,1次換乘可達的3條,2次換乘可達的2條,3次及以上換乘可達的3條(已捨去)。
KSP =
Road_Net: {[9x9 double]}
Line_Station: {[1 2 3 6 9 8 7 4] [2 5 8 9] [1 4 5 6]}
Section_Station: {1x24 cell}
W_Section: {[2] [1] [2] [3] [3] [3] [2] [1] [3] [5] [3] [3] [1] [2] [2] [1] [3] [5] [2] [2] [2] [4] [3] [4]}
Line_Section: {[1 4 7 17 24 21 18] [5 14 22] [2 9 13]}
Line_Section_01Mat: [3x24 double]
W_Line_Section: {{1x7 cell} {1x3 cell} {1x3 cell}}
Original: {[1]}
Destination: {[9]}
Kmax: {[100]}
Cost_of_Transfer: {[2.5000]}
H: {[1.5000]}
Transfer_Station: {[2 4 5 6 8]}
Irrespective_of_the_Transfer_KPaths: {1x10 cell}
Irrespective_of_the_Transfer_KCosts: {[8] [9] [10] [10] [11] [12] [14] [14] [15] [19]}
Transfer_Station_of_Path: {[6] [2 5 6] [5] [] [2] [] [6 5] [5 6 8] [5 2] [2 4 5]}
Times_of_Transfer: {[1] [3] [1] [0] [1] [0] [2] [3] [2] [3]}
KCosts_of_Transfer: {[2.5000] [Inf] [2.5000] [0] [2.5000] [0] [5] [Inf] [5] [Inf]}
Consider_the_Transfer_KCosts: {[10.5000] [Inf] [12.5000] [10] [13.5000] [12] [19] [Inf] [20] [Inf]}
Consider_not_consider_KPaths_number: {[4 1 6 3 5]}
Consider_not_consider_KPaths_Costs: {[10 10.5000 12 12.5000 13.5000]}
Consider_the_Transfer_KPaths: {[1 2 3 6 9] [1 4 5 6 9] [1 4 7 8 9] [1 4 5 8 9] [1 2 5 8 9]}
Mxf: {[5x24 double]}
計算耗時:
時間已過 0.153817 秒。
W_Section =
[2] [1] [2] [3] [3] [3] [2] [1] [3] [5] [3] [3] [1] [2] [2] [1] [3] [5] [2] [2] [2] [4] [3] [4]
Line_Section_01Mat =
1 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 1 1 0 0 1 0 0 1
0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0
0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
Mxf =
1 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 1 0 0
0 1 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0
1 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0
>>
算例分析:
算例分析: