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
;;