此算法收敛速度还可以,基本在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