最近在做一個遺傳算法應用的課程大作業,在網上找了一些TSP的算法,結合别人的算法進行更改和優化了下,得到了最終的仿真結果,感謝前人栽的樹。
遺傳算法在TSP問題上的應用
本文采用遺傳算法來求解TSP問題,将中國的35個省會級城市作為研究對象,設定西安為起點,曆經其餘34座城市後,最後回到西安的一條最短路徑。
一、設計過程
1、适應度函數設計:
TSP的目标是路徑總長度為最短,在進行選擇時更具适應度值越大則被遺傳到下一代的機率越大,适應度值與路徑總距離呈反比關系,是以路徑總長度的倒數就可以為TSP的适應度函數:
2、序表達式編碼
序表達式是TSP問題的最自然的表達,其中城市是按通路的順序排列的。例如,對于9個城市的TSP:3-2-5-4-7-1-6-9-8-3可簡單表示為向量[3254716983],表示從城市3出發依次經過2,5,4,7,1,6,9,8然後傳回城市3的一條路徑。
3、聯賽選擇算子設計
聯賽選擇是基于個體适應度之間大小關系的選擇方法,一般為兩兩比較,不需要滿足f(x)>0,具體步驟為将群體中随機選N個個體進行适應度值大小比較,将大的個體遺傳,将上述過程重複M次就可以得到M個個體。在本次設計中,随機從父代種群中選擇4個路徑個體,進行适應度值比較,将适應度最大的個體遺傳到下一代,種群規模為200,則重複該選擇步驟200次,得到新一代種群。
4、順序交叉算子設計
順序交叉(OX):從父代A随機選擇一個編碼子串片段,放到子代A的對應位置,子代A空餘的位置從父代B中按照B的順序選取(與已有的編碼不重複)。同理可得子代B。設定父代A=872|139|0546,父代B=983|567|1420。交叉過程:對于父代A,選擇139進行遺傳到下一代,其餘部分從父代B中按照期序随機選取,從父代B中選擇的元素不能和1、3、9散字相同,依次放到子代的對應位置,得到下一代A1,子代B1的求解過程同上,求解過程如下。
5、兩點對換變異算子設計(Mutation)
在本文中采用兩點對換變異算子對種群進行變異操作,由于TSP問題的限制為路徑中每個城市隻能周遊一次,是以不能采用遺傳算法常用的基于二進制編碼的變異操作,不能由簡單的變量進行翻轉來實作變異。兩點對換變異算子為在TSP問題中的個體編碼是一系列城市的序列,随機在這個城市序列中抽取兩個城市,然後交換他們的位置,這樣就實作了個體的變異操作。
二、仿真結果如下:
1、遺傳算法的基本運算步驟
(1)初始化:設定進化代數計數器t=0,設定最大進化代數T,随機生成M個個體作為初始群體P(0)。(2)個體評價:計算群體P(t)中各個個體的适應度。(3)選擇運算:将選擇算子作用于群體。選擇的目的是把優化的個體直接遺傳到下一代或通過配對交叉産生新的個體再遺傳到下一代。選擇操作是建立在群體中個體的适應度評估基礎上的。(4)交叉運算:将交叉算子作用于群體。遺傳算法中起核心作用的就是交叉算子。(5)變異運算:将變異算子作用于群體。即是對群體中的個體串的某些基因座上的基因值作變動。群體P(t)經過選擇、交叉、變異運算之後得到下一代群體P(t+1)。(6)終止條件判斷:若t=T,則以進化過程中所得到的具有最大适應度個體作為最優解輸出,終止計算。
2、初始種群路徑圖:
3、疊代找到最優解的路徑圖
4、收斂曲線圖
二、具體matlab代碼如下:
主函數main.c
clear;
clc;
tStart = tic; % 算法計時器
%************************初始化參數變量*******************************
[Num,Txt,Raw]=xlsread('provincial_ capital_coordinate12.xlsx'); %經緯度資料
cities = Num(:,5:6);
cities=cities';
[~,cityNum]=size(cities);
maxGEN = 500;
popSize = 300; % 遺傳算法種群大小
crossoverProbabilty = 0.95; %交叉機率
mutationProbabilty = 0.33; %變異機率
%********初始化種群***********
% 計算上述生成的各個城市之間的距離斷
distances = calculateDistance(cities);
% 生成種群,每個個體代表一個路徑
gbest = Inf;%行:Inf 無窮大
parentPop = zeros(popSize, cityNum);
% 随機打亂種群
for i=1:popSize
parentPop(i,2:cityNum-1) = randperm(cityNum-2); %randperm功能是随機打亂一個數字序列,将1-cityNum數字序列随機打亂
for a=2:cityNum-1
parentPop(i,a)=parentPop(i,a)+1;
end
parentPop(i,1)=1;
parentPop(i,cityNum)=cityNum;
end
% 定義子代種群
childPop = zeros(popSize,cityNum);
%定義每代的最小路徑
minPathes = zeros(maxGEN,1);
%****************************
%**********************************************************************
%*********************GA算法選擇交叉變異執行過程************************
for genNum=1:maxGEN
% 計算适應度的值,即路徑總距離
[fitnessValue, sumDistance, minPath, maxPath] = fitness(distances, parentPop);
tournamentSize=4; %設定大小
for k=1:popSize
%% 以下為聯賽選擇法
% 随機選擇父代
tourPopDistances=zeros( tournamentSize,1);
for i=1:tournamentSize
randomRow = randi(popSize);%行:傳回一個介于1到popSize的僞随機整數
tourPopDistances(i,1) = sumDistance(randomRow,1);
end
% 選擇最好的,即距離最小的
parent1 = min(tourPopDistances);
[parent1X,parent1Y] = find(sumDistance==parent1,1, 'first');%選出随機的4個中最短距離種群序号
parent1Path = parentPop(parent1X(1,1),:);%%選出随機的4個中最短距離種群路徑
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 = parentPop(parent2X(1,1),:);
%% 以上為聯賽選擇法
%% 順序交叉(OX)
individual = crossover(parent1Path, parent2Path, crossoverProbabilty);%個體交叉
%%
%% 對換變異(由于路徑不能出現重複元素,是以變異不能為随意變化,本實驗采用随機交換兩元素位置來實作變異)
individual = mutate(individual, mutationProbabilty);%個體變異
%%
childPop(k,:) = individual(1,:);%行:儲存下一代種群的個體
minPathes(genNum,1) = minPath; %行:保留此代中最短路徑
end
% 更新父代
parentPop = childPop;%行:種群此時更新為最新子代
%% 畫圖
% 畫出目前狀态下的最短路徑
if minPath < gbest
gbest = minPath;
paint(cities, parentPop, gbest, sumDistance,genNum);%行:畫出最新的最短路徑軌迹
end
fprintf('代數:%d 最短路徑:%.2fKM \n', genNum,minPath);
% fprintf('%s\n', Txt(Bestpath(1,0),2));
%%
end
%% 疊代完成,進行最終結果顯示
figure
plot(minPathes, 'MarkerFaceColor', 'blue','LineWidth',1);
title({'最短路徑收斂曲線圖';['最短路徑距離為', num2str(minPath)]});
set(gca,'ytick',0:10:450);
ylabel('路徑總長度');
xlabel('種群代數');
grid on
tEnd = toc(tStart);
fprintf('時間:%d 分 %f 秒.\n', floor(tEnd/60), rem(tEnd,60));
根據經緯度計算城市間距離函數:calculateDistance.m
function [ distances ] = calculateDistance( city )
%計算距離
[~, col] = size(city);
distances = zeros(col);
for i=1:col
for j=1:col
distances(i,j)= distances(i,j)+ sqrt( (city(1,i)-city(1,j))^2 + (city(2,i)-city(2,j))^2 );
end
end
end
交叉函數:crossover.m
function [childPath] = crossover(parent1Path, parent2Path, prob)
% 交叉
random = rand();
if prob >= random %行:随機生成一個01之前的數,和機率值比較,相當于一個輪盤賭的選擇,來執行機率選擇執行操作
[l, length] = size(parent1Path);
childPath = zeros(l,length);
setSize = floor(length/2) -1; %行:floor 朝負無窮大方向取整
offset = randi(setSize); %行:随機 産生1-setSize之間的整數
parent1OXstart=offset; %行:0X交叉,父代1遺傳給子代的片段起點
parent1OXend=setSize+offset-1; %行:0X交叉,父代1遺傳給子代的片段終點
for i=parent1OXstart:parent1OXend
childPath(1,i) = parent1Path(1,i);
end
childPath(1,1) = parent1Path(1,1);%行:将路徑的起點和終點直接賦給子代,因為起點和終點不變
childPath(1,length) = parent1Path(1,length);
%行:parent2依順序放入子代位坐标
parent2Point=2;
for x=2:length-1
if childPath(1,x)==0
for y=parent2Point:length-1
if ~any(childPath == parent2Path(1,y))%行:如果子代路徑相應元素不等于父代元素
childPath(1,x)=parent2Path(1,y);
parent2Point=y+1;
break;
end
end
end
end
else
childPath = parent1Path;
end
end
變異函數:mutate.m
function [ mutatedPath ] = mutate( path, prob )
%對指定的路徑利用指定的機率進行更新
%行:由于路徑不予許存在相同的數,是以變異不能随意變,隻能用随機互換位置的方法
random = rand();
if random <= prob
[l,length] = size(path);
index1 = randi([2,length-1]);
index2 = randi([2,length-1]);
%交換
temp = path(l,index1);
path(l,index1) = path(l,index2);
path(l,index2)=temp;
end
mutatedPath = path;
結果路徑圖檔顯示函數:paint.m
function [ bestPopPath ] = paint( cities, pop, minPath, totalDistances,gen)
gNumber=gen;
[~, length] = size(cities);
xDots = cities(1,:);
yDots = cities(2,:);
%figure(1);
%行:先畫城市的固定坐标
plot(xDots,yDots, 'p', 'MarkerSize', 10, 'MarkerFaceColor', 'green');
xlabel('經度');
ylabel('緯度');
axis equal
[num,txt,raw]=xlsread('provincial_ capital_coordinate12.xlsx'); %經緯度資料%
text(xDots(1)+0.2,yDots(1)+0.2,txt(1+1,2),'FontSize',9.5,'Color','red','FontWeight','Bold') ; %先畫起點,突出起點
for i=2:length-1
text(xDots(i)+0.2,yDots(i)+0.2,txt(i+1,2),'FontSize',8,'Color','blue','FontWeight','Bold') ; %加上0.01使标号和點不重合,可以自己調整
end
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
% title('目前代數%d 的路徑圖');
title(['第', num2str(gNumber),'代種群的最優路徑圖']) ;
%行:再畫城市的路徑連線
plot(bestX(1,:),bestY(1,:), 'red', 'LineWidth', 1.25);
legend('城市', '路徑');
axis equal%行:axis equal/将橫軸縱軸的定标系數設成相同值。
grid on
%text(5,0,sprintf('疊代次數: %d 總路徑長度: %.2f',gNumber, minPath),'FontSize',10);
drawnow
hold off
end
最後感謝前人的部落格,然我這個小白從0 完成了遺傳算法的作業,也希望此部落格能幫助到大家。
具體代碼:https://download.csdn.net/download/qq_37271216/12089106
有需要代碼的朋友也可以把郵箱留下,免費發到郵箱!