1. 程式人生 > >遺傳演算法的matlab實現

遺傳演算法的matlab實現

      遺傳演算法(Genetic Algorithm,GA)是20世紀70年代初興起的一門新興學科。遺傳演算法的基本思想來源於達爾文的進化論和孟德爾的遺傳學說,它通過模擬生物進化的過程來求解問題。生物中的基因對應優化問題中的變數組合,一個解則代表了一個個體。通過生物基因的交叉與變異來改變種群的性狀(函式值)。通過進化過程中優勝劣汰的原則挑選出優秀的個體(函式值大或小),最終通過迭代的方式模擬生物的進化,得到一個適合生存於特定環境的種群,以此來求解出優化問題的全域性最優解。

      遺傳演算法已經發展得很成熟,廣泛應用於優化問題的求解。
①遺傳演算法只對個體的基因進行操作,所以無論實際問題多麼複雜,其穩定性都不會受到太大的影響。
②遺傳演算法的搜尋過程屬於平行計算,能夠很好地搜尋解空間。
③穩定性、魯棒性強,適用於非線性、高維複雜優化問題。

      其流程如下:


初始化種群相當於確定原始解的位置,交叉是利用親代資訊來生成下一代個體,變異是基因的變異,並以此來豐富基因匹配的種類,適應度即構造的函式所對應的函式值,自然選擇是根據特定的規律選擇進入下一代的個體。         其涉及到的演算法引數有:

      種群數量:每一次迭代中保留下來的個體數量,一般設定為20~200,少了演算法穩定性差,多了增加計算量且求解能力不是線性提升。
      迭代次數:種群的進化代數,簡易的問題100代~300代即可,1000代左右最常見,若問題複雜度高,可以適當增加迭代次數。
      交叉概率:個體進行基因交叉的概率,通常取為0.3~0.9。
      變異概率

:個體基因突變的概率,通常取為0.001~0.1。對於遺傳演算法的原型而言,變異概率大了,種群不穩定。

      遺傳演算法中基因的編碼方式大致有二進位制編碼十進位制編碼這兩種,二進位制編碼屬於演算法原型中的編碼方式,因其轉碼時會徒增額外的計算量,且在做離散型編碼時,二進位制遠沒有十進位制便捷、快速,故筆者不推薦使用二進位制的編碼方式,後續的例題將在基於十進位制編碼的基礎上進行講解,對二進位制編碼感興趣的同學可以自己去嘗試,對比一下究竟哪種方法更好。
      但不管你使用何種編碼方式,都要求任意一個解對應的編碼應該是獨一無二的,且基因突變時每個基因出現的概率應該滿足均勻分佈,只要滿足了這兩個條件,使用什麼編碼都無所謂,只是計算機上的計算速度不同而已。

      同樣地,我們結合例子來理解:

      現有一個函式,y = sin(x) + x * cos(x),求該函式在區間[0,2π]上的最大值。首先我們需要畫出函式的影象,如下圖:

     幾乎所有的實際問題轉化成函式之後都無法畫出影象,這裡只是為了好觀察才畫圖的,解決問題的第一步就像之前說的,先編碼,該問題中只有一個變數需要編碼,即x。
  • 編碼
      假設編碼長度取5,即將求解區間劃分了10^5份,但是要注意第一份是0,而不是1,長度越長精度越高。

      解碼的方式非常簡單,舉個例子,基因12345對應的x就是12345÷99999×(2π-0)+0 ~= 0.7757 。可以很明顯的看出,基因00000代表x = 0,99999代表x = 2π 。對於連續型的問題編碼大多采取這樣的方式,但對於離散型的編碼,需要根據具體的問題而設計,編碼設計的優劣直接影響演算法最終的結果。
     離散型的編碼也很常見,如TSP問題的編碼、NP問題的編碼等決策型的實際問題大多都需要使用離散型編碼,基礎階段不要深究這個,此處不多做介紹。

  • 交叉
      交叉是遺傳演算法中一個非常重要的操作,其優劣影響演算法的收斂速度。假設需要交叉的基因為 12345 與 66666,則斷點隨機取3,將第三個位置之後的序列交換生成兩個新解,123 66與666 45 。這個應該很好理解,斷點的選取應滿足均勻分佈。
  • 變異
      變異是遺傳演算法中另一個重要的操作,其優劣影響演算法的最終結果與全域性最優的接近程度。假設滿足變異條件的基因為 66666,則當變異點隨機取2時,將第二個位置處的值隨機替換,生成一個新解,6 0 666 。這個0的生成與變異點的選取應該滿足均勻分佈。也就是變異成6 8 666也是可以的,且這兩個出現的期望應該相等。
  • 自然選擇
       通常來說,自然選擇包括輪盤法排名法。

       輪盤法是通過每個個體與總體的適應度佔比來衡量其優劣,比值越大越不容易被淘汰,當然輪盤法的計演算法複雜度也相對較高:Pi = Fi ÷ ΣF
       排名法是通過每個個體的適應度排名來看的,排名越靠前越不容易被淘汰,排名法的計演算法複雜度相對較低:Pi = Ri ÷ N 或 P = (Ri - 1) ÷ N 

      Pi 是第i個個體被淘汰的概率,Fi 是第i個個體的適應度,Ri 是第i個個體的適應度排名,N 是種群個體數。遺傳原型中使用的是輪盤法,但該方法除了計算快之外缺點太多,故推薦使用排名法。即適應度排名越靠後,其被淘汰的機率越高。

程式碼如下:

clear
clc
close all

f = @(x) sin(x) + x .* cos(x);   % 函式表示式
ezplot(f, [0, 2*pi])             % 畫出函式影象

N = 50;                          % 種群上限
ger = 100;                       % 迭代次數
L = 5;                           % 基因長度
pc = 0.8;                        % 交叉概率
pm = 0.1;                        % 變異概率
dco = [10000; 1000; 100; 10 ;1]; % 解碼器
dna = randi([0, 9], [N, L]);     % 基因
hold on
x = dna * dco / 99999 * 2 * pi;  % 對初始種群解碼
plot(x, f(x),'ko','linewidth',3) % 畫出初始解的位置

x1 = zeros(N, L);                % 初始化子代基因,提速用
x2 = x1;                         % 同上
x3 = x1;                         % 同上
fi = zeros(N, 1);                % 初始化適應度,提速

for epoch = 1: ger               % 進化代數為100
    for i = 1: N                 % 交叉操作
        if rand < pc
           d = randi(N);            % 確定另一個交叉的個體
           m = dna(d,:);            % 確定另一個交叉的個體
           d = randi(L-1);          % 確定交叉斷點
           x1(i,:) = [dna(i,1:d), m(d+1:L)];  % 新個體 1        
           x2(i,:) = [m(1:d), dna(i,d+1:L)];  % 新個體 2
        end
    end
    x3 = dna;
    for i = 1: N                           % 變異操作
        if rand < pm
            x3(i,randi(L)) = randi([0, 9]);
        end
    end
    dna = [dna; x1; x2; x3];               % 合併新舊基因
    fi = f(dna * dco / 99999 * 2 * pi);    % 計算適應度,容易理解
    dna = [dna, fi];
    dna = flipud(sortrows(dna, L + 1));    % 對適應度進行排名
    while size(dna, 1) > N                 % 自然選擇
        d = randi(size(dna, 1));           % 排名法
        if rand < (d - 1) / size(dna, 1)
            dna(d,:) = [];
            fi(d, :) = [];
        end
    end
    dna = dna(:, 1:L);
end
x = dna * dco / 99999 * 2 * pi;            % 對最終種群解碼
plot(x, f(x),'ro','linewidth',3)           % 畫出最終解的位置
disp(['最優解為x=',num2str(x(1))]);
disp(['最優值為y=',num2str(fi(1))]);