目录
遗传算法解决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
在实现遗传算法的时候,这个代码并没有用到两个个体的交叉互换,而是用了一个个体通过变异产生新个体。下面是变异函数的几点解释:
希望能够对读者的理解略尽绵薄之力
统计图: