遗传算法(n皇后问题)在MATLAB中被卡住

eagi6jfj  于 2023-02-23  发布在  Matlab
关注(0)|答案(1)|浏览(200)

我需要在MATLAB中解决n皇后问题,我已经尝试了下面所附的代码。我遇到的问题是,在模拟的某个点上,种群的“检查”卡在了某个值上(接近零但不是我需要的零)。90%的情况都是这样,剩下的10%给出了一个非常快的解决方案(在非常少的迭代中)。也许有些部分没有很好地优化,但这是我最好的尝试。随时询问关于它的任何事情,抱歉,如果评论是西班牙语的,谢谢。
这是我使用的代码,如果你多次运行它,其中只有几个会给予你一个解决方案。

% No siempre da soluciones, se suele quedar estancado con muy pocos jaques
% por población.

clc, clear
% Parámetros y vectores iniciales
N = 10000; % Número de iteraciones
n = 8; % Número de reinas
l = 8; % Longitud de cada lado del tablero
% Condición para que no haya más reinas que lados del tablero
while n > l
    n = n-1;
end
pob = 5; % Número de población
f = @(x) n*l - x; % Función de aptitud, entre mayor cantidad de jaques, menor será la puntuación.
tableros = cell(1,pob+2); % Celda en donde se guardarán los vectores de cada población
posiciones = cell(1,pob+2);
hijo1 = zeros(n,2);
hijo2 = zeros(n,2);
hijo1(1:n,1) = 0:n-1;
hijo2 = hijo1;

[posiciones_iniciales, tableros_iniciales, cont_jaques, puntuacion] = pob_inicial(pob, n, l, f);
posiciones = posiciones_iniciales;

% Padres
[valoresMayores, padres] = mink(cont_jaques,2);

cont = 1;
resultado = 0;
while cont ~= N
        pc = randi([1,n]); % Punto de cruzamiento
        hijo1 = posiciones{padres(1)};
        hijo2 = posiciones{padres(2)};
        hijo1(pc+1:n,2) = posiciones{padres(2)}(pc+1:n,2);
        hijo2(pc+1:n,2) = posiciones{padres(1)}(pc+1:n,2);
    if rand > 0.2 % Probabilidad de mutación del 80% del hijo 1
        m =  randi([1,n]);
        hijo1(m, 2) = randi([0,n-1]);     
    end
    if rand > 0.2 % Probabilidad de mutación del 80% del hijo 1
        m =  randi([1,n]);
        hijo2(m, 2) = randi([0,n-1]);     
    end
    posiciones{pob + 1} = hijo1;
    posiciones{pob + 2} = hijo2;

    % Posiciones de los tableros (para el cálculo de los jaques)
    for ii = 1:pob+2
    tableros{ii} = zeros(l,l); % Tablero (si en la casilla hay un 1, significa que en esa posición hay una reina)
    for k = 1:n
        tableros{ii}(posiciones{ii}(k,1)+1, posiciones{ii}(k,2)+1) = 1;
    end

    % Checar los jaques de los tableros iniciales
    n_jaques = jaques(posiciones{ii}(:,2)', l, ii, tableros);
    cont_jaques(ii) = n_jaques;
    
    % Prueba de aptitud
    puntuacion(ii) = f(cont_jaques(ii)); 
    end

    % Eliminados (los de menor aptitud)
    [valoresMenores, eliminados] = maxk(cont_jaques, 2);
    posiciones(:, eliminados) = [];
    cont_jaques(eliminados) = [];
    puntuacion(eliminados) = [];

    % Padres 
    [valoresMayores, padres] = mink(cont_jaques, 2);

    % Checar si hay soluciones
    for jj = 1:pob
        if cont_jaques(jj) == 0
            resultado = 1;
            break
        end
    end

    if resultado == 1
        break
    else
        cont = cont + 1;
    end
    
end

if cont == N
    fprintf('No se ha encontrado solución en %d iteraciones. \n', N)
else
    fprintf('Se encontró la solución despues de %d iteraciones. \n', cont)
end

for ii = 1:pob
    if cont_jaques(ii) == 0
        subplot(2, 3, 2)
        tablero(l)
        title('Tablero inicial', ii)
        reinas(posiciones_iniciales{ii}(:,1),posiciones_iniciales{ii}(:,2), n, 'red')
        subplot(2, 3, 5)
        tablero(l)
        title('Tablero final', ii)
        reinas(posiciones{ii}(:,1),posiciones{ii}(:,2), n, 'green')
    end
end

%% Función que genera las poblaciones iniciales aleatoriamente
function [posiciones_iniciales, tableros_iniciales, cont_jaques, puntuacion] = pob_inicial(pob, n, l, f)
% pob: Número de población
% n: Número de reinas
% l: Lado del tablero
% f: Función de aptitud

tableros_iniciales = cell(1,pob); % Celda en donde se guardarán los vectores de cada población
posiciones_iniciales = cell(1,pob); % Celda en donde se guardarán los vectores de cada población
cont_jaques = zeros(1,pob); % Vector de contador de jaques de cada tablero
puntuacion = zeros(1,pob); % Vector que guarda las puntuaciones de aptitud
for ii = 1:pob
    % Poblaciones iniciales aleatorias
    r = randi([0,l-1],1,n);
    for jj = 0:n-1
        posiciones_iniciales{ii}(jj+1,1) = jj;
        posiciones_iniciales{ii}(jj+1,2) = r(jj+1); 
    end
    
    % Posiciones del tablero
    tableros_iniciales{ii} = zeros(l,l); % Tablero (si en la casilla hay un 1, significa que en esa posición hay una reina)
    for k = 1:n
        tableros_iniciales{ii}(posiciones_iniciales{ii}(k,1)+1, posiciones_iniciales{ii}(k,2)+1) = 1;
    end

    % Checar los jaques de los tableros iniciales
    n_jaques = jaques(posiciones_iniciales{ii}(:,2), l, ii, tableros_iniciales);
    cont_jaques(ii) = n_jaques;
    
    % Prueba de aptitud
    puntuacion(ii) = f(cont_jaques(ii)); 
end
end

%% Función de comprobación de jaques
function n_jaques = jaques(ry, l, n_tablero, celda_tableros)
% Función que comprueba cuántas reinas hay en jaque dado un tablero
n_jaques = 0;
% Verticales
By = unique(ry);
n_repetidos_y = histc(ry, By); 
c_unicos_y = size(n_repetidos_y);
for jj = 1:c_unicos_y(2)
    if n_repetidos_y(jj) > 1
        combinatoria_y = factorial(n_repetidos_y(jj))/(factorial(n_repetidos_y(jj) - 2));
        n_jaques = n_jaques + combinatoria_y; % Se suman la cantidad de jaques posibles de la columna
    end
end

% Diagonales con pendiente negativa
for k = 0:l
% Diagonales con k positiva
diagonal1 = diag(celda_tableros{n_tablero},k);
n_reinas_d1 = numel(find(diagonal1==1));
if n_reinas_d1 == 2
   n_jaques = n_jaques + 1;
elseif n_reinas_d1 > 2
    combinatoria_d1 = factorial(n_reinas_d1)/(factorial(2)*(factorial(n_reinas_d1 - 2)));
    n_jaques = n_jaques + combinatoria_d1;
end
% Diagonales con k negativa
end
for k = -1:-1:-l
diagonal2 = diag(celda_tableros{n_tablero},k);
n_reinas_d2 = numel(find(diagonal2==1));
if n_reinas_d2 == 2
   n_jaques = n_jaques + 1;
elseif n_reinas_d2 > 2
    combinatoria_d2 = factorial(n_reinas_d2)/(factorial(2)*(factorial(n_reinas_d2 - 2)));
    n_jaques = n_jaques + combinatoria_d2; 
end
end

% Diagonales con pendiente positiva
for k = 0:l
% Diagonales con k positiva
diagonal1 = diag(fliplr(celda_tableros{n_tablero}),k);
n_reinas_d1 = numel(find(diagonal1==1));
if n_reinas_d1 == 2
   n_jaques = n_jaques + 1;
elseif n_reinas_d1 > 2
    combinatoria_d1 = factorial(n_reinas_d1)/(factorial(2)*(factorial(n_reinas_d1 - 2)));
    n_jaques = n_jaques + combinatoria_d1;
end
% Diagonales con k negativa
end
for k = -1:-1:-l
diagonal2 = diag(fliplr(celda_tableros{n_tablero}),k);
n_reinas_d2 = numel(find(diagonal2==1));
if n_reinas_d2 == 2
   n_jaques = n_jaques + 1;
elseif n_reinas_d2 > 2
    combinatoria_d2 = factorial(n_reinas_d2)/(factorial(2)*(factorial(n_reinas_d2 - 2)));
    n_jaques = n_jaques + combinatoria_d2; 
end

end
end

%% Función de gráfica del tablero
function tablero(l)
%   Función que crea un tablero de lado l
x1=0; x2=l;
y1=0; y2=l;
x = [x1, x2, x2, x1, x1];
y = [y1, y1, y2, y2, y1];
plot(x, y, 'k-', 'LineWidth', 1.5);
hold on
xlim([-1, l+1]);
ylim([-1, l+1]);
% Grid
for i = 1:l
plot([0,l],[i,i], 'k-', 'LineWidth', 1.5)
plot([i,i],[0,l],'k-', 'LineWidth', 1.5)
end
end

%% Función de gráfica de las reinas
function reinas(rx,ry,n,color)
% Función que grafica n cantidad de reinas en las posiciones en x y y dadas por los vectores rx y ry.
for n = 1:n
    g_reinas = plot(rx(n)+0.5,ry(n)+0.5, 'or');
    g_reinas.MarkerFaceColor = [color];
    g_reinas.MarkerSize = 6;
    g_reinas.MarkerEdgeColor = [color];
    % legend(reinas,'Reinas')
end
hold off
end
35g0bw71

35g0bw711#

也许使用随机函数查找所有总体并不能让您快速找到所有解。
您可以搜索列索引的所有可能排列n!,列索引的不同排列的示例是(1.5.8.6.3.7.2.4)和(1.6.8.3.7.4.2.5),在第一种情况下,它们对应于皇后(1,1)的位置;(二、五);(三、八);(四、六);(五、三);(六、七);(七、二);(8、4)。
或者使用像这样的优化解决方案,它不评估所有排列,但在满足某些条件时会进行跳跃。
此代码已通过SCILAB测试,但也可在MATLAB中运行:

n=8;
count=0;
Index_col=ones(1,n);
i = 2;
while (1 == 1)
    ck_var = 1;
    while(ck_var == 1);
        ck_var = 0;
        for j=1:i-1
            if ((Index_col(1,i) == Index_col(1,j)) || (abs(Index_col(1,i) - Index_col(1,j)) == i - j))
                if (Index_col(1,i) < n)
                    Index_col(1,i)=Index_col(1,i)+1;
                    ck_var = 1;
                else
                    ck_var = 2;
                end
                break;
            end
        end
    end
    if (i == n || ck_var == 2)
        if (ck_var == 0)
            //           print board
            for jc=1:n
                Board(jc,:)=zeros(1,n);
                Board(jc,Index_col(1,jc))=1;
            end
            Board
            //
            count=count+1;
        end
        while (Index_col(1,i) == n && i > 1)
            i=i-1;
        end
        if (Index_col(1,1) == n && i == 1)
            break;  
        else                
            Index_col(1,i)=Index_col(1,i)+1;
            Index_col(1,i+1:n) = 1;
            i = 2;
        end
    else
        i=i+1;
    end
end
count

相关问题