问题描述
旅行商问题(Traveling Salesman Problem,TSP)是一个著名的组合优化问题,其目标是找到一条最短路径,使得旅行商可以经过所有给定城市一次并回到起始城市。在这个问题中,假设有一位旅行商要访问多个城市,并且每两个城市之间都有确定的距离或成本。TSP的目标是找到一条路径,使得旅行商走过的总距离或总成本最小。
遗传算法
具体算法原理参考计算智能
代码实现
% 按表格定义城市坐标
% 按表格定义城市坐标
map = [18, 54; 87, 76; 74, 78; 71, 71; 25, 38; 58, 35; 04, 50; 13, 40; 18, 40; 24, 42;
71, 44; 64, 60; 68, 58; 83, 69; 58, 69; 54, 62; 51, 67; 37, 84; 41, 94; 02, 99;
07, 64; 22, 60; 25, 62; 62, 32; 87, 07; 91, 38; 83, 46; 41, 26; 45, 21; 44, 35];
n = max(size(map)); % 城市数
pc = 0.8; % 交叉概率
pm = 0.1; % 变异概率
MaxIter = 500; % 迭代次数
Popsize=150;% 染色体数/种群个体数
% 初始化祖先基因,150行30列的0矩阵
Road = zeros(Popsize, n);
for i = 1:Popsize
Road(i,:) = randperm(n);%产生1-30的随机序列进行打乱
end
Iter = 1;
MinRoad = zeros(MaxIter, n);%保存每次迭代的最短路径(城市序号排列)
MinRoad_value = zeros(MaxIter, 1);%保存每次迭代最短路径值
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%迭代主循环%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
while(Iter <= MaxIter)
Dist = calculateDistances(Road, map);%根据实际坐标计算每一条随机路径的长度
[Mindist, q] = min(Dist);%取距离最短的值Mindist以及序号q
Maxdist = max(Dist);%取距离最长的值Maxdist
MinRoad_parents = Road(q, :);
MinRoad_parents_value = Mindist;
% 利用最大最小值为每一个个体计算适应度(1行150列)
fitness = zeros(1, Popsize);
for i = 1:Popsize
fitness(i) = 1 - (Dist(i) - Mindist) / (Maxdist - Mindist);
end
% 对适应度进行从小到大排序,f为排序后适应度,p为排序后原本序号
[f, p] = sort(fitness);
% 使用轮盘赌选出同等数量的路径并替换
selected_indices = roulette_wheel_selection(f, Popsize);
for i = 1:Popsize
Road(p(i), :) = Road(p(selected_indices(i)), :);
end
% 交叉
for i=1:Popsize
if rand<pc %rand(1)小于 1 的随机数
% 随机选择两个父代
x = randi(Popsize,2,1);
parent1 = Road(x(1),:);
parent2 = Road(x(2),:);
% 使用顺序交叉获得子代
[parent1_new, parent2_new] = order_crossover(parent1, parent2);
Road(x(1), :) = parent1_new;
Road(x(2), :) = parent2_new;
end
end
% 计算交叉后路径的长度,并找出最小路径
Dist = calculateDistances(Road, map);
[Mindist, q] = min(Dist);
MinRoad_crossover = Road(q, :);
MinRoad_crossover_value = Mindist;
% 变异
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 = calculateDistances(Road, map);
[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(1, 2, 1);
draw_map = [map; map(1, :)];
plot(draw_map(:, 1), draw_map(:, 2), '*', 'DisplayName', '城市');
hold on;
plot(draw_map(:, 1), draw_map(:, 2), '-', 'LineWidth', 1, 'Color', [1, 0.5, 0],'DisplayName', '初始路径');
legend('show');
legend('Location', 'best');
for c = 1:n
text(map(c, 1), map(c, 2), [' ' num2str(c)], 'Color', 'k', 'FontWeight', 'b');
end
% 最优路径
subplot(1, 2, 2);
ordered_map = map(MinRoad(Iterindex, :), :);
ordered_map = [ordered_map; ordered_map(1, :)];
plot(ordered_map(:, 1), ordered_map(:, 2), '*', 'DisplayName', '城市');
hold on;
plot(ordered_map(:, 1), ordered_map(:, 2), '-', 'LineWidth', 1, 'Color', [1, 0.5, 0],'DisplayName', '最优路径');
legend('show');
legend('Location', 'best');
for c = 1:n
text(ordered_map(c, 1), ordered_map(c, 2), [' ' num2str(c)], 'Color', 'k','FontWeight', 'b');
end
% 迭代曲线图
figure;
plot(MinRoad_value);
title('迭代曲线');
xlabel('迭代次数');
ylabel('最短路径长度');
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%工具函数部分%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 计算路径总长度
function Dist = calculateDistances(Road, map)
Popsize = size(Road, 1); % 群体大小
n = size(Road, 2); % 节点数量
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
% 计算两城市之间距离
function d = distance(A, B)
d = sqrt((B(1) - A(1))^2 + (B(2) - A(2))^2);
end
% 轮盘赌
function selected_indices = roulette_wheel_selection(f, num_selections)
selected_indices = zeros(1, num_selections);
% 对适应度进行归一化
nf = f / sum(f);
% 选择 n 次
for iter = 1:num_selections
total_prob = 0;
randNum = rand();
% 计算累计概率,符合条件的则选择
for i = 1:length(nf)
total_prob = total_prob + nf(i);
if total_prob > randNum
selected_indices(iter) = i;
break;
end
end
end
end
% 顺序交叉
function [child1, child2] = order_crossover(parent1, parent2)
% 生成两个子代基因,初始化为-1
child1 = -ones(size(parent1));
child2 = -ones(size(parent2));
% 随机选择交叉的起始和结束位置
cp = sort(randi(length(parent1), 1, 2));
% 复制父代中交叉区域的基因到子代
child1(cp(1):cp(2)) = parent1(cp(1):cp(2));
child2(cp(1):cp(2)) = parent2(cp(1):cp(2));
% 补充缺失的基因到子代,按照父代中未被选择的顺序
child1(child1 == -1) = parent2(~ismember(parent2, child1));
child2(child2 == -1) = parent1(~ismember(parent1, child2));
end
输出结果
初始路径和最优路径对比图
迭代曲线图
最佳路径以及长度