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