巡迴旅行商問題TSP
TSP問題:給定一個圖
問題抽象:例如一條城市順序表
基於這樣的思想,只需要對遺傳算法的編碼部分作出改動:
function parent = InitGroup(GroupNum, Dist)
parent.fitness = [];
parent.chrom = [];
NumCity= size(Dist, 2);
for i = 1:GroupNum
city = 1 : NumCity;
order = randperm(NumCity);%隨機生成城市順序
item = [];
for j = 1 : NumCity - 1
item = [item, find(city == order(j))];
city(find(city == order(j))) = [];
end
%按照上文規則,根據順序生成染色體item
item = [item, 1];
item = num2str(item);
item(find(item==' ')) = [];
parent.chrom = [parent.chrom; item];
%其實沒必要轉化成字符串
%若城市數大於9(出現兩位數三位數時)
%採取單字節的字符串編碼在交叉時就會出現問題
%直接用double數組就好
end
parent.fitness = CalcFit(parent.chrom, Dist);
end
上段代碼中本來爲了節省空間,將編碼轉爲字符串。但在調試的過程中發現當城市數大於9後兩位數的城市代碼會破壞原先字符串的單字節特性,使交叉操作出現錯誤。故應該直接採取double數字編碼。
採用隨機生成的數據,照樣是當某解重複出現十次以上時認爲算法收斂。應用自適應的交叉變異概率的遺傳算法,可以得到某一次代碼運行時,隨機生成的TSP解的示意圖:
貼出本代碼之前,還有一些說明:
1. 本次計算中,由於TSP問題的特殊性,還加入了精英原則,即在選擇操作時,保留當前適應度最高的個體。
2. 本代碼中適應度計算(行走的總距離)時的解碼Decode過程沒有使用向量運算,而是直接循環嵌套。導致代碼效率較低。有朋友想出如何用向量運算的話可以顯著提高算法的運行速度。
3. 遺傳算法得出的是較優解,不保證每次最優。
%%
%緙栫爜鏂瑰紡錕?鍩烘湰浜岃繘鍒剁紪錕?
%Input: FitFunc: Any function
% pCrossover: probability of crossover, default 0.5
% pMutation: probability of mutation, default 0.04
% GroupNum: number of individuals of the virtual group, default 30
% MaxIter: maximum iterations
% MaxRepeat: (optional)determine the convergence standard 鍒ゆ柇鏀舵暃
%parent.fitness
%parent.chrom
function iter = GA_TSP(pCrossover, pMutation, GroupNum, MaxIter, MaxRepeat)
% Default parameters
if nargin < 6
MaxRepeat = 10;
if nargin < 5
MaxIter = 1000;
if nargin < 4
GroupNum = 10;
if nargin < 3
pMutation = 0.03;
if nargin < 2
pCrossover = 0.45;
end
end
end
end
end
NumCity = 9;
CityPos = 10 * rand(NumCity, 2) + 10;
Dist = zeros(NumCity, NumCity)
for i = 1:NumCity
for j = i+1:NumCity
Dist(i, j) = sqrt((CityPos(i, 1) - CityPos(j, 1))^2 + (CityPos(i, 2) - CityPos(j, 2))^2);
Dist(j, i) = Dist(i, j);
end
end
Result = [];
epsilon = 1e-4;
iter = 0;
iRepeat = 1;
thisMax = 0;
parent = InitGroup(GroupNum, Dist); %Generate initial population
while iter < MaxIter
%% Heredity
children1 = Crossover(parent, pCrossover/iter^0.1); %Return crossovered chromes
children.chrom = [];
children.fitness = [];
children.chrom = Mutation([parent.chrom; children1], pMutation/iter^0.1);
children.fitness = CalcFit(children.chrom, Dist);
children = select(children, GroupNum);
parent = children;
iter = iter + 1;
%parent.chrom;
%[m, I] = max(parent.fitness)
if abs((thisMax-max(parent.fitness))/max(parent.fitness)) < epsilon
(thisMax-max(parent.fitness))/max(parent.fitness);
iRepeat = iRepeat + 1;
if iRepeat == 10
break
end
else
iRepeat = 1;
end
[thisMax, I] = max(parent.fitness);
%% Decode
axis([0 22 0 22])
order = [];
city = 1 : NumCity;
for j = 1 : NumCity
order = [order, city( 1 + rem(str2num(parent.chrom(I(1), j))-1, length(city)) )];
city(1 + rem(str2num(parent.chrom(I(1), j))-1, length(city))) = [];
end
disp(200 - thisMax);
Result = [Result; thisMax];
end
%% Draw
scatter(CityPos(:,1), CityPos(:,2));
hold on
order
for j = 1 : NumCity
plot([CityPos(order(j),1) CityPos(order(rem(j, NumCity)+1),1)], [CityPos(order(j),2) CityPos(order(rem(j, NumCity)+1),2)],'r');
end
hold off
end
%Encoding method:
function parent = InitGroup(GroupNum, Dist)
parent.fitness = [];
parent.chrom = [];
NumCity= size(Dist, 2);
for i = 1:GroupNum
city = 1 : NumCity;
order = randperm(NumCity);
%order = num2str(order);
%order(find(b-' '==0)) = [];
item = [];
for j = 1 : NumCity - 1
item = [item, find(city == order(j))];
city(find(city == order(j))) = [];
end
item = [item, 1];
item = num2str(item);
item(find(item==' ')) = [];
parent.chrom = [parent.chrom; item];
end
parent.fitness = CalcFit(parent.chrom, Dist);
end
%Population = ['12345';
% '23451';]
%Calculate Fitness
function Fitness = CalcFit(Population, Dist)
NumCity = size(Dist, 2);
PopNum = size(Population, 1);
Fitness = zeros(PopNum, 1);
%Decode:
%city = 1 : NumCity;
%city = repmat(city, GroupNum, 1);
%Population
for i = 1 : PopNum
order = [];
city = 1 : NumCity;
for j = 1 : NumCity
order = [order, city( 1 + rem(str2num(Population(i, j))-1, length(city)) )];
city(1 + rem(str2num(Population(i, j))-1, length(city))) = [];
end
end
for i = 1 : NumCity
city1 = order(i);
city2 = order(1 + rem(i, NumCity));
Fitness = Fitness + Dist((city1-1)*NumCity + city2);
end
Fitness = 200 - Fitness;
end
%roulette selcting method. The same
function newPop = select(parent, PopNum)
%Add: The best survive?
cumFit = cumsum(parent.fitness)/sum(parent.fitness);
[M, I] = max(parent.fitness);
newPop.chrom(1,:) = parent.chrom(I, :);
newPop.fitness(1) = parent.fitness(I);
for i = 2 : PopNum
index = find (cumFit - rand > 0);
newPop.chrom(i,:) = parent.chrom(index(1),:);
newPop.fitness(i) = parent.fitness(index(1));
end
end
function childrenChrom = Crossover(parent, pCrossover)
[PopNum, CityNum] = size(parent.chrom);
childrenChrom = [];
[M, I] = max(parent.fitness);
childrenChrom = parent.chrom(I, :); %Parent with highest fitness
for i = 1 : PopNum/2
RandCross = rand(1);
if RandCross < pCrossover
i = fix(rand(1)*PopNum + 1);
j = fix(rand(1)*PopNum + 1);
while i == j
i = fix(rand(1)*PopNum + 1);
j = fix(rand(1)*PopNum + 1);
end
BreakPoint = fix(rand(1)*CityNum + 1);
temp = parent.chrom(i, 1:BreakPoint);
parent.chrom(i, 1:BreakPoint) = parent.chrom(j, 1:BreakPoint);
parent.chrom(j, 1:BreakPoint) = temp;
childrenChrom = [childrenChrom; parent.chrom(i, :); parent.chrom(j, :)];
end
end
end
function childrenChrom = Mutation(chrom, pMutation)
[PopNum, CityNum] = size(chrom);
childrenChrom = chrom;
for i = 1:PopNum
for j = 1:CityNum
if rand < pMutation
childrenChrom(i, j) = num2str(floor(rand*CityNum)+1);
end
end
end
end