1. 程式人生 > >通俗解釋matlab之遺傳演算法程式部分(二)

通俗解釋matlab之遺傳演算法程式部分(二)

(1)程式怎麼開始

從哪裡開始程式比較好了?直接先主函式吧,然後再分著說:

%-------------函式說明----------------

%             主函式      

%---------------------------------------

function main()

clear

clc

popsize = 100;     %種群大小

chromlength = 10;  %二進位制編碼長度

pc = 0.6;          %交叉概率

pm = 0.001;        %變異概率

pop = initpop(popsize,chromlength);   %初始種群

  for i=1:100

     [objvalue] = cal_objvalue(pop);     %計算適應度值(函式值)

      fitvalue = objvalue;

     [newpop] = selection(pop,fitvalue);  %選擇操作

     [newpop] = crossover(newpop,pc);     %交叉操作

     [newpop] = mutation(newpop,pm);      %變異操作

      pop = newpop;                %更新種群

      [bestindividual,bestfit]=best(pop,fitvalue);%尋找最優解

      x2 = binary2decimal(bestindividual);

      x1 = binary2decimal(newpop);

      [y1] = cal_objvalue(newpop);    

      if  mod(i,10)==0

      figure;   

      fplot('10*sin(5*x)+7*abs(x-5)+10',[0 10]);

      hold on;

      title(['迭代次數為 n=' num2str(i)]);

      plot(x1,y1,'*');

      end

  end

      fprintf('the best X is  --->>%5.2f\n'

,x2);

      fprintf('the best Y is  --->>%5.2f\n',bestfit);

好了,這就是主函式,最後執行這個基本上什麼都有了,包括圖行什麼的都畫了,下面在分著說吧。。。

(2)關於二進位制種群怎麼生成

        從上面的主程式可以看到,我們設定了100個個體(也就是100個初始化x值),二進位制編碼長度為10位,那麼先開始自然是生成這100個隨機個體了,注意是隨機的,不能使相同的,也就是說生成100個不同的二進位制編碼組合,像11000 10101這樣的,生成100組。怎麼辦,直接上程式:

%-------------函式說明----------------

%    初始化種群大小

%       輸入變數:

%               popsize:種群大小

%               chromlength:染色體長度--》轉化的二進位制長度

%       輸出變數:

%               pop:種群

%---------------------------------------

function pop = initpop(popsize,chromlength)

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

很簡單,一句話搞定,說一下,關於randm,n)用法,就是生成mn列的0~1之間隨機數,比如rand(3,4)為:

>> rand(3,4)

ans =

    0.6028    0.1174    0.4242    0.2625

    0.7112    0.2967    0.5079    0.8010

0.2217    0.3188    0.0855    0.0292

round什麼意思?四捨五入,簡單,這樣上面就變成了什麼了,roundans):

>> round(ans)

ans =

     1     0     0     0

     1     0     1     1

     0     0     0     0

這樣當popsize=100chromlength=10時就生成了對應的種群個體了。

(3)如何在把二進位制返回對應的十進位制

知道了二進位制怎麼返回對應範圍內的x值呢?前面講了演算法怎麼構造了,特別要注意的是二進位制的位數以及轉化完後的x值範圍,程式如下:

%-------------函式說明----------------

%     二進位制轉化十進位制函式

%       輸入變數:

%               二進位制種群

%       輸出變數:

%               十進位制數值

%---------------------------------------

function pop2 = binary2decimal(pop)

[px,py]=size(pop);

for i=1:py

    pop1(:,i) = 2.^(py-i).*pop(:,i);

end

%sum(.,2)對行求和,得到列的向量

temp = sum(pop1,2);

pop2 = temp*10/1023;

輸入的為10001編碼的二進位制,輸出的是x值。開始取一下種群大小size(pop),顯然這裡py=10了,接著對每一位求和,就是pop1(:,i) = 2.^(py-i).*pop(:,i);這裡省略用了冒號,什麼意思了,就是對所有的行都是這個操作,冒號意思就是從1100了。那麼就其中某個個體比如第一個吧,假設為11001 10010,那麼先進行這麼一個操作後就是什麼了?是不是就是對應為的01乘以2的對應次冪了,若果是1就管用,是0就不管用,那麼這個值就是:(2^0*1+2^1)*1+0+0+(2^4)*1+...,這樣就算出了一個值了,因為是10位編碼,所以這個數介於0~2^90~1023。那麼最大為多少?1023吧。temp = sum(pop1,2);對行求和吧,2表示對行,1表示對列,像

ans =  

     1     0     0     0

     1     0     1     1

     0     0     0     0

>> sum(ans,1)

ans =

     2     0     1     1    

>> sum(ans,2)

ans =

     3

     2

     2

明白了吧,這樣temp就變成了有1001列的值,且每個值都在0~1023之間變化,最後一行不解釋了,就是把它轉化為1000~10之間的數值了。

(4)計算適應度函式值

這裡也就是根據100x計算對應的100y的值,簡單,根據上面的x值帶入就可以了:

%-------------函式說明----------------

%     計算函式目標值

%       輸入變數:

%              二進位制數值 

%       輸出變數:

%               目標函式值

%---------------------------------------

function [objvalue]=cal_objvalue(pop)

x = binary2decimal(pop);

%轉化二進位制數為x變數的變化域範圍的數值

objvalue = 10*sin(5*x)+7*abs(x-5)+10;

(5)如何選擇新的個體

       上面所有個體的函式值都計算出來了,存在objvalue中,此時它是不是也是100y值呀,恩。那麼對於現有的隨機生成的100x,怎麼來在選擇100組新的更好的x呢?這裡我們把選擇放在了交叉與變異之間了,都可以。如何選擇,就要構造概率的那個輪盤了,誰的概率大,是不是選擇的個體就會多一些?也就是現在的選擇就是100中選100個,最後出現的結果就是以前的100箇中最優的x有一個的話,選擇完後,可能就變成了5個這個x了,多餘的4個是不是相當於頂替了以前的不好的4x值,這樣才能達到x總數100不變呀。

%-------------函式說明----------------  

%       輸入變數:

%                pop  :  二進位制種群

%                fitvalue  :  適應度值

%       輸出變數:

%                newpop:選擇以後的二進位制種群

%---------------------------------------

function [newpop] = selection(pop,fitvalue)

%構造輪盤

[px,py]=size(pop);   

totalfit = sum(fitvalue);

p_fitvalue = fitvalue/totalfit; 

p_fitvalue = cumsum(p_fitvalue);%概率求和排序

%-------

ms = sort(rand(px,1));%從小到大排列

fitin = 1;

newin = 1;

while newin<=px

     if (ms(newin))<p_fitvalue(fitin)

         newpop(newin,:)=pop(fitin,:);

         newin=newin+1;

     else fitin=fitin+1;

     end

 end

這一部分可能不太好理解。自己好好想想吧。前三句,求出每個個體被選擇的概率吧,第四句,求和排序是幹什麼的呢?比如現在假設一個概率可能是:

p_fitvalue =     

    0.0816

    0.0408

    0.1020

    0.1224

    0.0408

    0.1429

    0.1837

    0.0204

    0.0816

    0.1837

那麼執行這一句p_fitvalue = cumsum(p_fitvalue)後就變為:

p_fitvalue =

    0.0816

    0.1224

    0.2245

    0.3469

    0.3878

    0.5306

    0.7143

    0.7347

    0.8163

    1.0000

也就是後面的值加到前面去在替換,這樣做有什麼意義呢?看後面吧。ms = sort(rand(px,1))這一句,生成1000~1的隨機概率數,然後在從小到大排序,100次,好理解,輪盤賭不得賭100次嗎?為什麼排序呢?這和前面的cumsum求和了有關。好了真正的選擇開始了,while裡面,先對小的概率進行選擇,

if (ms(newin))<p_fitvalue(fitin),就選擇一次,個體就多了一個。符合,newin概率的那個排序往上走,再符合,就再選擇一次,直到不符合了,fitin就往上走,這樣p_fitvalue(fitin)這部分是不是變大了一點,變大了再看看這個值能被選擇幾次。其實每一次迴圈就是對於某一個個體來說,看能選擇幾次。比如假設fitin=9時,此時假設p_fitvalue(9)=0.5,被選擇完了,下一次時fitin=10,我們假設離譜點,p_fitvalue(10)=0.7的話,那麼在下一次去ms(newin)的話(假設現在已經選擇了newin=30個個體了),是不是一定為選擇呀,然後第31個個體就是p_fitvalue(10)這個個體了,在32個,因為ms(newin)也是從小到大排序的,所以這次選擇只是增加了一點點吧,假設為0.52(肯定大於0.5吧,不然也不會進來),0.52<0.7,又選擇p_fitvalue(10)這個個體吧,這樣一直下去,33,34,,,好,來到了第50個個體假設。此時ms(50)=0.71了,不行了,是不是不行了,fitin是不是加1變為11了,如果p_fitvalue(11)較p_fitvalue(10)變化不大,比如只變為了0.72,那麼第11個個體的被選擇的概率是不是隻是0.02呀,這麼小點,也只能是就選擇那麼2回就下一個了吧,而我們看看第10個個體,從0.5變到了0.7,它的概率是不是0.2呀,哇,0.2呀,好大呀,難怪能從第32個個體一直選擇到第50個個體呀。好了到這裡應該能明白了吧,再不明白就沒招了,自己好好想吧。那麼這麼一系列迴圈後,是不是100個個體就被選擇出來了呀,想想選擇的結果是什麼樣子的呢?100個個體是不是會出現為:第2個第2個,第3個第3個第3個,第5個第5個第5個第5個第5個,第6個,第10個第10個第10個,等等等,總共100個個體吧(那些重複的數是在以前種群中的編號)。好多呀,應該講明白了選擇這塊。

(6)怎麼交叉

選擇完後是不是就有新的相對優秀的100個個體了,那麼對它們我們在來進行交叉變異才能形成新的個體,畢竟這100個個體還是先前100個個體裡面挑好的挑出來的吧。

%-------------函式說明----------------    

%       輸入變數:

%             pop:二進位制的父代種群數

%             pc:交叉的概率

%       輸出變數:

%             newpop:交叉後的種群數   

%---------------------------------------

function [newpop]=crossover(pop,pc)

[px,py]=size(pop);

newpop = ones(size(pop));

for i=1:2:px-1

    if(rand<pc)

        cpoint = round(rand*py);

        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(i,:)=pop(i,:);

        newpop(i+1,:)=pop(i+1,:);

    end

end

這裡涉及到了一個概率,就是交叉的概率pc,因為交叉不是一定發生,所以用一個概率來限制,部分還是要保持原來不變的。程式開始先生產一個與原種群同大小的全1矩陣newpop,然後迴圈for i=1:2:px-1,想想為什麼間距是2呢?簡單,每兩個交叉吧,就像自然界偉大的規律一樣,當然也有例外,不考慮吧。然後生產隨機0~1rand,在一比較就知道是不是該交叉,這裡的pc = 0.6,那麼想想每次rand的話,由於隨機性,是不是它產生小於0.6的概率就是0.6呀。好了選擇了交叉,就執行下面操作,其中還有個交叉點cpoint 計算,能明白吧,不解釋,後面兩句也能看懂。如果概率比0.6大的話(也就是0.4的概率rand出了0.6~1之間的數),就不選擇交叉,直接賦值就行了吧。這樣知道結束,有個問題,萬一px為奇數怎麼辦?是不是相當於有一個光棍呀,是呀,咋辦的呢?我也不知道,但是我們這裡為100,還好是剛好11呀,很人性,大家程式設計序也要人性化一點呀,雖然那是數字,沒有生命,但是體現了你的那個什麼,道德修養,對世界的愛吧~_~。

(7)關於變異

     好了交叉完後就生成了新一代的種群了吧,那麼在對這個種群再一次進行變異,實際情況下交叉變異應該是同時發生的吧,這裡先後無所謂了。那麼怎麼變異呢?

%-------------函式說明----------------  

%       輸入變數:

%                 pop  :  二進位制種群

%                 pm  :  變異概率

%       輸出變數:

%                newpop  :  變異以後的種群

%---------------------------------------

function [newpop] = mutation(pop,pm)

[px,py] = size(pop);

newpop = ones(size(pop));

for i=1:px

    if(rand<pm)

        mpoint = round(rand*py);

        if mpoint<=0

            mpoint=1;

        end

        newpop(i,:) = pop(i,:);

        if newpop(i,mpoint)==0

            newpop(i,mpoint)=1;

        else newpop(i,mpoint)=0;

        end

    else

        newpop(i,:)=pop(i,:);

    end

end

個人感覺變異更簡單,就是找個點(高階一點的找多個點),把它的值(01)取反不就得了嘛,程式就像上面的那樣,講到這裡了感覺真不需要解釋了,略過。

(8)最後一點了

至此,主體部分算是完了吧,剩下的就是顯示、處理部分了,回到主程式去看吧,接下來就是更新種群吧,然後再變換十進位制去,提取最好的y值和對應的x值,這部分還有個求最優適應度的函式:

%-------------函式說明---------------- 

%       輸入變數:  

%                pop  :  種群

%                fitvalue  :  種群適應度 

%

%       輸出變數:

%                bestindividual  : 最佳個體(二進位制個體)

%                bestfit     :   最佳適應度值

%---------------------------------------

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

感覺也沒有什麼號解釋的,就是比較唄,最優值在bestfit裡面,如果有比它大的,就更新,否則略過,不解釋。

        最後是在matlab環境下畫出來,if  mod(i,10)==0求餘操作吧,也就是每迭代10代我畫一次圖,觀察結果吧,後面的fprintf,打印出來,很簡單,自己看吧。。。

至此,整個遺傳演算法的大致原理講解完了,程式的各部分程式碼都在這裡了,把每個小部分都編寫一個m檔案存起來再一起執行就ok了。所有的程式排列以及這部分實驗結果在下一個裡面再貼出來吧~_~