此算法收斂速度還可以,基本在1萬代之內就能找到解
主程序
clear; clc; %% %八皇后問題,8X8的棋盤上,放置8個皇后,使之兩兩都不能攻擊 %初始的狀態,隨機在棋盤上放置8個皇后,每列放一個 n = 8; %8皇后 %% %用遺傳算法計算 %先隨機獲得幾個個體,形成一個種群 %這個種群有10個個體 No_of_people = 10; people = randi(n,[No_of_people,n]); %計算每個初始種群的h值 people_h = ones(No_of_people,1); for i = 1:No_of_people people_h(i) = fun_c(people(i,:)); end %進化了多少代,由G來統計 G = 1; G_max = 1e5; plt = zeros(1,G_max); while prod(people_h)~=0 && G<=G_max %精英保留策略,保留初始種群中1/100的精英個體 %保留多少個精英 No_elite = fix(No_of_people/100); if No_elite == 0 No_elite =1; end %按照h值選出這些精英 [~,ind] = sort(people_h); index = ind(1:No_elite); people_elite = people(index,:); %計算這個種群中每個個體的優勢,以百分比表示,所有個體優勢之和為1 adv = people_h ./ sum(people_h); %從種群中隨機選出10對個體,根據個體的優勢來選擇,優勢越大的,被選中的概率越大 people_dad = people; people_mom = people; for i=1:No_of_people pick = ones(2,1); while pick(1)==pick(2) pick = randsrc(2,1,[1:No_of_people; adv']); end people_dad(i,:) = people(pick(1),:); people_mom(i,:) = people(pick(2),:); end %然后交叉繁殖,染色體交叉。一對夫婦只生一個孩子 for i=1:No_of_people %隨機生成一個交叉位點 p = randi(n-1); people(i,:) = [people_dad(i,1:p),people_mom(i,p+1:n)]; end %然后以一定的概率,產生隨機突變 for i=1:No_of_people %隨機生成一個突變位點 p_change = rand(1); p_dot = randi(n); %設定突變的概率為10% if p_change <= 0.1 people(i,p_dot) = randi(n); end end %更新種群的h值 for i = 1:No_of_people people_h(i) = fun_c(people(i,:)); end %找出繁殖的后代中最差的個體,個體數量 = 精英數量 [~,ind] = sort(people_h,'descend'); index_bad = ind(1:No_elite); %刪除最差的個體 people(index_bad,:) = []; %把精英加入種群 people_tmp = [people; people_elite]; people = people_tmp; %更新種群的h值 for i = 1:No_of_people people_h(i) = fun_c(people(i,:)); end plt(G) = min(people_h); G = G + 1; end plot(plt(1:G-1)); axis auto; %% if prod(people_h)==0 disp('遺傳算法收斂'); index = find(people_h == 0); disp('可能的解為 '); disp(people(index,:)); else disp('遺傳算法不收斂'); end disp(['經歷了 ',num2str(G-1),' 代遺傳']);
function [h] = fun_c(state) %根據一個狀態,評價它的代價函數 h = 0; n = length(state); %% %每列的狀態,看有多少個能互相攻擊,每兩兩攻擊算一次 for i=1:n count = length(find(state == i)); if count > 1; h = h + nchoosek(count,2); end end %% %將state轉換成nXn矩陣 state_full = zeros(n,n); for i=1:n for j=1:n if j == state(i) state_full(i,j) = 1; end end end %% %每個左斜對角的狀態,看有多少個能互相攻擊,每兩兩攻擊算一次 i=1; j=1; add = 0; while i<n+1 && j<n+1 && i>0 && j>0 %計算左斜對角每條線有多少個皇后 count = fun_calc_left(i,j,n,state_full); if count > 1; h = h + nchoosek(count,2); end if add == 0; j = j + 1; elseif add == 1; i = i + 1; end add = ~add; end %% %每個右斜對角的狀態,看有多少個能互相攻擊,每兩兩攻擊算一次 i=1; j=n; add = 0; while i<n+1 && j<n+1 && i>0 && j>0 %計算右斜對角有多少個皇后 count = fun_calc_right(i,j,n,state_full); if count > 1; h = h + nchoosek(count,2); end if add == 0; j = j - 1; elseif add == 1; i = i + 1; end add = ~add; end end
function count = fun_calc_left(i,j,n,state_full) %% %統計i,j 點,左下角 count = 0; i_l = i; i_r = i; j_l = j; j_r = j; while i_l>0 && j_l>0 && i_l<n+1 && j_l<n+1 count = count + state_full(i_l,j_l); i_l = i_l + 1; j_l = j_l - 1; end %% %右上角的個數 while i_r>0 && j_r>0 && i_r<n+1 && j_r<n+1 count = count + state_full(i_r,j_r); i_r = i_r - 1; j_r = j_r + 1; end %% %被重復加的,減去 count = count - state_full(i,j); end
function count = fun_calc_right(i,j,n,state_full) %% %統計i,j 點,左上角 count = 0; i_l = i; i_r = i; j_l = j; j_r = j; while i_l>0 && j_l>0 && i_l<n+1 && j_l<n+1 count = count + state_full(i_l,j_l); i_l = i_l - 1; j_l = j_l - 1; end %% %右下角的個數 while i_r>0 && j_r>0 && i_r<n+1 && j_r<n+1 count = count + state_full(i_r,j_r); i_r = i_r + 1; j_r = j_r + 1; end %% %被重復加的,減去 count = count - state_full(i,j); end