用於問題:每個旅行商的起始位置隨意,且每次都要回到對應的起始點
基本算法思路:
每個遺傳個體維護兩個基因片段,一個是路徑片段,一個時中斷點片段
什麼是路徑和中斷點呢?
% 假設有 10 個城市 3 個哥們
% 假設得到路徑 rte = [5 6 9 1 4 2 8 10 3 7], 中斷點 = [3 7]
% 於是可以得到每個哥們的路徑 [5 6 9][1 4 2 8][10 3 7],
% which designates the routes for the 3 salesmen as follows:
% . Salesman 1 travels from city 5 to 6 to 9 and back to 5
% . Salesman 2 travels from city 1 to 4 to 2 to 8 and back to 1
% . Salesman 3 travels from city 10 to 3 to 7 and back to 10
於是,每個旅行商的路徑就可以用這兩段基因來表示了!
基本算法:
1、設置5000個迭代次數,每一次迭代產生一個最佳個體,若這廝的路徑距離小於歷史的全局最小值,就作爲全局最小值。
2、從本次迭代中的個體,隨機分成n組,從每一組中的最佳個體裏修改基因片段(有的改路徑基因型,有的改中斷點基因型),從而得到子代。
3、子代再一次產生最小路徑值,若再次小於歷史的最小值,則設置他爲全局最小值。再次以2的方法產生子代
4、直到5000次迭代結束爲止。
下面是一段代碼,別人寫的,我寫了註釋:
%基因型設置:
%每個遺傳個體維護兩個基因片段,一個是路徑片段,一個時中斷點片段
% Summary:
% 1. 每個salesmen的起點隨意,並返回其所在起點
% 2. 每一個城市只能有同一個傢伙遍歷
%
% Input:
% XY %城市座標 N by 2 矩陣(用於畫圖)
% DMAT %距離矩陣 N by N
% NSALESMEN 有多少個salesmen
% MINTOUR 每一個salesmen必須 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.5
% Release Date: 11/07/11
% Translator: ze bin zhuo
function varargout = mtsp_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 = 3;
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;
% 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的整數,否則幫你用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
popBreak = zeros(popSize,nBreaks); % population of breaks
popRoute(1,:) = (1:n);
popBreak(1,:) = rand_breaks();
for k = 2:popSize
popRoute(k,:) = randperm(n);
popBreak(k,:) = 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
% 開始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','MTSP_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(pRoute(rng(s,2)),pRoute(rng(s,1)));
for k = rng(s,1):rng(s,2)-1
d = d + dmat(pRoute(k),pRoute(k+1));
end
end
%把每個個體對應的所有哥們的總距離放在向量totalDist中
totalDist(p) = d;
end
% 找出總距離最短的那個個體用於產生新個體
[minDist,index] = min(totalDist);
%記錄每一步迭代的最短距離到一個向量中
distHistory(iter) = minDist;
%從歷史距離中找出最小的那個作爲globalMin
if minDist < globalMin
%若本次迭代時的最佳距離小於歷史globalMin
%就把他畫在圖上,並記錄一共畫了幾次。
globalMin = minDist;
optRoute = popRoute(index,:);
optBreak = popBreak(index,:);
rng = [[1 optBreak+1];[optBreak n]]';
%畫圖代碼
if showProg
figure(pfig);
for s = 1:nSalesmen
rte = optRoute([rng(s,1):rng(s,2) rng(s,1)]);
if dims > 2, 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',minDist,iter));
hold on
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','MTSP_GA | Results','Numbertitle','off');
subplot(2,2,1);
if dims > 2, plot3(xy(:,1),xy(:,2),xy(:,3),'.','Color',pclr);
else plot(xy(:,1),xy(:,2),'.','Color',pclr); end
title('City Locations');
subplot(2,2,2);
imagesc(dmat(optRoute,optRoute));
title('Distance Matrix');
subplot(2,2,3);
rng = [[1 optBreak+1];[optBreak n]]';
for s = 1:nSalesmen
rte = optRoute([rng(s,1):rng(s,2) rng(s,1)]);
if dims > 2, 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',minDist));
hold on;
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
% Return Outputs
if nargout
varargout{1} = optRoute;
varargout{2} = optBreak;
varargout{3} = minDist;
end
% 改函數用於隨機的產生中斷點。
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
nAdjust = find(rand < cumProb,1)-1;
spaces = ceil(nBreaks*rand(1,nAdjust));
adjust = zeros(1,nBreaks);
for kk = 1:nBreaks
adjust(kk) = sum(spaces == kk);
end
breaks = minTour*(1:nBreaks) + cumsum(adjust);
end
end
end
demo在這裏呢
n = 35;
xy = 10*rand(n,2);
nSalesmen = 5;
minTour = 3;
popSize = 80;
numIter = 5e3;
a = meshgrid(1:n);
dmat = reshape(sqrt(sum((xy(a,:)-xy(a',:)).^2,2)),n,n);
[optRoute,optBreak,minDist] = mtsp_ga(xy,dmat,nSalesmen,minTour,popSize,numIter,1,1);
代碼中的產生新個體的算法解釋
在實現遺傳算法的時候,這個代碼並沒有用到兩個個體的交叉互換,而是用了一個個體通過變異產生新個體。下面是變異函數的幾點解釋:
希望能夠對讀者的理解略盡綿薄之力
運行結果這裏呢