求解八皇后问题的退火算法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了求解八皇后问题的退火算法相关的知识,希望对你有一定的参考价值。

这个算法收敛速度还算满意。此算法可以计算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

 


 

以上是关于求解八皇后问题的退火算法的主要内容,如果未能解决你的问题,请参考以下文章

八皇后问题求解java(回溯算法)

8皇后问题SQL求解(回溯算法)

算法求解思路培养-2. 八皇后问题(说说递归)

八皇后问题求解的C语言程序的实现

八皇后问题的遗传算法解法,c语言编写

暴力穷举和回溯法(八皇后问题)