Aller au contenu

Problème des huit dames (3)⚓︎

Aux échecs, la dame est capable de se déplacer dans toutes les directions.

Mouvement des dames

Le problème des huit dames consiste à placer 8 dames sur un échiquier classique (8 lignes et 8 colonnes) de façon à ce qu'aucune d'entre elles ne soit menacée par une autre. Ainsi il n'y a qu'une seule dame par ligne, colonne ou diagonale. La figure ci-dessous illustre une disposition valide :

Disposition valide

On considère dans cet exercice des « échiquiers » de taille \(1 \le n \le 8\) variable.

Pour satisfaire au problème, une disposition sur un échiquier de taille \(n\) doit compter exactement \(n\) dames : une par colonne.

Le but de l'exercice est de déterminer l'ensemble des dispositions valides pour un échiquier de taille \(n\).

On représentera les dispositions des dames à l'aide de listes dont les indices représentent les colonnes de l'échiquier et les valeurs associées l'indice de la ligne sur laquelle est placée la dame de cette colonne.

Exemples

Dans le cas où la taille de l'échiquier est \(n = 5\), [0, 2, 4] est une disposition incomplète (elle compte moins de 5 valeurs). On peut néanmoins lire que l'on a placé une dame dans la colonne 3 (indice 2) et la ligne 5 (valeur 4).

Les dispositions valides seront stockées dans la liste dispositions_valides. Initialement vide, cette liste sera complétée au fil de la recherche.

On fournit une fonction est_valide qui prend en argument une disposition et la taille de l'échiquier et vérifie que cette disposition est valide ou non et renvoie le booléen correspondant.

Exemples

🐍 Console Python
>>> # disposition incomplète, n = 5
>>> est_valide([0, 2, 4], 5)
True
>>> # disposition complète, n = 5
>>> est_valide([2, 0, 4, 1, 3], 5)
False
>>> # disposition complète, n = 4
>>> est_valide([1, 3, 0, 2], 4)
True
Code de la fonction est_valide (pour information)
🐍 Script Python
def est_valide(disposition, n):
    # Vérifications des lignes
    nb_dames_placees = len(disposition)
    lignes_occupees = [False] * n
    for ligne in disposition:
        if lignes_occupees[ligne]:
            return False
        else:
            lignes_occupees[ligne] = True

    # Vérification des diagonales
    for j1 in range(nb_dames_placees - 1):
        for j2 in range(j1 + 1, nb_dames_placees):
            if abs(disposition[j2] - disposition[j1]) == (j2 - j1):
                return False
    return True

Cette fonction est déjà chargée en mémoire. Il est inutile de la recopier.

La fonction cherche_valides prend en argument une disposition en cours de construction disposition et la taille de l'échiquier n.

Cette fonction fonctionne de la façon suivante :

  • si le nombre de dame placées dans la disposition est égal à la taille de l'échiquier, cela signifie que l'on a déterminé un solution au problème : on la copie dans la liste dispositions_valides ;

  • sinon, il reste des dames à placer. On ajoute alors une nouvelle dame dans la disposition sur la première ligne. Si cette disposition est valide on continue l'exploration un appel récursif ;

  • quel que soit le statut de cette nouvelle disposition, valide ou invalide, une fois testée, on retire la dame ajoutée et on en place une nouvelle suivante ;

  • on étudie ainsi toutes les lignes entre 0 et \(n\) (exclu).

La figure ci-dessous illustre une partie de la recherche dans le cas d'un échiquier de taille \(n=4\).

graph TD
    R{ } --- A0((0))
    R --- A1{1}
    R --- A2((2))
    R --- A3((3))
    A1 --- B0((0))
    A1 --- B1((1))
    A1 --- B2((2))
    A1 --- B3{3}
    B3 --- C0{0}
    B3 --- C1((1))
    B3 --- C2((2))
    B3 --- C3((3))
    C0 --- D0((0))
    C0 --- D1((1))
    C0 --- D2{2}
    C0 --- D3((3))

    style A1 stroke:#0a0,stroke-width:4px
    style B3 stroke:#0a0,stroke-width:4px
    style C0 stroke:#0a0,stroke-width:4px
    style D2 stroke:#0a0,stroke-width:4px
    style B0 stroke:#a00,stroke-width:4px
    style B1 stroke:#a00,stroke-width:4px
    style B2 stroke:#a00,stroke-width:4px
    style C1 stroke:#a00,stroke-width:4px
    style C2 stroke:#a00,stroke-width:4px
    style C3 stroke:#a00,stroke-width:4px
    style D0 stroke:#a00,stroke-width:4px
    style D1 stroke:#a00,stroke-width:4px
    style D3 stroke:#a00,stroke-width:4px

Exemples

🐍 Console Python
>>> # Echiquier de dimension n = 3
>>> dispositions_valides = []
>>> cherche_valides([], 3)
>>> dispositions_valides
[]
🐍 Console Python
>>> # Echiquier de dimension n = 4
>>> dispositions_valides = []
>>> cherche_valides([], 4)
>>> dispositions_valides
[[1, 3, 0, 2], [2, 0, 3, 1]]
# Testsbksl-nl# Echiquier de dimension n = 3bksl-nldispositionspy-undvalides = []bksl-nlcherchepy-undvalides([], 3)bksl-nlassert dispositionspy-undvalides == []bksl-nlbksl-nl# Echiquier de dimension n = 4bksl-nldispositionspy-undvalides = []bksl-nlcherchepy-undvalides([], 4)bksl-nlassert sorted(dispositionspy-undvalides) == [[1, 3, 0, 2], [2, 0, 3, 1]]bksl-nlbksl-nlbksl-nl# Tests supplémentairesbksl-nlvalidepy-und1 = [[0]]bksl-nlvalidepy-und2 = []bksl-nlvalidepy-und5 = [bksl-nl [0, 2, 4, 1, 3],bksl-nl [0, 3, 1, 4, 2],bksl-nl [1, 3, 0, 2, 4],bksl-nl [1, 4, 2, 0, 3],bksl-nl [2, 0, 3, 1, 4],bksl-nl [2, 4, 1, 3, 0],bksl-nl [3, 0, 2, 4, 1],bksl-nl [3, 1, 4, 2, 0],bksl-nl [4, 1, 3, 0, 2],bksl-nl [4, 2, 0, 3, 1],bksl-nl]bksl-nlvalidepy-und6 = [bksl-nl [1, 3, 5, 0, 2, 4],bksl-nl [2, 5, 1, 4, 0, 3],bksl-nl [3, 0, 4, 1, 5, 2],bksl-nl [4, 2, 0, 5, 3, 1],bksl-nl]bksl-nlvalidepy-und7 = [bksl-nl [0, 2, 4, 6, 1, 3, 5],bksl-nl [0, 3, 6, 2, 5, 1, 4],bksl-nl [0, 4, 1, 5, 2, 6, 3],bksl-nl [0, 5, 3, 1, 6, 4, 2],bksl-nl [1, 3, 0, 6, 4, 2, 5],bksl-nl [1, 3, 5, 0, 2, 4, 6],bksl-nl [1, 4, 0, 3, 6, 2, 5],bksl-nl [1, 4, 2, 0, 6, 3, 5],bksl-nl [1, 4, 6, 3, 0, 2, 5],bksl-nl [1, 5, 2, 6, 3, 0, 4],bksl-nl [1, 6, 4, 2, 0, 5, 3],bksl-nl [2, 0, 5, 1, 4, 6, 3],bksl-nl [2, 0, 5, 3, 1, 6, 4],bksl-nl [2, 4, 6, 1, 3, 5, 0],bksl-nl [2, 5, 1, 4, 0, 3, 6],bksl-nl [2, 6, 1, 3, 5, 0, 4],bksl-nl [2, 6, 3, 0, 4, 1, 5],bksl-nl [3, 0, 2, 5, 1, 6, 4],bksl-nl [3, 0, 4, 1, 5, 2, 6],bksl-nl [3, 1, 6, 4, 2, 0, 5],bksl-nl [3, 5, 0, 2, 4, 6, 1],bksl-nl [3, 6, 2, 5, 1, 4, 0],bksl-nl [3, 6, 4, 1, 5, 0, 2],bksl-nl [4, 0, 3, 6, 2, 5, 1],bksl-nl [4, 0, 5, 3, 1, 6, 2],bksl-nl [4, 1, 5, 2, 6, 3, 0],bksl-nl [4, 2, 0, 5, 3, 1, 6],bksl-nl [4, 6, 1, 3, 5, 0, 2],bksl-nl [4, 6, 1, 5, 2, 0, 3],bksl-nl [5, 0, 2, 4, 6, 1, 3],bksl-nl [5, 1, 4, 0, 3, 6, 2],bksl-nl [5, 2, 0, 3, 6, 4, 1],bksl-nl [5, 2, 4, 6, 0, 3, 1],bksl-nl [5, 2, 6, 3, 0, 4, 1],bksl-nl [5, 3, 1, 6, 4, 2, 0],bksl-nl [5, 3, 6, 0, 2, 4, 1],bksl-nl [6, 1, 3, 5, 0, 2, 4],bksl-nl [6, 2, 5, 1, 4, 0, 3],bksl-nl [6, 3, 0, 4, 1, 5, 2],bksl-nl [6, 4, 2, 0, 5, 3, 1],bksl-nl]bksl-nlvalidepy-und8 = [bksl-nl [0, 4, 7, 5, 2, 6, 1, 3],bksl-nl [0, 5, 7, 2, 6, 3, 1, 4],bksl-nl [0, 6, 3, 5, 7, 1, 4, 2],bksl-nl [0, 6, 4, 7, 1, 3, 5, 2],bksl-nl [1, 3, 5, 7, 2, 0, 6, 4],bksl-nl [1, 4, 6, 0, 2, 7, 5, 3],bksl-nl [1, 4, 6, 3, 0, 7, 5, 2],bksl-nl [1, 5, 0, 6, 3, 7, 2, 4],bksl-nl [1, 5, 7, 2, 0, 3, 6, 4],bksl-nl [1, 6, 2, 5, 7, 4, 0, 3],bksl-nl [1, 6, 4, 7, 0, 3, 5, 2],bksl-nl [1, 7, 5, 0, 2, 4, 6, 3],bksl-nl [2, 0, 6, 4, 7, 1, 3, 5],bksl-nl [2, 4, 1, 7, 0, 6, 3, 5],bksl-nl [2, 4, 1, 7, 5, 3, 6, 0],bksl-nl [2, 4, 6, 0, 3, 1, 7, 5],bksl-nl [2, 4, 7, 3, 0, 6, 1, 5],bksl-nl [2, 5, 1, 4, 7, 0, 6, 3],bksl-nl [2, 5, 1, 6, 0, 3, 7, 4],bksl-nl [2, 5, 1, 6, 4, 0, 7, 3],bksl-nl [2, 5, 3, 0, 7, 4, 6, 1],bksl-nl [2, 5, 3, 1, 7, 4, 6, 0],bksl-nl [2, 5, 7, 0, 3, 6, 4, 1],bksl-nl [2, 5, 7, 0, 4, 6, 1, 3],bksl-nl [2, 5, 7, 1, 3, 0, 6, 4],bksl-nl [2, 6, 1, 7, 4, 0, 3, 5],bksl-nl [2, 6, 1, 7, 5, 3, 0, 4],bksl-nl [2, 7, 3, 6, 0, 5, 1, 4],bksl-nl [3, 0, 4, 7, 1, 6, 2, 5],bksl-nl [3, 0, 4, 7, 5, 2, 6, 1],bksl-nl [3, 1, 4, 7, 5, 0, 2, 6],bksl-nl [3, 1, 6, 2, 5, 7, 0, 4],bksl-nl [3, 1, 6, 2, 5, 7, 4, 0],bksl-nl [3, 1, 6, 4, 0, 7, 5, 2],bksl-nl [3, 1, 7, 4, 6, 0, 2, 5],bksl-nl [3, 1, 7, 5, 0, 2, 4, 6],bksl-nl [3, 5, 0, 4, 1, 7, 2, 6],bksl-nl [3, 5, 7, 1, 6, 0, 2, 4],bksl-nl [3, 5, 7, 2, 0, 6, 4, 1],bksl-nl [3, 6, 0, 7, 4, 1, 5, 2],bksl-nl [3, 6, 2, 7, 1, 4, 0, 5],bksl-nl [3, 6, 4, 1, 5, 0, 2, 7],bksl-nl [3, 6, 4, 2, 0, 5, 7, 1],bksl-nl [3, 7, 0, 2, 5, 1, 6, 4],bksl-nl [3, 7, 0, 4, 6, 1, 5, 2],bksl-nl [3, 7, 4, 2, 0, 6, 1, 5],bksl-nl [4, 0, 3, 5, 7, 1, 6, 2],bksl-nl [4, 0, 7, 3, 1, 6, 2, 5],bksl-nl [4, 0, 7, 5, 2, 6, 1, 3],bksl-nl [4, 1, 3, 5, 7, 2, 0, 6],bksl-nl [4, 1, 3, 6, 2, 7, 5, 0],bksl-nl [4, 1, 5, 0, 6, 3, 7, 2],bksl-nl [4, 1, 7, 0, 3, 6, 2, 5],bksl-nl [4, 2, 0, 5, 7, 1, 3, 6],bksl-nl [4, 2, 0, 6, 1, 7, 5, 3],bksl-nl [4, 2, 7, 3, 6, 0, 5, 1],bksl-nl [4, 6, 0, 2, 7, 5, 3, 1],bksl-nl [4, 6, 0, 3, 1, 7, 5, 2],bksl-nl [4, 6, 1, 3, 7, 0, 2, 5],bksl-nl [4, 6, 1, 5, 2, 0, 3, 7],bksl-nl [4, 6, 1, 5, 2, 0, 7, 3],bksl-nl [4, 6, 3, 0, 2, 7, 5, 1],bksl-nl [4, 7, 3, 0, 2, 5, 1, 6],bksl-nl [4, 7, 3, 0, 6, 1, 5, 2],bksl-nl [5, 0, 4, 1, 7, 2, 6, 3],bksl-nl [5, 1, 6, 0, 2, 4, 7, 3],bksl-nl [5, 1, 6, 0, 3, 7, 4, 2],bksl-nl [5, 2, 0, 6, 4, 7, 1, 3],bksl-nl [5, 2, 0, 7, 3, 1, 6, 4],bksl-nl [5, 2, 0, 7, 4, 1, 3, 6],bksl-nl [5, 2, 4, 6, 0, 3, 1, 7],bksl-nl [5, 2, 4, 7, 0, 3, 1, 6],bksl-nl [5, 2, 6, 1, 3, 7, 0, 4],bksl-nl [5, 2, 6, 1, 7, 4, 0, 3],bksl-nl [5, 2, 6, 3, 0, 7, 1, 4],bksl-nl [5, 3, 0, 4, 7, 1, 6, 2],bksl-nl [5, 3, 1, 7, 4, 6, 0, 2],bksl-nl [5, 3, 6, 0, 2, 4, 1, 7],bksl-nl [5, 3, 6, 0, 7, 1, 4, 2],bksl-nl [5, 7, 1, 3, 0, 6, 4, 2],bksl-nl [6, 0, 2, 7, 5, 3, 1, 4],bksl-nl [6, 1, 3, 0, 7, 4, 2, 5],bksl-nl [6, 1, 5, 2, 0, 3, 7, 4],bksl-nl [6, 2, 0, 5, 7, 4, 1, 3],bksl-nl [6, 2, 7, 1, 4, 0, 5, 3],bksl-nl [6, 3, 1, 4, 7, 0, 2, 5],bksl-nl [6, 3, 1, 7, 5, 0, 2, 4],bksl-nl [6, 4, 2, 0, 5, 7, 1, 3],bksl-nl [7, 1, 3, 0, 6, 4, 2, 5],bksl-nl [7, 1, 4, 2, 0, 6, 3, 5],bksl-nl [7, 2, 0, 5, 1, 4, 6, 3],bksl-nl [7, 3, 0, 2, 5, 1, 6, 4],bksl-nl]bksl-nlbksl-nlvalides = {1: validepy-und1, 2: validepy-und2, 5: validepy-und5, 6: validepy-und6, 7: validepy-und7, 8: validepy-und8}bksl-nlfor n in valides:bksl-nl dispositionspy-undvalides = []bksl-nl cherchepy-undvalides([], n)bksl-nl assert sorted(dispositionspy-undvalides) == valides[n]bksl-nlbksl-nl ∞/∞

#--- HDR ---#bksl-nldef estpy-undvalide(disposition, n):bksl-nl # Vérifications des lignesbksl-nl nbpy-unddamespy-undplacees = len(disposition)bksl-nl lignespy-undoccupees = [False] py-str nbksl-nl for ligne in disposition:bksl-nl if lignespy-undoccupees[ligne]:bksl-nl return Falsebksl-nl else:bksl-nl lignespy-undoccupees[ligne] = Truebksl-nlbksl-nl # Vérification des diagonalesbksl-nl for j1 in range(nbpy-unddamespy-undplacees - 1):bksl-nl for j2 in range(j1 + 1, nbpy-unddamespy-undplacees):bksl-nl if abs(disposition[j2] - disposition[j1]) == (j2 - j1):bksl-nl return Falsebksl-nl return Truebksl-nl#--- HDR ---#bksl-nldef cherchepy-undvalides(disposition, n):bksl-nl nbpy-unddamespy-undplacees = len(...)bksl-nlbksl-nl if nbpy-unddamespy-undplacees == ...: # on a placé toutes les damesbksl-nl ....append(....copy())bksl-nl else: # il reste des dames à placerbksl-nl for ligne in range(...):bksl-nl disposition.append(...)bksl-nl if estpy-undvalide(..., n):bksl-nl cherchepy-undvalides(..., ...)bksl-nl disposition.pop()bksl-nlbksl-nl# Testsbksl-nl# Echiquier de dimension n = 3bksl-nln = 3bksl-nldisposition = [None] py-str nbksl-nldispositionspy-undvalides = []bksl-nlcherchepy-undvalides(0)bksl-nlassert dispositionspy-undvalides == []bksl-nlbksl-nl# Echiquier de dimension n = 4bksl-nln = 4bksl-nldisposition = [None] py-str nbksl-nldispositionspy-undvalides = []bksl-nlcherchepy-undvalides(0)bksl-nlassert sorted(dispositionspy-undvalides) == [[1, 3, 0, 2], [2, 0, 3, 1]]bksl-nlbksl-nldef estpy-undvalide(disposition, n):bksl-nl # Vérifications des lignesbksl-nl nbpy-unddamespy-undplacees = len(disposition)bksl-nl lignespy-undoccupees = [False] py-str nbksl-nl for ligne in disposition:bksl-nl if lignespy-undoccupees[ligne]:bksl-nl return Falsebksl-nl else:bksl-nl lignespy-undoccupees[ligne] = Truebksl-nlbksl-nl # Vérification des diagonalesbksl-nl for j1 in range(nbpy-unddamespy-undplacees - 1):bksl-nl for j2 in range(j1 + 1, nbpy-unddamespy-undplacees):bksl-nl if abs(disposition[j2] - disposition[j1]) == (j2 - j1):bksl-nl return Falsebksl-nl return Truebksl-nlbksl-nlbksl-nldef cherchepy-undvalides(disposition, n):bksl-nl nbpy-unddamespy-undplacees = len(disposition)bksl-nlbksl-nl if nbpy-unddamespy-undplacees == n: # on a placé toutes les damesbksl-nl dispositionspy-undvalides.append(disposition.copy())bksl-nl else: # il reste des dames à placerbksl-nl for ligne in range(n):bksl-nl disposition.append(ligne)bksl-nl if estpy-undvalide(disposition, n):bksl-nl cherchepy-undvalides(disposition, n)bksl-nl disposition.pop()bksl-nlbksl-nlbksl-nl# Testsbksl-nl# Echiquier de dimension n = 3bksl-nldispositionspy-undvalides = []bksl-nlcherchepy-undvalides([], 3)bksl-nlassert dispositionspy-undvalides == []bksl-nlbksl-nl# Echiquier de dimension n = 4bksl-nldispositionspy-undvalides = []bksl-nlcherchepy-undvalides([], 4)bksl-nlassert sorted(dispositionspy-undvalides) == [[1, 3, 0, 2], [2, 0, 3, 1]]bksl-nlbksl-nl

A

Commentaires⚓︎

{{ IDE('exo_corr') }}

On explore les différentes possibilités avant de revenir en arrière dès que l'on observe que la disposition est invalide. Cette façon de faire classique est appelée backtracking.

Z

Retour en haut de la page