Sudokus - Les pages perso du Crans
Transcription
Sudokus - Les pages perso du Crans
I Lycée Faidherbes – MPSI – Option Informatique Cinquième TP Caml Vendredis 21 et 28 mars 2008 Sudokus 8 7 4 3 9 6 1 5 1 3 1 5 5 4 2 3 7 6 8 9 7 2 Lors de ce cinquième TP, nous allons essayer de résoudre des grilles de sudoku. 1 Structure de données Il y a plusieurs façon de représenter la grille de sudoku dans la mémoire de l’ordinateur. Par exemple : une liste de listes d’entiers ( int list list), un tableau de tableaux d’entiers ( int array array), un tableau d’entiers ( int array)... J’ai choisi d’utiliser des tableaux de tableaux, vous pouvez utiliser la représentation qui vous conviendra le mieux, mais vous devrez écrire des fonction de conversion si vous souhaitez utiliser mes exemples. Nous allons également avoir besoin de représenter des cases vides. Une solution courante consiste à considérer que la valeur 0 correspond à une case vide. Cette méthode n’est cependant pas la plus élégante et Caml nous offre des mécanismes bien plus intéressant. Par exemple il nous permet de définir nos propres types, soit en combinant des types existants : ( ∗ ce type correspond a une l i s t e d ’ e n t i e r s ∗ ) type l i s t e d e n t i e r s = i n t l i s t ; ; soit en construisant des types à l’aide de mots clefs : ( ∗ l e s j o u r s de l a semaine ∗ ) type j o u r s e m a i n e = Lundi | Mardi | Mercredi . . . ; ; ( ∗ Notez que l e s mots c l e f s commencent t o u j o u r s par une majuscule ∗ ) on peut enfin combiner les deux : utiliser à la foi les mots clefs et des types existants : ( ∗ l e s nombre , e n t i e r s ou f l o t a n t s ∗ ) type nombre = E n t i e r of i n t | Reel of i n t , , En ce qui nous concerne, pour représenter l’état de nos cases, on peut utiliser le type suivant : type case = Vide | P l e i n e of i n t ; ; Mais Caml nous propose déjà un type presque identique, le type option. Ce type est défini de la façon suivante : type ’ a o p t i o n = None | Some of ’ a ; ; On peut donc utiliser une type int option pour représenter nos cases. On n’utilisera donc pas des tableaux ou des listes d’entiers mais des des tableaux ou des listes d’entiers optionnels ( int option array ou int option list). Dans mon cas, une grille de sudoku est donc représentée par un objet du type int option array array. Dorénavant, j’utiliserai sudoku pour désigner le type d’une grille, quelque soit la représentation choisie. ≫ Question 1 Écrire une fonction qui crée une grille vide. (type : init sudoku : unit −> sudoku) Malheureusement, Caml ne nous permet pas de fixer des règles sur la taille des tableaux : il nous faut une fonction qui vérifie qu’un élément du type sudoku est bien une grille de sudoku (une matrice carrée de taille 9) ≫ Question 2 Écrire une fonction qui vérifie la taille 1 4 7 2 5 8 3 6 9 de la grille et qui lève une exception si la grille est mauvaise. (type : check1: sudoku −> unit) ≫ Question 3 Écrire une fonction 2 5 8 3 6 9 4 7 1 3 6 9 4 7 1 5 8 2 4 7 1 5 8 2 6 9 3 5 8 2 6 9 3 7 1 4 6 9 3 7 1 4 8 2 5 7 1 4 8 2 5 9 3 6 8 2 5 9 3 6 1 4 7 9 3 6 1 4 7 2 5 8 case : sudoku −> int −> int −> int option qui renvoie le contenu d’une case en fonction de ses numéros de ligne et de colonne. ≫ Question 5 Pour remplir cette grille, on commence par la ligne 1 puis la 4 puis la 7 puis la 2 puis la 3.... l’ordre des lignes est en fait le suivant : 2 Règles du jeu 1, 4, 7, 2, 5, 8, 3, 6, 9 Je rappelle les règles du jeu : pour pouvoir insérer un chiffre dans une case, il faut que ce chiffre n’apparaisse pas déjà : 1. dans la colonne 2. dans la ligne 3. dans la sous-grille Construire une boucle qui affiche ces indices dans l’ordre. ≫ Question 4 Écrire une fonction : 4 Backtracking ≫ Question 6 À l’aide de cette boucle, écrivez une fonction qui remplis correctement une grille vide. verif case : sudoku −> int −> int −> int −> unit À présent nous allons résoudre des grilles avec contraintes, c’est à dire partiellement remplies. Pour cela on utilisera une méthode assez naı̈ve qui essayer toutes les solutions possible jusqu’à en trouver une bonne. on appelle cette méthode “backtracking” car on revient en arrière quand on tombe sur un cul-de-sac. On va tenter d’insérer un 1 dans la première case vide. Si c’est possible alors on passe à la case suivante, si ça ne l’est pas, ou si on a pas trouvé de solution avec 1 on essaie 2 puis 3, etc. Si on arrive à 9 sans trouver une solution, on retourne à la case précédente pour y essayer le nombre suivant etc. qui vérifie, étant donnés une grille, un numéro de ligne, un numéro de colonne et un chiffre, s’il est licite d’insérer le chiffre dans la case correspondant à la ligne et la colonne dans la grille. 3 Grille vide ? On va essayer dans cette partie de remplir une grille vierge. Pour cela, on va utiliser une solution canonique : On rempli la première ligne avec les chiffres dans l’ordre, puis la quatrième en les décalants une fois, la septième en les décalant à nouveau, on retourne à la seconde, etc. Cela devrait donner la grille suivante : ≫ Question 7 Écrivez une fonction qui résout une grille par backtracking. 2 I MPSI – Option Informatique Cinquième TP Caml Vendredis 21 et 28 mars 2008 Sudokus Un corrigé I Question 1 On utilisera la fonction Array.make : l e t i n i t s u d o k u () = l e t s = Array . make 9 [ | | ] i n f o r i = 0 to 8 do s . ( i ) <− Array . make 9 None done ; s ;; I Question 2 l e t check1 s = i f Array . l e n g t h s <> 9 then f a i l w i t h ” T a i l l e i n c o r r e c t . . . ” ; f o r i = 0 to 8 do i f Array . l e n g t h s . ( i ) <> 9 then f a i l w i t h ” T a i l l e i n c o r r e c t . . . ” done ;; I Question 5 f o r i = 0 to 2 do f o r j = 0 to 2 do p r i n t i n t ( ( i +3∗ j ) mod 9 + 1 ) done done ;; I Question 6 let remplis g x = let x = ref x in f o r i = 0 to 2 do f o r j = 0 to 2 do f o r k = 0 to 8 do g . ( ( i +3∗ j ) mod 9 ) . ( k) <− Some ( ( k + ! x ) mod 9 + 1) done ; x : = ! x+ 1 done done ;; I Question 3 l e t case s l c = s . ( l ) . ( c ) ;; I Question 7 J’utilise deux exceptions spéciales, une pour indiquer une erreur (pas de solution) et une pour interrompre le calcul en cas de réussite. exception SolutionTrouvee ; ; exception PasDeSolution ; ; I Question 4 ( ∗ v e r i f i e r s i v e s t l i c i t e dans l a case l i g n e l colonne c de l a g r i l l e s ∗) let verif case s l c v = let r e s u l t = r ef true in (∗ v e r i f i e r l a l i g n e ∗) f o r i = 0 to 8 do i f i<>c && s . ( l ) . ( i ) = Some v then r e s u l t : = f a l s e done ; ( ∗ v e r i f i e r l a colonne ∗ ) f o r i = 0 to 8 do i f i<>l && s . ( i ) . ( c ) = Some v then r e s u l t : = f a l s e done ; ( ∗ v e r i f i e r l e cadran ∗ ) f o r i = l − ( l mod 3 ) to l − ( l mod 3 ) + 2 do f o r j = c − ( c mod 3 ) to c − ( c mod 3 ) + 2 do i f ( not ( i = l && j = c )) && s . ( i ) . ( j ) = Some v then r e s u l t : = f a l s e ; done done ; ! result ;; l e t b a c k t r a c k i n g su = l e t rec b a c k t r a c k i n g a u x su n = i f n >80 then r a i s e SolutionTrouvee else l e t l = n / 9 in (∗ l i g n e ∗) l e t c = n mod 9 i n ( ∗ colonne ∗ ) i f su . ( l ) . ( c) <> None then b a c k t r a c k i n g a u x su ( n+1) else f o r i = 1 to 9 do i f v e r i f c a s e su l c i then begin su . ( l ) . ( c) <− Some i ; b a c k t r a c k i n g a u x su ( n+1); su . ( l ) . ( c) <− None end done in l e t temp = copy sudoku su i n try b a c k t r a c k i n g a u x temp 0 ; r a i s e PasDeSolution with SolutionTrouvee −> p r i n t s u d o k u temp ; temp ;;