綜合算法02—指定點之間的K短路

%注意:程序中調用了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

>> 


算例分析:
算例分析:
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章