一、实验目标
1、完成基于Matlab 的遗传算法的编写,并用于测试旅行商问题,给出结果
2、 掌握遗传算法的基本流程
3、 掌握 TSP 问题的数据化处理
4、 掌握适应度函数编写
5、 掌握交叉、变异和选择操作编写
二、实验内容
1、 将例子中城市的个数扩充到 30 个
%城市坐标
map=[18,54;87,76;74,78;71,71;25,38;58,35;4,50;13,40;18,40;24,42;
71,44;64,60;68,58;83,69;58,69;54,62;51,67;37,84;14,94;2,99;
7,64;22,60;25,62;62,32;87,7;91,38;83,46;41,26;45,21;44,35];
2、 将例子中的选择算子改成轮盘赌实现。
% 调用函数进行轮盘赌选择操作
Road = chooseParentsRoulette(Road, fitness);
3、 将例子中的顺序交叉改成下图所示的顺序交叉方法
% 调用顺序交叉函数
children = [];
for i=1:Popsize
if rand<pc %rand(1)小于1的随机数
sgpoint=randi(n,2,1); %获得1-n中的随机数,数量为2
minsg=min(sgpoint);
maxsg=max(sgpoint);
x=randi(Popsize,2,1); %调用randi函数,获得[1:Popsize]中的两个随机数
parent1=Road(x(1),:);%获得两个交叉用父代
parent2=Road(x(2),:);
child = sequentialCrossover(parent1, parent2);
children = [children; child];
end
end
4、 将例子中计算路径长度的语句用调用函数的形式实现
% 调用函数计算路径长度
Dist = calculatePathLengths(children, map, @distance);
5、 结果展示中,画出一条初始路径、一条全局最优路径和迭代曲线
% 绘制初始路径
figure;
subplot(2, 1, 1);
plot(map(:,1), map(:,2), 'ro');
hold on;
plot([map(Road(1,:),1); map(Road(1,1),1)], [map(Road(1,:),2); map(Road(1,1),2)], 'b-');
title('初始路径');
xlabel('X坐标');
ylabel('Y坐标');
% 绘制全局最优路径
subplot(2, 1, 2);
plot(map(:,1), map(:,2), 'ro');
hold on;
plot([map(MinRoad(Iterindex,:),1); map(MinRoad(Iterindex,1),1)], [map(MinRoad(Iterindex,:),2); map(MinRoad(Iterindex,1),2)], '*-');
title('全局最优路径');
xlabel('X坐标');
ylabel('Y坐标');
% 绘制迭代曲线
figure;
plot(1:MaxIter, MinRoad_value, 'b-');
title('迭代曲线');
xlabel('迭代次数');
ylabel('最短路径长度');
三、实验总结
通过运行我们的遗传算法程序,我们得到了30个城市TSP问题的解。在结果展示中,我们也绘制了初始路径、全局最优路径和迭代曲线。通过对比和分析这些结果,我们可以得出以下结论:
- 遗传算法能够有效地解决TSP问题。
通过运行我们的程序,我们得到了一个相对较优的解,这表明遗传算法是一种解决TSP问题的有效方法。 - 增加城市数量对算法性能有一定影响。
在本次实验中,我们将城市数量扩展到30个,增加了一定的计算量和复杂性。但是我们的算法仍然能够得到较好的结果,这说明我们的算法具有一定的扩展性和鲁棒性。
四、代码附录
运行代码
%parameters
%城市坐标
map=[18,54;87,76;74,78;71,71;25,38;58,35;4,50;13,40;18,40;24,42;
71,44;64,60;68,58;83,69;58,69;54,62;51,67;37,84;14,94;2,99;
7,64;22,60;25,62;62,32;87,7;91,38;83,46;41,26;45,21;44,35];
n=max(size(map)); % the number of cities
pc=0.8; %交叉概率,相当于基因遗传的时候染色体交叉
pm=0.1; %染色体变异
MaxIter=200; %允许迭代最大次数
Popsize=150; %群体的大小
% initialize population
for i=1:Popsize
Road(i,:)=randperm(n); %产生自然数1到n的一个随机排列
end
Iter=1;
MinRoad=zeros(MaxIter,n);
MinRoad_value=zeros(MaxIter,1);
fitness=zeros(MaxIter,1);
while(Iter<=MaxIter)
%计算每条路径的长度
% 调用函数计算路径长度
for i =1:Popsize
route(1,:)=[Road(i,:),Road(i,1)];
end
Dist = calculatePathLengths(Road, map, @distance);
[Mindist,q]=min(Dist); %在Dist中,找出最短路径长度
Maxdist=max(Dist);
MinRoad_parents=Road(q,:);%存入MinRoad_parents_value,并得到该最短路径
MinRoad_parents_value=Mindist;%将它存入MinRoad_parents
for i=1:Popsize
fitness(i)=1-(Dist(i)-Mindist)/(Maxdist-Mindist);
%计算每条路径的适应度函数,存入fitness, fitness大小为Popsize*1
%最小值问题转成最大值问题,并进行归一化处理
end
% 调用函数进行轮盘赌选择操作
Road = chooseParentsRoulette(Road, fitness);
% 调用顺序交叉函数
children = [];
for i=1:Popsize
if rand<pc %rand(1)小于1的随机数
sgpoint=randi(n,2,1); %获得1-n中的随机数,数量为2
minsg=min(sgpoint);
maxsg=max(sgpoint);
x=randi(Popsize,2,1); %调用randi函数,获得[1:Popsize]中的两个随机数
parent1=Road(x(1),:);%获得两个交叉用父代
parent2=Road(x(2),:);
child = sequentialCrossover(parent1, parent2);
children = [children; child];
end
end
% 调用函数计算路径长度
Dist = calculatePathLengths(children, map, @distance);
[Mindist,q]=min(Dist);
MinRoad_crossover=Road(q,:);
MinRoad_crossover_value=Mindist;
% mutation变异
for i=1:Popsize
if rand<pm&&i~=q %不对最优的路径变异
mupoint=randi(n,1,2);
temp_point=Road(i,mupoint(1));
Road(i,mupoint(1))=Road(i,mupoint(2));
Road(i,mupoint(2))=temp_point;
end
end
% 调用函数计算路径长度
Dist = calculatePathLengths(Road, map, @distance);
[Mindist,q]=min(Dist);
MinRoad_mutation=Road(q,:);
MinRoad_mutation_value=Mindist;
%每一代中找到的最优路径
MinRoad_opt=[MinRoad_parents;MinRoad_crossover;MinRoad_mutation];
a=[MinRoad_parents_value;MinRoad_crossover_value;MinRoad_mutation_value];
[Minvalue,m]=min(a);
MinRoad(Iter,:)=MinRoad_opt(m,:);
MinRoad_value(Iter,1)=Minvalue;
Iter=Iter+1;
end
%找出全局最优路径及其迭代次数
[MinestRoad_value,Iterindex]=min(MinRoad_value);
disp('最优路径为:');
disp(MinRoad(Iterindex,:));
disp('最优路径长度为:');
disp(MinestRoad_value);
disp('获得最佳路径的迭代次数为:');
disp(Iterindex);
% 绘制初始路径
figure;
subplot(2, 1, 1);
plot(map(:,1), map(:,2), 'ro');
hold on;
plot([map(Road(1,:),1); map(Road(1,1),1)], [map(Road(1,:),2); map(Road(1,1),2)], 'b-');
title('初始路径');
xlabel('X坐标');
ylabel('Y坐标');
% 绘制全局最优路径
subplot(2, 1, 2);
plot(map(:,1), map(:,2), 'ro');
hold on;
plot([map(MinRoad(Iterindex,:),1); map(MinRoad(Iterindex,1),1)], [map(MinRoad(Iterindex,:),2); map(MinRoad(Iterindex,1),2)], '*-');
title('全局最优路径');
xlabel('X坐标');
ylabel('Y坐标');
% 绘制迭代曲线
figure;
plot(1:MaxIter, MinRoad_value, 'b-');
title('迭代曲线');
xlabel('迭代次数');
ylabel('最短路径长度');
calculatePathLengths.m
function Dist = calculatePathLengths(Road, map, distance)
% 计算每条路径的长度
[Popsize, n] = size(Road);
Dist = zeros(Popsize, 1);
for i = 1:Popsize
dist = 0;
for j = 1:(n-1)
A = Road(i, j);
B = Road(i, j+1);
A1 = map(A, :);
B1 = map(B, :);
dist = dist + distance(A1, B1);
end
% 将路径首尾两个城市的距离加入路径长度中
A = Road(i, 1);
B = Road(i, n);
A1 = map(A, :);
B1 = map(B, :);
dist = dist + distance(A1, B1);
Dist(i, 1) = dist;
end
end
chooseParentsRouLette.m
function Road = chooseParentsRoulette(Road, fitness)
% 选择操作:使用轮盘赌选择父代路径
Popsize = size(Road, 1);
totalFitness = sum(fitness);
selectionProb = fitness / totalFitness;
% 计算累积概率
cumProb = cumsum(selectionProb);
% 选择父代路径
newRoad = zeros(size(Road));
for i = 1:Popsize
r = rand;
for j = 1:Popsize
if r <= cumProb(j)
newRoad(i, :) = Road(j, :);
break;
end
end
end
Road = newRoad;
end
distance.m
function d=distance(A,B) %计算两城市之间距离函数
d=sqrt((A(1)-B(1))^2+(A(2)-B(2))^2); %距离公式
end
eliminate.m
function elim=eliminate(x,y)
for n=1:length(y)
x=x(find(x~=y(n))); %去除x中的y
end
elim=x;
end
fitness.m
function f=fitness(fmin,fmax,froad)
f=1-(froad-fmin)/(fmax-fmin); %适应度函数
end
plotResults.m
function plotResults(initialPath, bestPath, iteration, distances)
% 绘制初始路径、全局最优路径和迭代曲线
figure;
% 绘制初始路径
subplot(1, 2, 1);
plotPath(initialPath, '初始路径');
% 绘制全局最优路径
subplot(1, 2, 2);
plotPath(bestPath, '全局最优路径');
% 绘制迭代曲线
figure;
plot(iteration, distances, 'b.-');
xlabel('迭代次数');
ylabel('最短路径长度');
title('迭代曲线');
end
function plotPath(path, titleText)
x = path(:, 1);
y = path(:, 2);
x = [x; x(1)]; % 将路径首尾相连形成闭环
y = [y; y(1)];
plot(x, y, 'r.-');
xlabel('X坐标');
ylabel('Y坐标');
title(titleText);
axis equal;
end
sequentialCrossover.m
function child = sequentialCrossover(parent1, parent2)
% 顺序交叉操作:从第一双亲随机选择一个字串,通过拷贝子串产生原始后代
n = length(parent1);
% 从第一双亲中随机选择一个字串
startIdx = randi([1, n-1]);
endIdx = randi([startIdx+1, n]);
% 拷贝子串生成原始后代
child = zeros(1, n);
child(startIdx:endIdx) = parent1(startIdx:endIdx);
% 删除第二双亲中已有的城市
for i = startIdx:endIdx
parent2(parent2 == child(i)) = 0;
end
% 将剩余的城市按顺序填入后代的空缺位置
child(child == 0) = parent2(parent2 ~= 0);
end