注:旅行商问题的已知最优解是通过遍历得到的,不可能通过智能算法得到更短的距离,除非计算公式出错或者数据出现问题。
软件:matlab
日志:csdn博客
资料来源:百度搜索的结果页面链接
《TSP的已知最优解》:百度文库网址 该文档的上传日期为2014年,该文档最后一行显示的时间点为2007年。
oschina中的数据集下载网址:MP-TESTDATA - The TSPLIB Symmetric Traveling Salesman Problem Instances
数据集和代码已经上传到csdn中,欢迎学习和交流
遇到的第一个问题是距离的计算问题,具体可以通过下载这个文件去查看具体数据集的距离计算公式:http://comopt.ifi.uni-heidelberg.de/software/TSPLIB95/tsp95.pdf
文件第九页有每种问题的对应距离计算方法,再通过该方法找到具体的措施即可
举个例子:
a280数据集(图片中的第一个),可以找到Type为EUC_2D,在pdf文件中搜索该类型,即可找到对应的计算公式如下:
轮盘赌:
锦标赛
看不懂没关系,后面《最终代码》有更详细的代码,或者下载页面末尾的代码连接,下载到自己的电脑上,运行一下,看一下代码逻辑就懂了
首先是一个初始化的参数设置
clear;
clc; %清空其他程序残留的变量
tStart = tic; % 算法计时器
%%%%%%%%%%%%自定义参数%%%%%%%%%%%%%
[cityNum,cities] = Read('att48.tsp');%读取tsp文件中的数据
cities = cities';%读取数据后进行反转
%cityNum = 100;
maxGEN = 1000;%设置迭代的次数
popSize = 1000; % 遗传算法种群大小
crossoverProbabilty = 0.9; %交叉概率
mutationProbabilty = 0.3; %变异概率
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%存储最好的生成结果为最大值
gbest = Inf;
% 计算上述生成的城市距离
distances = calculateDistance(cities);
距离计算公式 --> 不同type不同
function [ distances ] = calculateDistance( city )
%计算距离
[~, col] = size(city);
distances = zeros(col);
for i=1:col
for j=1:col
distances(i,j)= sqrt( ((city(1,i)-city(1,j))^2 + (city(2,i)-city(2,j))^2) / 10);
end
end
end
Read方法–> 读取tsp文件
function [n_citys,city_position] = Read(filename)
fid = fopen(filename,'rt');
location=[];
A = [1 2];
tline = fgetl(fid);
while ischar(tline)
if(strcmp(tline,'NODE_COORD_SECTION'))
while ~isempty(A)
A=fscanf(fid,'%f',[3,1]);
if isempty(A)
break;
end
location=[location;A(2:3)'];
end
end
tline = fgetl(fid);
if strcmp(tline,'EOF')
break;
end
end
[m,n]=size(location);
n_citys = m;
city_position=location;
fclose(fid);
end
randperm(int[]) 将一列序号随机打乱,序号必须是整数。
find(square,1, ‘first’); 找出square中第一个非零元素
find(条件, -, -) 返回满足某条件的值
find(sumDistance==parent1,1, ‘first’);
tourPopDistances=zeros( tournamentSize,1);
%选择了4个父代元素的距离
for i=1:tournamentSize
randomRow = randi(popSize);
tourPopDistances(i,1) = sumDistance(randomRow,1);
end
% 选择最好的,即距离最小的
parent1 = min(tourPopDistances);
% 选择第一个和距离最小的值匹配的个体
% 每个非零元素的行和列下标
[parent1X,parent1Y] = find(sumDistance==parent1,1, 'first');
parent1Path = pop(parent1X(1,1),:);
随机选择四个,选择里面最好的适应度的元素
然后找到对应的路径进行取出
锦标赛算法
保留上一代最好的值,然后使用锦标赛算法
for gen=1:maxGEN
% 计算适应度的值,即路径总距离
% fval(n*1):适应度的值 sumDistance(n*1):总长度
% minPath(n*1):最小路径 maxPath(n*1):最大路径
[fval, sumDistance, minPath, maxPath] = fitness(distances, pop);
% 保留上一代的最好结果
[parent1X,parent1Y] = find(sumDistance==minPath,1, 'first');
offspring(1,:) = pop(parent1X(1,1),:);
minPathes(gen,1) = minPath;
fprintf('代数:%d 最短路径:%.2fKM \n', gen,minPath);
% 锦标赛选择
tournamentSize=20; %设置大小
for k=2:popSize
% 选择父代进行交叉 4*1
tourPopDistances=zeros( tournamentSize,1);
%选择了4个父代元素的距离
for i=1:tournamentSize
randomRow = randi(popSize);
tourPopDistances(i,1) = sumDistance(randomRow,1);
end
% 选择最好的,即距离最小的
parent1 = min(tourPopDistances);
% 选择第一个和距离最小的值匹配的个体
% 每个非零元素的行和列下标
[parent1X,parent1Y] = find(sumDistance==parent1,1, 'first');
parent1Path = pop(parent1X(1,1),:);
for i=1:tournamentSize
randomRow = randi(popSize);
tourPopDistances(i,1) = sumDistance(randomRow,1);
end
parent2 = min(tourPopDistances);
[parent2X,parent2Y] = find(sumDistance==parent2,1, 'first');
parent2Path = pop(parent2X(1,1),:);
subPath = crossover(parent1Path, parent2Path, crossoverProbabilty);%交叉
subPath = mutate(subPath, mutationProbabilty);%变异
offspring(k,:) = subPath(1,:);
end
% 更新
pop = offspring;
% 画出当前状态下的最短路径
if minPath < gbest
gbest = minPath;
paint(cities, pop, gbest, sumDistance,gen);
end
end
function [ fitnessvar, sumDistances,minPath, maxPath ] = fitness( distances, pop )
% 计算整个种群的适应度值
[popSize, col] = size(pop);
sumDistances = zeros(popSize,1);
fitnessvar = zeros(popSize,1);
for i=1:popSize
for j=1:col-1
sumDistances(i) = sumDistances(i) + distances(pop(i,j),pop(i,j+1));
end
end
minPath = min(sumDistances);
maxPath = max(sumDistances);
for i=1:length(sumDistances)
fitnessvar(i,1)=(maxPath - sumDistances(i,1)+0.000001) / (maxPath-minPath+0.00000001);
end
end
看不懂没关系,后面有改进方案,学后面的
在开头计算好所有城市的距离矩阵(n*n),在之后的适应度计算中,直接读取矩阵中的数据即可,大大减轻计算压力
直接选择父代最好的一半元素作为下一代的父代,并从中随机选择两个进行交叉变异获得新个体
保留每一代最好的个体
初始化种群的时候不要随意初始化,使用贪婪算法进行初始化–> 对大数据集优化明显
变异的时候变异多个基因
重组的时候重组多个部分
设置超过200次,变异概率提高
设置超过1000次,跳出循环
迭代收敛图
路线图
clear;
clc;
tStart = tic; % 算法计时器
%%%%%%%%%%%%自定义参数%%%%%%%%%%%%%
[ctype,cityNum,cities] = Read('att48.tsp');
cities = cities';
%cityNum = 100;
maxGEN = 50000;
popSize = 1000; % 遗传算法种群大小
inputCrossoverProbabilty = 0.75;%0.75最优
crossoverProbabilty = inputCrossoverProbabilty; %交叉概率
inputProbabilty = 0.2;%0.1最好
maxProbabilty = 0.9;
mutationProbabilty = inputProbabilty; %变异概率
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%存储最好的生成结果为最大值
gbest = Inf;
% 计算上述生成的城市距离
distances = calculateDistance(cities, ctype);
% 生成种群,每个个体代表一个路径 popSize*cityNum的矩阵
pop = zeros(popSize, cityNum);
%for i=1:popSize
% pop(i,:) = randperm(cityNum);
%end
pop = initParent(distances, cityNum,popSize);
offspring = zeros(popSize,cityNum);
%保存每代的最小路径便于画图
minPathes = zeros(maxGEN,1);
% GA算法
% 计算有多少次没更新最好权值了
count = 0;
lastgbest = 0;
countGen = 1;
flag = 0;
for gen=1:maxGEN
% sumDistance(n*1):总长度
[sumDistance] = newFitness(distances, pop);
[sortDistance, sortIndex] = sort(sumDistance);
% 保留上一代的最好结果
offspring(1,:) = pop(sortIndex(1),:);
minPath = sortDistance(1);
minPathes(gen,1) = minPath;
fprintf('代数:%d 最短路径:%.2fKM \n', gen,minPath);
for k=2:popSize
% 选择父代进行交叉 4*1
parent1Path = selectParent(sortIndex, pop, popSize);
parent2Path = selectParent(sortIndex, pop, popSize);
subPath = crossover(parent1Path, parent2Path, crossoverProbabilty);%交叉
subPath1 = mutate(subPath, mutationProbabilty);%变异
offspring(k,:) = subPath1(:);
end
% 更新
pop = offspring;
% 画出当前状态下的最短路径
if minPath < gbest
gbest = minPath;
countGen = gen;
flag = 1;
end
% 超过一定次数没有更新,就跳出
if gen - countGen > 1000
break;
end
%超过500次没更新就画图
if gen - countGen > 500 && flag == 1
paint(cities, pop, gbest, sumDistance, gen);
flag = 0;
end
%超过200次没更新就调整变异概率
if gen - countGen > 200
mutationProbabilty = maxProbabilty;
end
% if mutationProbabilty < 1
% mutationProbabilty = mutationProbabilty + 0.05;
% end
% if crossoverProbabilty < 0.8
% crossoverProbabilty = crossoverProbabilty + 0.05;
% end
% else
% %更新了就复原
% mutationProbabilty = inputProbabilty;
% crossoverProbabilty = inputCrossoverProbabilty;
% end
end
figure
plot(minPathes, 'MarkerFaceColor', 'red','LineWidth',1);
title('收敛曲线图(每一代的最短路径)');
set(gca,'ytick',500:100:5000);
ylabel('路径长度');
xlabel('迭代次数');
grid on
tEnd = toc(tStart);
fprintf('时间:%d 分 %f 秒.\n', floor(tEnd/60), rem(tEnd,60));
function pop = initParent(distances, cityNum,popSize)
pop = zeros(popSize, cityNum);
[row, col] = size(distances);
% 1/4*popSize 和 cityNum之间选最小的作为最优初始化的个数
% 为了保证选择前面50%的时候多样性还在
if(popSize / 4 > cityNum)
minLength = cityNum;
else
minLength = popSize / 4;
end
% 随机生成一个随机数数组作为开头
randomList = randperm(cityNum);
for k = 1:minLength
cityBest = zeros(1, cityNum);
flagList = zeros(1, cityNum);
%轮到第几行被遍历了
rowIndex = k;
%第k行的数字已经被使用了,不可以再使用
flagList(randomList(k)) = 1;
%第一个数字是randomList(k)
cityBest(1) = randomList(k);
%一共循环cityNum - 1
for i = 2:row
thisRow = distances(rowIndex, :);
best = Inf;
bestIndex = 1;
for j = 1:col
if thisRow(j) ~= 0 && thisRow(j) < best && flagList(j) == 0
best = thisRow(j);
bestIndex = j;
end
end
flagList(bestIndex) = 1;
rowIndex = bestIndex;
cityBest(i) = bestIndex;
end
pop(k, :) = cityBest;
end
%如果更大,证明还有一些种群要随机生成
for i=minLength + 1:popSize
pop(i,:) = randperm(cityNum);
end
end
function [returnPath] = crossover(parent1Path, parent2Path, prob)
% 交叉
random = rand();
if prob >= random
[l, length] = size(parent1Path);
% 交叉后会生成两个子代
childPath = zeros(1,length);
flagPath = zeros(1, length);
% 交换的幅度是不超过一半
startIndex = randi(length);
%middleIndex = randi(length);
%middleIndex1 = randi(length);
endIndex = randi(length);
%list = [startIndex, endIndex, middleIndex, middleIndex1];
%list = sort(list);
%two = sort([startIndex, endIndex]);
count = 1;
for i = startIndex:endIndex
childPath(count) = parent1Path(i);
%flag上面为1,证明被使用了
flagPath(parent1Path(i)) = 1;
count = count + 1;
end
%for i = list(3):list(4)
% childPath(count) = parent1Path(i);
% %flag上面为1,证明被使用了
% flagPath(parent1Path(i)) = 1;
% count = count + 1;
%end
%赋值第二个父代的不同部分
for i = 1:length
if(flagPath(parent2Path(i)) == 1)
continue;
else
childPath(count) = parent2Path(i);
count = count + 1;
end
end
returnPath = childPath;
else
returnPath = parent1Path;
end
end
function [ mutatedPath ] = mutate( path, prob)
%对指定的路径利用指定的概率进行更新
random = rand();
length1 = length(path);
%thisDistance = getDistance(path, distances);
% 概率->允许变异
if random <= prob
%for i = 1:10
index1 = randi(length1);
index2 = randi(length1);
%交换
temp = path(index1);
path(index1) = path(index2);
path(index2)=temp;
%end
end
mutatedPath = path;
end
function [ output_args ] = paint( cities, pop, minPath, totalDistances,gen)
gNumber=gen;
[~, length] = size(cities);
xDots = cities(1,:);
yDots = cities(2,:);
%figure(1);
title('GA TSP');
plot(xDots,yDots, 'p', 'MarkerSize', 14, 'MarkerFaceColor', 'blue');
xlabel('经度');
ylabel('纬度');
axis equal
hold on
[minPathX,~] = find(totalDistances==minPath,1, 'first');
bestPopPath = pop(minPathX, :);
bestX = zeros(1,length);
bestY = zeros(1,length);
for j=1:length
bestX(1,j) = cities(1,bestPopPath(1,j));
bestY(1,j) = cities(2,bestPopPath(1,j));
end
bestX(1, length+1) = cities(1, bestPopPath(1, 1));
bestY(1, length+1) = cities(2, bestPopPath(1, 1));
title('GA TSP');
plot(bestX(1,:),bestY(1,:), 'red', 'LineWidth', 1.25);
legend('城市', '路径');
axis equal
grid on
%text(5,0,sprintf('迭代次数: %d 总路径长度: %.2f',gNumber, minPath),'FontSize',10);
drawnow
hold off
end
function [ distances ] = calculateDistance( city, ctype )
%计算距离
if(strcmp(ctype, "ATT"))
[~, col] = size(city);
distances = zeros(col);
for i=1:col
for j=1:col
rij = sqrt( ((city(1,i)-city(1,j))^2 + (city(2,i)-city(2,j))^2) / 10);
tij = round(rij);
if tij
function distance = getDistance(path, distances)
col = length(path);
distance = distances(path(col),path(1));
for j=1:col-1
distance = distance + distances(path(j),path(j+1));
end
end
实验数据集 | 实验效果 | 最优解 | 百分比 |
---|---|---|---|
att48 | 10684 | 10628 | 99.5% |
ch130 | 6212 | 6110 | 98.35% |
a280 | 2725 | 2579 | 94.6% |
att532 | 31143 | 27686 | 88.25% |
dsj1000 | 21803908 | 18660188 | 85.5% |
rl11849 | 1107708 | 923288 | 83.4% |
usa13509 | 24826834 | 19982859 | 80.5% |
如果对你有帮助,欢迎转发收藏哦~
转发注明出处,感谢
最终代码打包地:https://download.csdn.net/download/a1303835514/14978704