这个算法收敛速度还算满意。此算法可以计算n皇后问题,只需要将n改为相应整数即可!
主程序:
clear; clc; %% %八皇后问题,8X8的棋盘上,放置8个皇后,使之两两都不能攻击 %使用退火算法计算 %初始的状态,随机在棋盘上放置8个皇后,每列放一个,使每一行都不能攻击 n = 8; %8皇后 %% %产生一个随机的状态 state = randperm(n); %% %计算当前状态的h函数值(与目标状态的差距,h越大,差距越大。h=0代表此状态即为目标状态) h = fun_c(state); disp(state); disp(['初始状态的h为',num2str(h)]); %% %用退火算法从初始状态产生新的状态 %从时间1开始,t代表时间 t = 1; t_max = 1e5; %允许循环的最大时间 plt = zeros(3,t_max); while t < t_max && h ~=0 %交换现在状态中的任意两个数 i_rand = 1; j_rand = 1; while i_rand == j_rand i_rand = randi(n); j_rand = randi(n); end state_tmp = state; tmp = state_tmp(i_rand); state_tmp(i_rand) = state_tmp(j_rand); state_tmp(j_rand) = tmp; h_tmp = fun_c(state_tmp); if h_tmp <= h state = state_tmp; h = h_tmp; elseif h_tmp > h T = fun_temp(t); % 当前温度 %温度越高,增加的能量越低,此状态的概率越大 p = exp(-(h_tmp-h)*500/T); %有一定的概率接受此状态 if p > rand(1) state = state_tmp; h = h_tmp; end end plt(1,t) = t; plt(2,t) = fun_temp(t); plt(3,t) = h; t = t + 1; end figure(1); axis auto; plot(plt(1,1:(t-1)),plt(2,1:(t-1)),'b'); %t VS T hold on; plot(plt(1,1:(t-1)),100*plt(3,1:(t-1)),'r'); %t VS h hold off; %% if h == 0 disp('退火算法成功'); else disp('退火算法失败'); end disp(['经历的时间为',num2str(t)]); disp(['最终状态的h为',num2str(h)]); disp(state); %将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 %% %显示最终的状态 disp(state_full);
各子函数
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
function T = fun_temp(t) %给定退火的时间和速率,t为时间,T为温度 if t<= 50 T = 1000; elseif t>50 && t<1000 T = 1000 - ( t - 50 )*100/95; else T = 1e-3; end end