遺傳演算法講解(Matlab描述)

Eremiter發表於2015-08-28

    另一個部落格地址:eremita.lofter.com

    遺傳演算法(Genetic Algorithm)是模擬達爾文生物進化論的自然選擇和遺傳學機理的生物進化過程的計算模型,是一種通過模擬自然進化過程搜尋最優解的方法。遺傳演算法可以解決多種優化問題,如:TSP問題、生產排程問題、軌道優化問題等,在現代優化演算法中佔據了重要的地位,本文簡要地介紹了遺傳演算法。

    在此之前我們通過一個小故事來通俗地講解遺傳演算法:

    從前有一群快樂的袋鼠(初代),生活在某某不知名的山上,有的袋鼠喜歡生活在高處,有的袋鼠喜歡生活在山腳,如圖:


    可是天有不測風雲,袋鼠有禍兮旦福。隨著全球氣候變暖,生活在山腳的袋鼠被熱死了(所以說保護環境、減少排放很重要)。但是,生活在山麓和山頂的袋鼠生存了下來,他(她)們互相啪啪啪,生下了下一代(第二代)。


    第二代袋鼠繼承了父母的好奇心和勇氣,有部分袋鼠繼續向山頂跑,當然也有一小部分去探索山腳的世界去了。但是,可惡的人類不知道節制,溫室效應繼續增強,山麓的袋鼠也相繼死去,當然,山腳的袋鼠也死了。

    值得開心的是,接近山頂的袋鼠還活著誒!!!這群勇敢的袋鼠快樂地生活、繁衍、生活……繁衍,氣候也不停地變熱。就這樣過了很久很久,終於有一隻袋鼠跑到了山頂!!!到了山頂!!!到了山頂!!!(重要的事情說三遍)跑到山頂的他也得到了一塊巧克力作為獎勵,然後,故事快樂的結束了。



    故事雖然結束了,但是,我們學習遺傳演算法的腳步不能停歇。回顧整個故事,每隻袋鼠就是個體,最開始袋鼠是隨機地分佈在整座山上的。但隨之氣候變暖,有的袋鼠死去(選擇)。同時,活下來的袋鼠繁衍,產生新的一代袋鼠,他們大部分生活在更高處,小部分去低處生活(交叉、變異)。同時,氣候又變暖了,活在山麓的袋鼠也死了,只有生活在更高處的袋鼠活了下來。就這樣周而復始地過了很久(迭代),終於有一隻袋鼠到了山頂,吃到了巧克力,故事結束(跳出迭代)。

    所以,遺傳演算法有幾個部分:

例題:

    尋找函式f(x)=x*sin(3*pi*x)的最大值,搜尋範圍[-1,2]。

    初始化種群

    隨機產生一定數量的個體,組成一個種群。程式碼如下:

function pop = IntPop(numPop)
pop = [];%初始化種群矩陣
for n=1:numPop
    pop(:,n) = 3*rand-1;%個體範圍[-1,2]
    %產生初始種群
end

      


    計算適應度

    計算適應度需要個人找到合適的目標函式,適應度函式的優化目標是適應度越大越好。所以,如果你的目標是min,請使用它的倒數,即max=1/min。本題中,在已知最大值的條件下,使用個體與目標的歐式距離的倒數作為適應度函式。當然,也可以將函式本身作為適應度函式。

function fitness = Fitness(pop)
for n = 1:size(pop,2)
    fitness(n) = 1/abs(2-Fx(pop(n)));
end

    選擇

     選擇是為了將適應度小的個體淘汰的,模擬了自然環境中的優勝劣汰。選擇的演算法一般選擇輪盤賭演算法。輪盤賭演算法是指,所有個體的適應度組成圓盤,適應度越大的扇形面積越大。


程式如下:

function parentsPop = Select(matrixFitness,pop,SELECTRATE)
sumFitness = sum(matrixFitness);%計算所有個體的適應度之和
accP = [];%積累概率
for n=1:size(pop,2)
    accP(n) = sum(matrixFitness(1:n))./sumFitness;
    %計算從1到n的積累概率
end
%輪盤賭選擇法
for n=1:round(SELECTRATE*size(pop,2))
    matrix = find(accP>rand);%找到比隨機數小的積累概率位置
    if isempty(matrix)
        continue
    end
    parentsPop(:,n) = pop(:,matrix(1));
    %將首個比隨機數小的積累概率小的位置的個體遺傳下去
end

    編碼

    編碼是為了產生一個2進位制染色體,方便進行交叉和變異。當然,編碼規則具體問題需要具體規定。

function binPop = Codeing(pop)
codeLength = 35;%編碼長度
pop = round((pop+1)*10^10);%[-1,2]浮點數
for n=1:size(pop,2)
    for k=1:size(pop,1)
        dec2binPop{k,n} = dec2bin(pop(k,n));        
        lengthPop = length(dec2binPop{k,n});        
        for s=1:codeLength-lengthPop
            dec2binPop{k,n} = ['0' dec2binPop{k,n}];
        end
    end
    binPop{n} = dec2binPop{k,n};
end

    解碼

    有編碼,當然也要解碼。

function pop = Incodeing(binPop)
popNum = 1;%染色體包含的引數數量
for n=1:size(binPop,2)
    Matrix = binPop{1,n};
    for num=1:popNum
        pop(num,n) = bin2dec(Matrix);
    end
end
pop = pop./10^10-1;

    交叉

    交叉互換是生物遺傳變異的主要形式,正是因為交叉互換,帶給了我們一些和父母都不相同的生理特徵。

    同樣,交叉運算元是遺傳演算法中非常重要的一部分。機器從種群中隨機選擇一對“父母親”,隨機產生交叉位置,交換染色體的剩下部分。如圖


    發生交叉之後的染色體:


    可以看到,一次交叉就可以產生兩個新個體。當時,考慮種群的多樣性,我們可以只取一條個體。

    程式如下:

function kidsPop = Crossover(parentsPop,NUMPOP,CROSSOVERRATE)
kidsPop = {[]};n = 1;
while size(kidsPop,2)<NUMPOP-size(parentsPop,2)
    %選擇出交叉的父代和母代
    father = parentsPop{1,ceil((size(parentsPop,2)-1)*rand)+1};
    mother = parentsPop{1,ceil((size(parentsPop,2)-1)*rand)+1};
    %隨機產生交叉位置
    crossLocation = ceil((length(father)-1)*rand)+1;
    %如果隨即數比交叉率低,就雜交
    if rand<CROSSOVERRATE
        father(1,crossLocation:end) = mother(1,crossLocation:end);
        kidsPop{n} = father;
        n = n+1;
    end
end

    變異

    在遺傳變異過程中,另一個重要的過程就是變異。

    我們通過隨機某位取反,完成變異操作

    程式如下:    

function kidsPop = Variation(kidsPop,VARIATIONRATE)
for n=1:size(kidsPop,2)
    if rand<VARIATIONRATE
        temp = kidsPop{n};
        %找到變異位置
        location = ceil(length(temp)*rand);
        temp = [temp(1:location-1) num2str(~temp(location))...
            temp(location+1:end)];
       kidsPop{n} = temp;
    end
end

最後的優化結果:


完整程式下載地址:http://download.csdn.net/detail/treasure_c_j_l/9059215
    

相關文章