Le problème des 8 reines

Il est souvent utile de bien analyser un problème avant de commencer la programmation de sa solution. Par exemple celui des 8 reines : il s'agit de positionner sur un échiquier (8 X 8 donc) 8 reines afin qu'elles ne se menacent pas l'une l'autre.

Aux échecs, la reine est la pièce la plus mobile. Elle peut se déplacer vers toute case de la ligne où elle se trouve, comme toute case de sa colonne ou des 2 diagonales se croisant sur son emplacement.

Si l'on considère que la première reine peut se trouver sur une case parmi 64, la seconde une sur 63, etc. on se rend compte que le nombre total de combinaisons, ainsi que celui des tests à effectuer, peut être énorme. L'idée de départ est donc d'éviter au maximum l'explosion combinatoire. Pour cela on énumère des règles pour la résolution du problème :

- Chaque reine sera seule sur sa ligne. Toute ligne sera occupée.

- Chaque reine sera seule sur sa colonne. Toute colonne sera occupée.

- Les lignes et colonnes seront numérotées de 1 à 8 pour le traitement (les colonnes sont normalement codées de A à H). Si X1,Y1 et X2,Y2 sont les coordonnées sur l'échiquier de 2 reines, elles seront sur la même diagonale si (X1 - X2) = (Y1 - Y2) ou (X1 - X2) = -(Y1 - Y2). Toute combinaison où cette condition est vérifiée pour 2 reines quelconques sera invalidée.

La solution choisie est d'employer un tableau de 8 chiffres, donnant les numéros des colonnes restant valables après les tests réalisés jusque là. Successivement pour chaque ligne de 1 à 8, on cherchera les colonnes utilisables (d'après les reines déjà placées) et chaque fois que l'une d'elles sera trouvée, elle sera notée dans un tableau résultat temporaire et enlevée du tableau des colonnes qui sera passé à l'appel suivant (récursif) de la procédure qui cherche les solutions. C'est en dépassant la ligne 8 (par 9 appels imbriqués) qu'une solution complète sera trouvée. Du fait qu'il y a un appel différent pour chaque ligne et que les numéros des colonnes occupées sont progressivement supprimés du tableau des possibilités restantes, le seul test nécessaire est celui des 2 diagonales. L'élimination progressive des colonnes minimalise le nombre total de tests.

J'ai écrit le programme en Turbo Pascal il y a quelques années. La recherche des 92 solutions au problème durait 5 secondes sur un PC de base (8088 à 4,77 MHz), les solutions étant stockées dans un tableau pour éviter les entrées-sorties.

 

{----------------------------------------------
      Résolution du problème des 8 reines
      Turbo Pascal
 ----------------------------------------------}

program Reines;

type    Colonnes        = array[1..8] of byte;

var     Resul,
        Combinaison     : Colonnes;
        Solutions       : array[1..100] of Colonnes;
        NbSolu          : integer;

procedure Initialisation;

var     i               : integer;

begin
NbSolu := 0;
for i := 1 to 8 do
    Combinaison[i] := i;
end;

procedure Chercher(var Possib : Colonnes; Ligne : integer);

var     NbResul,Nombre,
        i,j             : integer;
        Delta1,Delta2   : integer;
        Ok              : boolean;
        Trav            : Colonnes;

begin
if Ligne < 9 then
    begin
    Nombre := 9 - Ligne;
    NbResul := Ligne - 1;
    for i := 1 to Nombre do
        begin
        Ok := true;
        j := 1;
        while Ok and (j <= NbResul) do
            begin
            Delta1 := Ligne - j;
            Delta2 := Possib[i] - Resul[j];
            if (Delta1 = Delta2) or (Delta1 = -Delta2) then
                Ok := false;
            j := j+1;
            end;
        if Ok then
            begin
            Resul[NbResul + 1] := Possib[i];
            Trav := Possib;
            for j := i+1 to Nombre do
                Trav[j-1] := Trav[j];
            Chercher(Trav,Ligne + 1);
            end;
        end;
    end
else
    begin
    NbSolu := NbSolu + 1;
    Solutions[NbSolu] := Resul;
    end;
end;

function Verification : boolean;

var     i,j,k,NbCorres  : integer;
        Ok              : boolean;

begin
Ok := true;
for i := 1 to NbSolu-1 do
    begin
    for j := i+1 to NbSolu do
        begin
        NbCorres := 0;
        for k := 1 to 8 do
            begin
            if Solutions[i,k] = Solutions[j,k] then
                NbCorres := NbCorres + 1;
            end;
        if NbCorres = 8 then
            Ok := false;           { Ne doit jamais arriver }
        end;
    end;

Verification := Ok;
end;

procedure EcrireSolutions;

var     Sortie          : text;
        i,j             : integer;

procedure Graphique;

const   Separ           = '---+---+---+---+---+---+---+---+';

type    Echiquier       = array[1..8,1..8] of char;

var     Init,Trav       : Echiquier;
        i,j,k           : integer;

begin
for i := 1 to 8 do
    begin
    for j := 1 to 8 do
        Init[i,j] := ' ';
    end;

writeln(Sortie);
for i := 1 to NbSolu do
    begin
    Trav := Init;
    for j := 1 to 8 do
        Trav[j,Solutions[i,j]] := '@';
    writeln(Sortie,Separ);
    for j := 8 downto 1 do
        begin
        for k := 1 to 8 do
            write(Sortie,' ',Trav[j,k],' !');
        writeln(Sortie);
        writeln(Sortie,Separ);
        end;
    writeln(Sortie);
    end;
end;

begin
assign(Sortie,'Reines.txt');
rewrite(Sortie);

writeln(Sortie,NbSolu,' solutions trouvees');
writeln(Sortie);

for i := 1 to NbSolu do
    begin
    for j := 1 to 8 do
        write(Sortie,chr(ord('A') + Solutions[i,j] - 1),j,' ');
    writeln(Sortie);
    end;

{  Graphique;  }

close(Sortie);
end;

begin
Initialisation;
Chercher(Combinaison,1);
if Verification then
    EcrireSolutions
else
    writeln('Anomalie detectee : 2 solutions identiques');
end.