遺傳算法


基本概念

  1. 染色體:待解決的數學問題的一個可行解成為染色體。
  2. 基因:一個可行解一般由多個元素構成,那么這每一個元素就被稱為染色體上的一個基因。
  3. 適應度函數:執行優勝劣汰的函數。將適應度高的染色體留下,將適應度低的染色體淘汰掉。從而經過若干次迭代后染色體的質量將越來越優良。
  4. 交叉:兩個染色體生成一個新的染色體,新染色體上的基因由輪盤賭算法完成。在每完成一次進化后,都要計算每一條染色體的適應度,然后采用公式計算每一條染色體的適應度概率。那么在進行交叉過程時,就需要根據這個概率來選擇父母染色體。適應度比較大的染色體被選中的概率就越高。這也就是為什么遺傳算法能保留優良基因的原因。(染色體i被選擇的概率 = 染色體i的適應度 / 所有染色體的適應度之和)
  5. 變異:交叉能保證每次進化留下優良的基因,但它僅僅是對原有的結果集進行選擇,基因還是那么幾個,只不過交換了他們的組合順序。這只能保證經過N次進化后,計算結果更接近於局部最優解,而永遠沒辦法達到全局最優解,為了解決這一個問題,我們需要引入變異。變異很好理解。當我們通過交叉生成了一條新的染色體后,需要在新染色體上隨機選擇若干個基因,然后隨機修改基因的值,從而給現有的染色體引入了新的基因,突破了當前搜索的限制,更有利於算法尋找到全局最優解。
  6. 選擇:淘汰fw個體,選出優秀個體傳給下一代。
  7. 表現型:輸入的值。
  8. 基因型:將輸入值編碼后的值。故表現型通過編碼變成基因型,基因型通過解碼變成表現型。
  9. 編碼方式:分為二進制編碼,實數編碼和符號編碼。

算法

流程

  1. 在算法初始階段,它會隨機生成一組可行解,也就是第一代染色體。
  2. 然后采用適應度函數分別計算每一條染色體的適應程度,並根據適應程度計算每一條染色體在下一次進化中被選中的概率。
  3. 通過“交叉”,生成N-M條染色體;
  4. 再對交叉后生成的N-M條染色體進行“變異”操作;
  5. 然后使用“復制”的方式生成M條染色體;

流程圖如下:

遺傳算法工具箱

從界面運行 APP - Optimization app,然后從 Solver 菜單選擇ga - Genetic Algorithm。

遺傳算法工具箱是求最小值的,最大值的話就添負號。

代碼

求 $f(x)=x+10sin(5x)+7cos(4x),x∈[0,10] $ 的最大值。

步驟

  1. 初始化種群
  2. 計算目標函數值和適應度值:目標函數的結果可以是負數,但是適應度值不能為負數。因為后面會用輪盤賭法算概率,如果適應度值為負數的話,概率也為負數。母豬會上樹概率都不可能為負數。所以,要進行處理,當目標函數為負數時候適應度值要為0。
  3. 選擇,交叉,變異
  4. 求最優個體,並記錄
  5. 繼續下一次迭代,直到迭代結束
  6. 根據每一次迭代的最優個體,再找出其中的最優個體,即為最優解

主程序

clear
clc
popsize=20;                         %群體大小
chromlength=10;                  %字符串長度(個體長度)
pc=0.6;                     %交叉概率,只有在隨機數小於pc時,才會產生交叉
pm=0.001;                           %變異概率
times = 2000                % 遺傳次數


pop=initpop(popsize,chromlength);               %隨機產生初始群體
for i=1:times
        [objvalue]=calobjvalue(pop);                  %計算目標函數
        fitvalue=calfitvalue(objvalue);    %計算群體中每個個體的適應度

        [newpop]=selection(pop,fitvalue);             % 選擇
        [newpop1]=crossover(newpop,pc);               % 交叉
        [newpop2]=mutation(newpop1,pm);               % 變異

        [objvalue]=calobjvalue(newpop2);     %計算目標函數
        fitvalue=calfitvalue(objvalue);   %計算群體中每個個體的適應度

        [bestindividual,bestfit]=best(newpop2,fitvalue); %求出群體中適應值最大的個體及其適應值
        y(i)=bestfit;   %返回的 y 是自適應度值,而非函數值
        x(i)=decodebinary(bestindividual)*10/1023; %將自變量解碼成十進制
        pop=newpop2;
end
fplot('x+10*sin(5*x)+7*cos(4*x)',[0 10])
hold on
plot(x,y,'r*')
hold on

[z ,index]=max(y);             % 計算所有最優良個體中的最優良個體
x5=x(index)                    % 計算最大值對應的x值
ymax=z

初始化 initpop.m

根據種群大小和個體長度構造個全為0和1的隨機二進制編碼基因的種群,同時這個種群每一橫行表示一個個體。此例的矩陣為20*10。

function pop=initpop(popsize,chromlength) 
pop=round(rand(popsize,chromlength)); 

計算目標函數值 calobjvalue.m

將基因型轉換為表現型。

function [objvalue]=calobjvalue(pop)
temp1=decodebinary(pop);   % 將pop每行轉化成十進制數
x=temp1*10/1023;      % 得到十進制數在0~1023之間,通過變換得到x屬於0~10的值
objvalue=x+10*sin(5*x)+7*cos(4*x);   % 通過x的值計算目標函數值

如果是和神經網絡連用的話,神經網絡輸入值歸一化的值就在0到1之間,所以只用將轉換的十進制數除以1023然后就可以代入網絡中。

除了把二進制數看作是二進制整數外,我還有個思路就是把二進制數看成小數,這樣的話轉換成十進制數的時候本來就在0~1之間,這樣就不用再處理了。

二進制轉換為十進制 decodebinary.m

sum函數第二個參數不寫默認是1,表示對每一列求和得到行向量。如果第二個參數輸入2,則表示對每一行求和得到列向量。

function pop2=decodebinary(pop)
[px,py]=size(pop);                   %求pop行和列數
for i=1:py
	pop1(:,i)=2.^(py-i).*pop(:,i);
end
pop2=sum(pop1,2);        %求pop1的每行之和

適應度函數 calfitvalue.m

如果目標函數值大於等於0,適應度是目標函數值。如果目標函數值小於0,適應度就是0。

function fitvalue=calfitvalue(objvalue)

[px,py]=size(objvalue);                   %目標值有正有負
for i=1:px
        if objvalue(i)>0                    
                temp=objvalue(i);          
        else
                temp=0.0;
        end
        fitvalue(i)=temp;
end
fitvalue=fitvalue';

選擇 selection.m

這坨代碼的思路是除掉fw個體,重復添加優秀個體,越優秀的個體添加的次數可能性越大。

function [newpop]=selection(pop,fitvalue) 
totalfit=sum(fitvalue);                   %求適應值之和
fitvalue=fitvalue/totalfit;       %單個個體被選擇的概率,輪盤賭算法的公式
fitvalue=cumsum(fitvalue);      % 累加是為了將分布律轉換為分布函數然后可以比較
[px,py]=size(pop);                       %20*10
ms=sort(rand(px,1));                   % 將隨機數從小到大排列
fitin=1;
newin=1;
while newin<=px       % 直到20個隨機數被走完才跳出循環
        if(ms(newin))<fitvalue(fitin)   % 概率小於分布函數,優良個體被選擇
                newpop(newin,:)=pop(fitin,:);
                newin=newin+1;
        else             % 概率大於分布函數,個體被淘汰,繼續選擇下一個個體
                fitin=fitin+1;
        end
end

如果這群個體中某個概率較大,則累加的數在累加到它的時候增量較大,它的增量較大幾率大於隨機數的增量,這個增量較大的個體會有更大的幾率多次被選中。

除了上面的代碼外,

交叉 crossover.m

交叉我還以為是基因的交叉,沒想到是鹼基對的交叉。將原來兩個個體交叉出現兩個新個體,然后用這兩個新個體來替換掉原來的兩個個體放入新的種群中。

function [newpop]=crossover(pop,pc)    % pc=0.6 pc是交叉概率
[px,py]=size(pop);    % px是種群數量,py是編碼長度
newpop=ones(size(pop));   % 生成一群全為1的種群
for i=1:2:px-1        % 步長為2,是將相鄰的兩個個體進行交叉
    if(rand<pc)     % 如果隨機數小於交叉概率,則發生了交叉
        cpoint=round(rand*py);    % cpoint: 0-10的隨機數
        newpop(i,:)=[pop(i,1:cpoint),pop(i+1,cpoint+1:py)]; % 交叉操作
        newpop(i+1,:)=[pop(i+1,1:cpoint),pop(i,cpoint+1:py)];  % 交叉操作
    else    % 沒有發生交叉newpop保持原有pop的個體
        newpop(i,:)=pop(i,:);
        newpop(i+1,:)=pop(i+1,:);
    end
end

變異 mutation.m

隨機選取一個位置,將該位置的0變成1或1變成0。

function [newpop]=mutation(pop,pm)    % pop是種群,pm是變異概率
[px,py]=size(pop);
newpop=ones(size(pop));
for i=1:px
	if(rand<pm)      % 隨機數小於變異概率,發生了變異
		mpoint=round(rand*py);     % 產生的變異點在1-10之間
		if mpoint<=0  % 可以寫成 mpoint == 0 ,因為這個值不可能小於0,但是可以等於0
		 	mpoint=1;                % mpoint表示變異位置
		end          
 		newpop(i,:)=pop(i,:);
		if any(newpop(i,mpoint))==0% 這個any意義不大,因為傳入的是一個值,不是矩陣
			newpop(i,mpoint)=1;     % 把0變異成1
		else
			newpop(i,mpoint)=0;    % 或者把1變異成0
		end
	else                          % 不發生變異,原來的個體還是原來的個體
		newpop(i,:)=pop(i,:);    
	end
end

求最大適應度個體 best.m

目的:將最大適應度個體保留在一個矩陣中,然后在最大適應度個體矩陣中找出其中最大適應度個體,從而獲得它的x值和y值,於是它就是最大值。

function [bestindividual,bestfit]=best(pop,fitvalue)
[px,py]=size(pop);
bestindividual=pop(1,:);
bestfit=fitvalue(1);
for i=2:px
        if fitvalue(i)>bestfit
                bestindividual=pop(i,:);
                bestfit=fitvalue(i);
        end
end

自己的一些感悟

  1. 關於交叉:0-1編碼的交叉是截取染色體部分進行交叉,實數編碼的交叉是所有染色體進行隨機權值相乘交叉。

  2. 關於選擇:如果適應度是越小越好可以將適應度取倒數(或者用一個常數除以這個數,比如10)然后再用輪盤賭算法。

  3. 關於變異:0-1編碼的變異是將1變成0,將0變成1。實數編碼就是將該數加上或減去該數與上界或下界的差或和(這個隨機判定)再乘以一個比較小的數,記得實數編碼要檢驗是否在規定邊界范圍內。0-1編碼不用檢測。關於變異有一套代碼:

fg = (rand*(1-num/maxgen))^2;  % num:當前迭代次數。maxgen:最大迭代次數
if pick > 0.5
	% chrom:種群。i:第i個個體索引。pos:變異的位置
	chrom(i, pos) = chrom(i, pos)+(chrom(i, pos)-bound(pos, 2))*fg;
else
	chrom(i, pos) = chrom(i, pos)+(bound(pos, 1)-chrom(i, pos))*fg;
end

參考文章

  1. 十分鍾搞懂遺傳算法:https://zhuanlan.zhihu.com/p/33042667

  2. 遺傳算法介紹並附上matlab代碼:https://www.cnblogs.com/LoganChen/p/7509702.html

  3. matlab遺傳工具包:https://zhuanlan.zhihu.com/p/158600868

  4. 一個非常好的理解遺傳算法的例子 強烈推薦入門:https://blog.csdn.net/u012422446/article/details/68061932

  5. 【算法】超詳細的遺傳算法(Genetic Algorithm)解析:https://www.jianshu.com/p/ae5157c26af9

  6. 遺傳算法(Genetic Algorithm)原理詳解和matlab代碼解析實現及對應gaot工具箱實現代碼https://blog.csdn.net/qq_35608277/article/details/83785678


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM