MTSP問題遺傳算法解決代碼及其案例

目錄

 

遺傳算法解決MTSP問題

個體的基因型:

基本算法:

代碼及其解釋

產生問題:

遺傳算法代碼:

運行結果: 


遺傳算法解決MTSP問題

問題類型:解決所有旅行商從同一地點出發,同時回到同一地點(不是出發點)的問題。

個體的基因型:

設置基因型爲兩段:

1、路徑基因型

2、中斷點基因型

這些意味着什麼呢?假設有3個旅行商,10個城市,城市代號1爲起始點,代號10位出發點,那麼假設遺傳算法中產生了這麼一個個體,他的基因型爲:

路徑基因型:[2 3 5 6 7 9 8]

中斷點基因型:[2 4]

那麼,這個個體代表的信息爲:

旅行商1的旅遊路徑:1 2 3 10

旅行商2的旅遊路徑:1 5 6 10

旅行商3的旅遊路徑:1 7 9 8 10

基本算法:

1、設置5000個迭代次數,每一次迭代產生一個最佳個體,若這廝的路徑距離小於歷史的全局最小值,就作爲全局最小值。

2、從本次迭代中的個體,隨機分成n組,從每一組中的最佳個體裏修改基因片段(有的改路徑基因型,有的改中斷點基因型),從而得到子代。

3、子代再一次產生最小路徑值,若再次小於歷史的最小值,則設置他爲全局最小值。再次以2的方法產生子代

4、直到5000次迭代結束爲止。

 

代碼及其解釋

產生問題:


n = 35;    %設置城市個數
xy = 10*rand(n,2);   %隨機產生城市的座標,實際應用中可以自己輸入座標。主要用以畫圖,真正起作用的是距離矩陣啦。
salesmen = 5;   %設置旅行商的人數
min_tour = 3;    %設置每個旅行商至少走過三個城市(除去起始點和終止點的話就是一個城市)
pop_size = 80;    %設置種羣的個數,必須是8的倍數,因爲代碼中以 8 做爲步驟 2 的分組個數
num_iter = 5e3;   %設置迭代總次數, i.e. 5000次
a = meshgrid(1:n);   %用以計算距離矩陣。
dmat = reshape(sqrt(sum((xy(a,:)-xy(a',:)).^2,2)),n,n);   %計算距離矩陣(歐式距離),可以自己輸入。
[opt_rte,opt_brk,min_dist] = mtspof_ga(xy,dmat,salesmen,min_tour, ...
    pop_size,num_iter,1,1);  %運行代碼

遺傳算法代碼:

根據以上的基本步驟,寫出代碼如下(包括:檢查輸入是否合理、畫圖與統計。) 




% Summary:
%     1. Each salesman starts at the first point, and ends at the last
%        point, but travels to a unique set of cities in between (none of
%        them close their loops by returning to their starting points)
%     2. Except for the first and last, each city is visited by exactly one salesman
%
% Note: The Fixed Start is taken to be the first XY point and the Fixed End
%     is taken to be the last XY point
% Input:
%     XY %城市座標 N by 2 矩陣(用於畫圖)
%     DMAT %距離矩陣 N by N
%     NSALESMEN 有多少個nSalesmen
%     MINTOUR 每一個nSalesmen必須 travel 大於等 MINTOUR 個城市
%     POPSIZE 每一次迭代的種羣個數,必須爲8的倍數,因爲新生代的產生是由 8 個
%             老傢伙產生 8 個新傢伙
%     NUMITER 迭代次數,這個代碼是將這些次數都迭代完的。
%     SHOWPROG 畫圖,如果等於1,就將每一次迭代路徑畫出來
%     SHOWRESULT 畫圖,如果等於1,將最後的結果,城市座標,路徑和歷史總長度
%
% Output:
%     OPTROUTE (integer array) 輸出最佳路徑
%     OPTBREAK (integer array) 輸出中斷點
%     MINDIST (scalar float) 總距離
%
%

% Author: Joseph Kirk
% Email: [email protected]
% Release: 1.3
% Release Date: 6/2/09
% comment by: zhuo

function varargout = mtspof_ga(xy,dmat,nSalesmen,minTour,popSize,numIter,showProg,showResult)
nargs = 8;
% 下面是默認值處理,也就是說,如果函數中缺乏輸入參數,那麼下面的代碼就自作主張幫你添了。
for k = nargin:nargs-1
    switch k
        case 0
            xy = 10*rand(40,2);
        case 1
            N = size(xy,1);
            a = meshgrid(1:N);
            dmat = reshape(sqrt(sum((xy(a,:)-xy(a',:)).^2,2)),N,N);
        case 2
            nSalesmen = 5;
        case 3
            minTour = 1;
        case 4
            popSize = 80;
        case 5
            numIter = 5e3;
        case 6
            showProg = 1;
        case 7
            showResult = 1;
        otherwise
    end
end

% Verify Inputs  驗證輸入是否可行,驗證原理爲城市個數 N 是否和 距離矩陣的 size相等
[N,dims] = size(xy);
[nr,nc] = size(dmat);
if N ~= nr || N ~= nc
    error('Invalid XY or DMAT inputs!')
end
n = N-2;  %出去了起始點和結束點

% Sanity Checks    驗證輸入:可以不看
nSalesmen = max(1,min(n,round(real(nSalesmen(1)))));  
%驗證輸入的旅行商個數是不是大於1,並且是整數,否則幫你四捨五入改了
minTour = max(1,min(floor(n/nSalesmen),round(real(minTour(1)))));
%驗證輸入的minTour是不是大於1,並且是整數,否則幫你四捨五入改了
popSize = max(8,8*ceil(popSize(1)/8));
%驗證輸入的個體數是否爲8的整數(因爲後面的分組8個爲一組),否則幫你用ceil函數改了
numIter = max(1,round(real(numIter(1))));
%驗證輸入的迭代次數是否大於1,否則幫改了
showProg = logical(showProg(1));
%驗證是否爲1或0,下同
showResult = logical(showResult(1));


% Initializations for Route Break Point Selection
nBreaks = nSalesmen-1;    %設置中斷點個數。
dof = n - minTour*nSalesmen;          % degrees of freedom
addto = ones(1,dof+1);
for k = 2:nBreaks
    addto = cumsum(addto);
end
cumProb = cumsum(addto)/sum(addto);


% Initialize the Populations
popRoute = zeros(popSize,n);          % population of routes,popRoute 爲所有個體的路徑基因型
popBreak = zeros(popSize,nBreaks);   % population of breaks
for k = 1:popSize
    popRoute(k,:) = randperm(n)+1;   %隨機產生所有個體的路徑基因型,下同。
    %爲什麼+1?別忘了上面有一句 n = N-2,所以應該是 2~34的隨機序列纔對。
    popBreak(k,:) = rand_breaks();   %rand_breaks()爲產生中斷點的代碼,在下面呢。
end


%畫圖時,將每一個旅行商們走的路用不用顏色標出來。
pclr = ~get(0,'DefaultAxesColor');
clr = [1 0 0; 0 0 1; 0.67 0 1; 0 1 0; 1 0.5 0];
if nSalesmen > 5
    clr = hsv(nSalesmen);
end

% Run the GA
globalMin = Inf; %初始化全局最小值。設爲無窮大,等着被換的傢伙。
totalDist = zeros(1,popSize);  %初始化總距離,是一個行向量,每一個個體對一應一個總距離
distHistory = zeros(1,numIter);   %歷史距離,用於比較最好的距離,每一次迭代,都產生一
%最好距離作爲歷史距離存起來。
tmpPopRoute = zeros(8,n);
%暫時變量,用完就丟。用於產生新個體的,(路徑的基因型)
tmpPopBreak = zeros(8,nBreaks);
%同上,用於產生新的中斷點的基因型
newPopRoute = zeros(popSize,n);
%新生代的路徑基因型  
newPopBreak = zeros(popSize,nBreaks);
%新生代的斷點基因型
if showProg
    pfig = figure('Name','MTSPOF_GA | Current Best Solution','Numbertitle','off');
end
%畫圖:初始點
for iter = 1:numIter
    % Evaluate Members of the Population
    for p = 1:popSize
        d = 0;
        pRoute = popRoute(p,:);  
        %將相應的個體的路徑基因型取出
        pBreak = popBreak(p,:);
        %將相應的個體的中斷點基因型取出
        rng = [[1 pBreak+1];[pBreak n]]';
        %計算每個旅行商的距離之用
        %下面的迭代用於計算每個個體的對應的所有旅行商的總距離
        for s = 1:nSalesmen
            d = d + dmat(1,pRoute(rng(s,1))); % 加上從出發點到下一個城市的距離
            
            for k = rng(s,1):rng(s,2)-1   %加上路徑中的距離
                d = d + dmat(pRoute(k),pRoute(k+1));
            end
            
            d = d + dmat(pRoute(rng(s,2)),N); % 加上從城市回到終點的距離
        end
        totalDist(p) = d;
    end

    % Find the Best Route in the Population
    [min_dist,index] = min(totalDist);
    distHistory(iter) = min_dist;
     
    if min_dist < globalMin
    %若本次迭代時的最佳距離小於歷史全局最小值。
    %就把他畫在圖上,並記錄一共畫了幾次。
        globalMin = min_dist;
        opt_rte = popRoute(index,:);
        opt_brk = popBreak(index,:);
        rng = [[1 opt_brk+1];[opt_brk n]]';
        if showProg
            % Plot the Best Route
            figure(pfig);
            for s = 1:nSalesmen
                rte = [1 opt_rte(rng(s,1):rng(s,2)) N];
                %下面用於三維畫圖,如果輸入的座標時三維的,那麼就啓動如下代碼用以三維繪圖
                if dims == 3, plot3(xy(rte,1),xy(rte,2),xy(rte,3),'.-','Color',clr(s,:));
                else plot(xy(rte,1),xy(rte,2),'.-','Color',clr(s,:)); end
                title(sprintf('Total Distance = %1.4f, Iteration = %d',min_dist,iter));
                hold on
            end
            if dims == 3, plot3(xy(1,1),xy(1,2),xy(1,3),'ko',xy(N,1),xy(N,2),xy(N,3),'ko');
            else plot(xy(1,1),xy(1,2),'ko',xy(N,1),xy(N,2),'ko'); end
            hold off
        end
    end

% 子代個體的產生過程
% 產生一個隨機序列,用於挑選隨機的8個父代產生子代
% 8個傢伙來交配產生子代,(其實也不算交配啦!)
randomOrder = randperm(popSize);
    for p = 8:8:popSize
        rtes = popRoute(randomOrder(p-7:p),:);
        brks = popBreak(randomOrder(p-7:p),:);
        %隨機挑選的8個父代
        dists = totalDist(randomOrder(p-7:p));
        [ignore,idx] = min(dists); 
        %從這8個父代中挑選出最佳父代,用於產生8個子代。
        bestOf8Route = rtes(idx,:);
        bestOf8Break = brks(idx,:);
        routeInsertionPoints = sort(ceil(n*rand(1,2)));
        %從中挑選出基因序列的2個位置
        %這兩個位置用來從父代中產生新的基因新的
        I = routeInsertionPoints(1);
        J = routeInsertionPoints(2);
        for k = 1:8 % Generate New Solutions
            tmpPopRoute(k,:) = bestOf8Route;
            tmpPopBreak(k,:) = bestOf8Break;
            switch k
                case 2 % Flip
                    %將最佳父代的基因型從上面兩個位置中間的片段反轉,產生一個子代。
                    tmpPopRoute(k,I:J) = tmpPopRoute(k,J:-1:I);
                case 3 % Swap
                    %交換這兩個片段的基因,產生新子代。
                    tmpPopRoute(k,[I J]) = tmpPopRoute(k,[J I]);
                case 4 % Slide
                    % 自己看吧,描述不出
                    tmpPopRoute(k,I:J) = tmpPopRoute(k,[I+1:J I]);
                    %上面都是調整路徑基因型的
                    %下面用於調整中斷點基因型,過程差不多,大家可以自己看的
                case 5 % Modify Breaks
                    %隨機產生,跟最佳父代沒關係的一代。
                    tmpPopBreak(k,:) = rand_breaks();
                case 6 % Flip, Modify Breaks
                    tmpPopRoute(k,I:J) = tmpPopRoute(k,J:-1:I);
                    tmpPopBreak(k,:) = rand_breaks();
                case 7 % Swap, Modify Breaks
                    tmpPopRoute(k,[I J]) = tmpPopRoute(k,[J I]);
                    tmpPopBreak(k,:) = rand_breaks();
                case 8 % Slide, Modify Breaks
                    tmpPopRoute(k,I:J) = tmpPopRoute(k,[I+1:J I]);
                    tmpPopBreak(k,:) = rand_breaks();
                otherwise % Do Nothing
            end
        end
        newPopRoute(p-7:p,:) = tmpPopRoute;
        newPopBreak(p-7:p,:) = tmpPopBreak;
    end
    popRoute = newPopRoute;
    popBreak = newPopBreak;
end


if showResult
    %用於畫出統計結果圖,(博客中的第二張圖)
    % Plots
    figure('Name','MTSPOF_GA | Results','Numbertitle','off');
    subplot(2,2,1);
    if dims == 3, plot3(xy(:,1),xy(:,2),xy(:,3),'k.');
    else plot(xy(:,1),xy(:,2),'k.'); end
    title('City Locations');
    subplot(2,2,2);
    imagesc(dmat([1 opt_rte N],[1 opt_rte N]));
    title('Distance Matrix');
    subplot(2,2,3);
    rng = [[1 opt_brk+1];[opt_brk n]]';
    for s = 1:nSalesmen
        rte = [1 opt_rte(rng(s,1):rng(s,2)) N];
        if dims == 3, plot3(xy(rte,1),xy(rte,2),xy(rte,3),'.-','Color',clr(s,:));
        else plot(xy(rte,1),xy(rte,2),'.-','Color',clr(s,:)); end
        title(sprintf('Total Distance = %1.4f',min_dist));
        hold on;
    end
    if dims == 3, plot3(xy(1,1),xy(1,2),xy(1,3),'ko',xy(N,1),xy(N,2),xy(N,3),'ko');
    else plot(xy(1,1),xy(1,2),'ko',xy(N,1),xy(N,2),'ko'); end
    subplot(2,2,4);
    plot(distHistory,'b','LineWidth',2);
    title('Best Solution History');
    set(gca,'XLim',[0 numIter+1],'YLim',[0 1.1*max([1 distHistory])]);
end

% 返回結果
if nargout
    varargout{1} = opt_rte;  %參數1 最佳個體的路徑基因型
    varargout{2} = opt_brk;   %參數2 最佳個體的中斷點基因型
    varargout{3} = min_dist;    %參數3 最佳個體的總距離
end

%產生終端點代碼(隨機生成)
%爲什麼要單獨寫呢?因爲前面有要求,每一個旅行商至少走個3個城市。
function breaks = rand_breaks()
    if minTour == 1 % No Constraints on Breaks
        tmpBreaks = randperm(n-1);
        breaks = sort(tmpBreaks(1:nBreaks));
    else % Force Breaks to be at Least the Minimum Tour Length
        num_adjust = find(rand < cumProb,1)-1;
        spaces = ceil(nBreaks*rand(1,num_adjust));
        adjust = zeros(1,nBreaks);
        for kk = 1:nBreaks
            adjust(kk) = sum(spaces == kk);
        end
        breaks = minTour*(1:nBreaks) + cumsum(adjust);
    end
end

end  %結束總function

運行結果: 

統計圖: 

 

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