遺傳算法(三)解TSP問題

巡迴旅行商問題TSP

TSP問題:給定一個圖G=(V,E) ,每個邊eE 中有非負權值ω(e) ,尋找G 的Hamilton圈C ,使C 的總權W(C)=eE(C)ω(e) 最小。即旅行商的路程最短。

問題抽象:例如一條城市順序表C={1,2,3,4,5,6,7,8,9} , 先將即將探訪的城市在順序表中的順序記錄在染色體p 中;然後每走一個城市,將其在順序表C中劃去,那麼下一個要走的城市在新順序表中的位置將被記錄在p 中。例如,若實際的行走順序T={1,2,3,4,5,6,7,8,9} 則對應的染色體p={1,1,1,1,1,1,1,1,1} ;若實際的行走順序爲T={2,1,5,6,7,9,3,4,8} ,則對應的染色體p={2,1,3,3,3,4,1,1,1} 。在這種情況下,兩個不同染色體在任意位置斷點交叉後得到的新染色體都能構建出一個符合實際情況的路徑。(若染色體中某個值m大於當前剩餘的城市數目,則取m除以當前城市數目的餘數;例如,若染色體p={1,1,9,1,1,1,1,1,1} ,9大於其當前城市數7,9除以7餘2, 則這時染色體等價於p={1,1,2,1,1,1,1,1,1} )。

基於這樣的思想,只需要對遺傳算法的編碼部分作出改動:

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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章