Skip to content
Snippets Groups Projects
Select Git revision
  • b798bfc4b0ff1efc83f60e1e1ce2445e653ae69a
  • master default protected
  • yassin.elhakoun-master-patch-35538
  • yassin.elhakoun-master-patch-06640
  • yassin.elhakoun-master-patch-05495
  • yassin.elhakoun-master-patch-26727
  • yassin.elhakoun-master-patch-76337
7 results

cours_8.md

Blame
  • Forked from algorithmique / cours
    549 commits behind the upstream repository.
    Orestis's avatar
    0550c9c3
    History
    cours_8.md 8.58 KiB
    title: "Backtracking et piles"
    date: "2021-11-17"
    patat:
      eval:
        tai:
          command: fish
          fragment: false
          replace: true
        ccc:
          command: fish
          fragment: false
          replace: true
      images:
        backend: auto

    L'algorithme du PPCM récursif (1/3)

    Exemple d'algorithme pour le calcul

    PPCM(36, 90):
    36  < 90  // 36  + 36
    72  < 90  // 72  + 36
    108 > 90  // 90  + 90
    108 < 180 // 108 + 36
    144 < 180 // 144 + 36
    180 = 180 // The End!

    . . .

    • On incrémente de 36 à gauche et de 90 à droite jusqu'à ce que les deux soient égaux.

    L'algorithme du PPCM récursif (2/3)

    Rappel de l'algorithme

    // on cherche i*m == j*m (i,j aussi petits que possibles)
    int ppcm(int m, int n) { 
        int mult_m = m, mult_n = n;
        while (mult_m != mult_n) {
            if (mult_m > mult_n) {
                mult_n += n;
            } else {
                mult_m += m;
            }
        }
        return mult_m;
    }

    . . .

    Écrire l'algorithme récursif du PPCM (matrix)

    L'algorithme du PPCM récursif (3/3)

    Pseudo-code

    • Algorithme du PPCM de deux nombres n et m
      • ppcm(mult_n,mult_m) = ppcm(mult_n + n, mult_m)
        si mult_n < mult_m (récursivité)
      • ppcm(mult_n,mult_m) = ppcm(mult_n, mult_m + m)
        si mult_n > mult_m (récursivité)
      • ppcm(mult_n,mult_m) = mult_n
        si mult_n = mult_m (condition d’arrêt)

    Problème des 8-reines

    • Placer 8 reines sur un échiquier de
      8×88 \times 8
      .
    • Sans que les reines ne puissent se menacer mutuellement (92 solutions).

    Conséquence

    • Deux reines ne partagent pas la même rangée, colonne, ou diagonale.
    • Donc chaque solution a une reine par colonne ou ligne.

    Généralisation

    • Placer
      NN
      reines sur un échiquier de
      N×NN \times N
      .
    • Exemple de backtracking (retour en arrière)
      \Rightarrow
      récursivité.

    Problème des 8-reines. Source: wikipedia

    Problème des 2-reines

    Le problème des 2 reines n'a pas de solution.

    Comment trouver les solutions?

    • On pose la première reine sur la première case disponible.
    • On rend inaccessibles toutes les cases menacées.
    • On pose la reine suivante sur la prochaine case non-menacée.
    • Jusqu'à ce qu'on puisse plus poser de reine.
    • On revient alors en arrière jusqu'au dernier coup où il y avait plus qu'une possibilité de poser une reine.
    • On recommence depuis là.

    . . .

    • Le jeu prend fin quand on a énuméré toutes les possibilités de poser les reines.

    Problème des 3-reines

    Le problème des 3 reines n'a pas de solution non plus.

    Problème des 4-reines

    Le problème des 4 reines a une solution.

    Problème des 4-reines, symétrie

    Le problème des 4 reines a une autre solution (symétrie horizontale).

    Problème des 5 reines

    Exercice: Trouver une solution au problème des 5 reines

    • Faire une capture d'écran / une photo de votre solution et la poster sur matrix.
    
    
    
    
    
    
    
    
    
    
    
    
    

    Quelques observations sur le problème

    • Une reine par colonne au plus.
    • On place les reines sur des colonnes successives.
    • On a pas besoin de "regarder en arrière" (on place "devant" uniquement).
    • Trois étapes:
      • On place une reine dans une case libre.
      • On met à jour le tableau.
      • Quand on a plus de cases libres on "revient dans le temps" ou c'est qu'on a réussi.

    Le code du problème des 8 reines (1/N)

    Quelle structure de données?

    . . .

    Une matrice de booléens fera l'affaire:

    bool board[n][n];

    Quelles fonctionalités?

    . . .

    // Pour chaque ligne placer la reine sur toutes les colonnes
    //    et compter les solutions
    void nbr_solutions(board, column, counter);
    // Copier un tableau dans un autre
    void copy(board_in, board_out);
    // Placer la reine à li, co et rendre inaccessible devant
    void placer_devant(board, li, co);

    Le code du problème des 8 reines (2/N)

    Le calcul du nombre de solutions

    // Calcule le nombre de solutions au problème des <n> reines
    nbr_solutions(board, column, count)
       // pour chaque ligne 
           // si la case libre
              // si column < n - 1
                  // copier board dans un "new" board, 
                  //   y poser une reine
                  //   et mettre à jour ce "new" board
                  // nbr_solutions(new_board, column+1, count)
              // sinon
                  // on a posé la n-ème et on a gagné
                  // count += 1

    Le code du problème des 8 reines (3/N)

    Le calcul du nombre de solutions

    // Placer une reine et mettre à jour
    placer_devant(board, ligne, colonne)
        // board est occupé à ligne/colonne
            // toutes les cases des colonnes
            //    suivantes sont mises à jour

    Le code du problème des 8 reines (4/N)

    Compris? Alors écrivez le code et postez le!

    . . .

    Le nombre de solutions

    \footnotesize

    // Calcule le nombre de solutions au problème des <n> reines
    void nb_sol(int n, bool board[n][n], int co, int *ptr_cpt) {
        for (int li = 0; li < n; li++) {
            if (board[li][co]) {
                if (co < n-1) {
                    bool new_board[n][n]; // alloué à chaque nouvelle tentative
                    copy(n, board, new_board);         
                    prises_devant(n, new_board, li, co);
                    nb_sol(n, new_board, co+1, ptr_cpt);
                } else {
                    *ptr_cpt = (*ptr_cpt)+1;
                }
            }
        }
    }

    Le code du problème des 8 reines (5/N)

    \footnotesize

    Placer devant

    // Retourne une copie du tableau <board> complété avec les positions
    // prises sur la droite droite par une reine placée en <board(li,co)>
    void prises_devant(int n, bool board[n][n], int li, int co) {
        board[li][co] = false; // position de la reine
        for (int j = 1; j < n-co; j++) {
            // horizontale et diagonales à droite de la reine
            if (j <= li) {
                board[li-j][co+j] = false;
            }
            board[li][co+j] = false;
            if (li+j < n) {
                board[li+j][co+j] = false;
            }
        }
    }

    Les piles (1/N)

    Qu'est-ce donc?

    • Structure de données abstraite...

    . . .

    • de type LIFO (Last in first out).

    Une pile où on ajoute A, puis B avant de les retirer. Source: Wikipedia

    Des exemples de la vraie vie

    . . .

    • Pile d'assiettes, de livres, ...
    • Adresses visitées par un navigateur web.
    • Les calculatrices du passé (en polonaise inverse).
    • Les boutons undo de vos éditeurs de texte (aka u dans vim).

    Les piles (2/N)

    Fonctionnalités

    . . .

    1. Empiler (push): ajouter un élément sur la pile.
    2. Dépiler (pop): retirer l'élément du sommet de la pile et le retrouner.
    3. Liste vide? (is_empty?).

    . . .

    1. Jeter un oeil (peek): retourner l'élément du sommet de la pile (sans le dépiler).
    2. Nombre d'éléments (length).

    Comment faire les 4,5 à partir de 1 à 3?

    . . .

    1. Dépiler l'élément, le copier, puis l'empiler à nouveau.
    2. Dépiler jusqu'à ce que la pile soit vide, puis empiler à nouveau.

    . . .

    Existe en deux goûts

    • Pile avec ou sans limite de capacité (à concurrence de la taille de la mémoire).

    Les piles (3/N)

    Implémentation

    • Jusqu'ici on n'a pas du tout parlé d'implémentation (d'où le nom de structure abstraite).
    • Pas de choix unique d'implémentation.

    Quelle structure de données allons nous utiliser?

    . . .

    Et oui vous avez deviné: un tableau!

    La structure: de quoi avons-nous besoin (pile de taille fixe)?

    . . .

    #define MAX_CAPACITY 500
    typedef struct _stack {
        int data[MAX_CAPACITY]; // les données
        int top;                // indice du sommet
    } stack;

    Les piles (4/N)

    Initialisation

    . . .

    void stack_init(stack *s) {
        s->top = -1;
    }

    Est vide?

    . . .

    bool stack_is_empty(stack s) {
        return s.top == -1;
    } 

    Empiler (ajouter un élément au sommet)

    . . .

    void stack_push(stack *s, int val) {
        s->top += 1;
        s->data[s->top] = val;
    }

    Les piles (5/N)

    Dépiler (enlever l'élément du sommet)

    . . .

    int stack_pop(stack *s) {
        s->top -= 1;
        return s->data[s->top+1];
    }

    Jeter un oeil (regarder le sommet)

    . . .

    int stack_peek(stack *s) {
        return s->data[s->top];
    }

    Voyez-vous des problèmes potentiels avec cette implémentation?

    . . .

    • Empiler avec une pile pleine.
    • Dépiler avec une pile vide.
    • Jeter un oeil au sommet d'une pile vide.