Comment presser un citron (première partie)

Ainsi donc, cette première partie détaillera la méthode de base utilisée pour résoudre un sudoku en une seule requête SQL.  Et en passant, pour ceux que la petite histoire intéresse, vous trouverez ici un excellent article sur l’évolution du sudoku.

Pour nous faciliter la tâche, nous utiliserons des tables de type MyISAM pour commencer.  Dans le prochain article, nous ferons aussi en sorte que notre méthode ne retourne qu’une seule grille valide même dans le cas où plusieurs solutions existeraient.  Rappelons que notre objectif ultime est d’optimiser une base de données dans le but bien précis de solutionner une grille valide dans des délais raisonnables.  Mais pour cet article, nous nous contenterons de verser dans la facilité!

Si l’envie vous prenait de vous salir les mains, vous aurez besoin d’un langage de programmation (idéalement Smalltalk comme j’utilise Pharo mais Squeak ou n’importe quel autre environnement Smalltalk fera l’affaire!) pour générer vous-même certaines données.  Si ce qui vous intéresse c’est seulement le résultat, n’importe quel client MySQL pourrait faire l’affaire comme je vous fournirai tous les scripts SQL nécessaires.

Ainsi, pour la suite de cet article, vous aurez besoin d’un client MySQL.

Bien que ce qui suit puisse se faire à l’aide de vos outils favoris, je vous suggère l’invite de commande et le client MySQL par souci de simplicité et de rapidité!

1. Création de la base de données

Il nous faut tout d’abord créer la base de données en exécutant:

CREATE DATABASE sudoku;

2. Création de la table de positions

La solution (et la représentation des données que j’ai envisagée) pour résoudre ce problème est triviale : il s’agit de créer toutes les permutations possibles pour une ligne et de toutes les insérer dans une table que nous joindrons ensuite 9 fois sur elle-même de façon à créer le puzzle complet.  Évidemment,  nous devrons aussi ajouter toutes les règles nécessaires (dans une vue) pour que la grille soit valide ainsi que les valeurs de départ connues du sudoku.

Ainsi donc, comme la grille standard a 9 colonnes, il a fallu créer 9! (9 factorielle = 362880 = 9*8*7*6*5*4*3*2*1) lignes que nous avons insérées dans cette table.  Chaque enregistrement de la table positions représente une ligne de sudoku, constituée de ses 9 colonnes (de là les identifiants c1, c2, c3, c4, etc).

USE sudoku;

CREATE TABLE positions (
c1 TINYINT UNSIGNED NOT NULL,
c2 TINYINT UNSIGNED NOT NULL,
c3 TINYINT UNSIGNED NOT NULL,
c4 TINYINT UNSIGNED NOT NULL,
c5 TINYINT UNSIGNED NOT NULL,
c6 TINYINT UNSIGNED NOT NULL,
c7 TINYINT UNSIGNED NOT NULL,
c8 TINYINT UNSIGNED NOT NULL,
c9 TINYINT UNSIGNED NOT NULL
) ENGINE=MYISAM;

Vous remarquerez l’absence d’index. Pour accélérer les choses, nous définirons les index seulement une fois la table remplie!  Les index seront essentiels dans notre cas, en particulier pour ce qui nous attend dans le prochain article!

3. Générer les données

J’ai créé les requêtes pour les INSERT dans la table positions à l’aide d’un petit script exécuté dans un workspace Pharo Smalltalk.

Voici le script utilisé :


| file |
file := StandardFileStream forceNewFileNamed: 'positions.sql'.
(1 to: 9) permutationsDo: [:e | file
nextPutAll: 'INSERT INTO positions(c1,c2,c3,c4,c5,c6,c7,c8,c9) VALUES(';
nextPutAll: (e at: 1) printString;nextPut: $, ;
nextPutAll: (e at: 2) printString;nextPut: $, ;
nextPutAll: (e at: 3) printString;nextPut: $, ;
nextPutAll: (e at: 4) printString;nextPut: $, ;
nextPutAll: (e at: 5) printString;nextPut: $, ;
nextPutAll: (e at: 6) printString;nextPut: $, ;
nextPutAll: (e at: 7) printString;nextPut: $, ;
nextPutAll: (e at: 8) printString;nextPut: $, ;
nextPutAll: (e at: 9) printString;nextPutAll: ');';cr].
file close.

Pour les plus aventuriers d’entre vous, je suggère d’étudier le contenu du fichier positions.sql afin de pouvoir générer l’équivalent à l’aide de votre langage de programmation favori! Pour les autres, j’ai déjà fait le travail et vous trouverez le fichier ici.

L’exécution de ce script dans un workspace créera un fichier nommé positions.sql  contenant toutes les requêtes de INSERT servant à remplir la table positions.

ATTENTION: la prochaine étape prend quelques minutes!

Pour insérer les données dans la table positions, ouvrez un invite de commande et exécutez ceci :

4. Générer les contraintes

Les contraintes de la requête servant à solutionner la grille sudoku ont aussi été générées à l’aide d’un script exécuté dans un workspace avec Pharo. Ce script élimine toute les clauses superflues ou dupliquées de façon à garder leur nombre au strict minimum en prenant bien soin de s’assurer d’avoir une grille de sudoku valide. Plus précisément, ce script veille à :

1) éliminer les clauses dupliquées (par exemple, nul besoin de spécifier que (r1.c3 <> r2.c3) et que (r2.c3 <> r1.c3), c’est la même chose!;

2) éliminer tous les clauses reliées aux lignes comme nous savons que toutes les lignes de la table position sont valides comme nous les avons toutes générées.

| s1 s2 s3 |
s1 := Set new.
1 to: 9 do: [:r |
1 to: 9 do: [:c |    | cell |
cell := 'r', r printString, '.c', c printString.
1 to: 9 do: [:r2 |         | compCell |
compCell := 'r', r2 printString, '.c', c printString.
compCell ~= cell
ifTrue: [    cell < compCell
ifTrue: [s1 add: (cell, ' <> ', compCell)]
ifFalse: [s1 add: (compCell, ' <> ', cell)].
]
]
]
].
Transcript cr;show: '-- Unique in column';cr.
s1 asSortedCollection do: [:e | Transcript show: '(', e, ') AND ';cr].

s2 := Set new.
1 to: 9 do: [:r |
1 to: 9 do: [:c |    | cell deltaR deltaC |
deltaR := ((r-1) // 3) * 3.
deltaC := ((c-1) // 3) * 3.
1 to: 3 do: [:r2 |
1 to: 3 do: [:c2 |    | compCell |
cell := 'r', r printString, '.c', c printString.
compCell := 'r', (r2 + deltaR) printString, '.c', (c2 + deltaC) printString.
(compCell ~= cell and: [(r2 + deltaR) ~= r])
ifTrue:[    cell < compCell
ifTrue: [s2 add: (cell, ' <> ', compCell)]
ifFalse: [s2 add: (compCell, ' <> ', cell)].
]
]
]
]
].
Transcript cr;show: '-- Unique within 3x3 square';cr.
s3 := Set new.
s2 do: [:each | (s1 includes: each) ifFalse: [s3 add: each]].
s3 asSortedCollection do: [:e | Transcript show: '(', e, ') AND ';cr].

Nous utiliserons le résultat affiché dans le Transcript pour créer notre vue!

Note: il vous faudra préalablement changer une méthode dans Pharo pour être en mesure d’afficher la totalité des contraintes.

5. Créer les index

Pour accélérer la recherche, il nous faut des index! Il est temps de les générer!

ATTENTION: la prochaine étape prend plusieurs minutes!

USE sudoku;

CREATE INDEX i12 ON positions(c1, c2); CREATE INDEX i13 ON positions(c1, c3);
CREATE INDEX i14 ON positions(c1, c4); CREATE INDEX i15 ON positions(c1, c5);
CREATE INDEX i16 ON positions(c1, c6); CREATE INDEX i17 ON positions(c1, c7);
CREATE INDEX i18 ON positions(c1, c8); CREATE INDEX i19 ON positions(c1, c9);
CREATE INDEX i23 ON positions(c2, c3); CREATE INDEX i24 ON positions(c2, c4);
CREATE INDEX i25 ON positions(c2, c5); CREATE INDEX i26 ON positions(c2, c6);
CREATE INDEX i27 ON positions(c2, c7); CREATE INDEX i28 ON positions(c2, c8);
CREATE INDEX i29 ON positions(c2, c9); CREATE INDEX i34 ON positions(c3, c4);
CREATE INDEX i35 ON positions(c3, c5); CREATE INDEX i36 ON positions(c3, c6);
CREATE INDEX i37 ON positions(c3, c7); CREATE INDEX i38 ON positions(c3, c8);
CREATE INDEX i39 ON positions(c3, c9); CREATE INDEX i45 ON positions(c4, c5);
CREATE INDEX i46 ON positions(c4, c6); CREATE INDEX i47 ON positions(c4, c7);
CREATE INDEX i48 ON positions(c4, c8); CREATE INDEX i49 ON positions(c4, c9);
CREATE INDEX i56 ON positions(c5, c6); CREATE INDEX i57 ON positions(c5, c7);
CREATE INDEX i58 ON positions(c5, c8); CREATE INDEX i59 ON positions(c5, c9);
CREATE INDEX i67 ON positions(c6, c7); CREATE INDEX i68 ON positions(c6, c8);
CREATE INDEX i69 ON positions(c6, c9); CREATE INDEX i78 ON positions(c7, c8);
CREATE INDEX i79 ON positions(c7, c9); CREATE INDEX i89 ON positions(c8, c9);
CREATE INDEX i9 ON positions(c9);

6. Créer une vue pour chercher les sudokus

C’est ici que les contraintes générées précédemment à l’aide de Pharo deviennent utiles!  Observez le nombre effarant de conditions dans la clause WHERE de la requête définissant la vue!

USE sudoku;

CREATE VIEW sudoku_rows_view AS
SELECT
r1.c1 as r1c1, r1.c2 as r1c2, r1.c3 as r1c3, r1.c4 as r1c4, r1.c5 as r1c5, r1.c6 as r1c6, r1.c7 as r1c7, r1.c8 as r1c8, r1.c9 as r1c9, r2.c1 as r2c1,
r2.c2 as r2c2, r2.c3 as r2c3, r2.c4 as r2c4, r2.c5 as r2c5, r2.c6 as r2c6, r2.c7 as r2c7, r2.c8 as r2c8, r2.c9 as r2c9, r3.c1 as r3c1, r3.c2 as r3c2,
r3.c3 as r3c3, r3.c4 as r3c4, r3.c5 as r3c5, r3.c6 as r3c6, r3.c7 as r3c7, r3.c8 as r3c8, r3.c9 as r3c9, r4.c1 as r4c1, r4.c2 as r4c2, r4.c3 as r4c3,
r4.c4 as r4c4, r4.c5 as r4c5, r4.c6 as r4c6, r4.c7 as r4c7, r4.c8 as r4c8, r4.c9 as r4c9, r5.c1 as r5c1, r5.c2 as r5c2, r5.c3 as r5c3, r5.c4 as r5c4,
r5.c5 as r5c5, r5.c6 as r5c6, r5.c7 as r5c7, r5.c8 as r5c8, r5.c9 as r5c9, r6.c1 as r6c1, r6.c2 as r6c2, r6.c3 as r6c3, r6.c4 as r6c4, r6.c5 as r6c5,
r6.c6 as r6c6, r6.c7 as r6c7, r6.c8 as r6c8, r6.c9 as r6c9, r7.c1 as r7c1, r7.c2 as r7c2, r7.c3 as r7c3, r7.c4 as r7c4, r7.c5 as r7c5, r7.c6 as r7c6,
r7.c7 as r7c7, r7.c8 as r7c8, r7.c9 as r7c9, r8.c1 as r8c1, r8.c2 as r8c2, r8.c3 as r8c3, r8.c4 as r8c4, r8.c5 as r8c5, r8.c6 as r8c6, r8.c7 as r8c7,
r8.c8 as r8c8, r8.c9 as r8c9, r9.c1 as r9c1, r9.c2 as r9c2, r9.c3 as r9c3, r9.c4 as r9c4, r9.c5 as r9c5, r9.c6 as r9c6, r9.c7 as r9c7, r9.c8 as r9c8,
r9.c9 as r9c9

FROM
positions r1, positions r2, positions r3,
positions r4, positions r5, positions r6,
positions r7, positions r8, positions r9

WHERE
-- Nombres uniques dans chaque colonne
(r1.c1 <> r2.c1) AND (r1.c1 <> r3.c1) AND (r1.c1 <> r4.c1) AND (r1.c1 <> r5.c1) AND (r1.c1 <> r6.c1) AND (r1.c1 <> r7.c1) AND (r1.c1 <> r8.c1) AND (r1.c1 <> r9.c1) AND (r1.c2 <> r2.c2) AND (r1.c2 <> r3.c2) AND (r1.c2 <> r4.c2) AND (r1.c2 <> r5.c2) AND (r1.c2 <> r6.c2) AND (r1.c2 <> r7.c2) AND (r1.c2 <> r8.c2) AND (r1.c2 <> r9.c2) AND
(r1.c3 <> r2.c3) AND (r1.c3 <> r3.c3) AND (r1.c3 <> r4.c3) AND (r1.c3 <> r5.c3) AND (r1.c3 <> r6.c3) AND (r1.c3 <> r7.c3) AND (r1.c3 <> r8.c3) AND (r1.c3 <> r9.c3) AND (r1.c4 <> r2.c4) AND (r1.c4 <> r3.c4) AND (r1.c4 <> r4.c4) AND (r1.c4 <> r5.c4) AND (r1.c4 <> r6.c4) AND (r1.c4 <> r7.c4) AND (r1.c4 <> r8.c4) AND (r1.c4 <> r9.c4) AND
(r1.c5 <> r2.c5) AND (r1.c5 <> r3.c5) AND (r1.c5 <> r4.c5) AND (r1.c5 <> r5.c5) AND (r1.c5 <> r6.c5) AND (r1.c5 <> r7.c5) AND (r1.c5 <> r8.c5) AND (r1.c5 <> r9.c5) AND (r1.c6 <> r2.c6) AND (r1.c6 <> r3.c6) AND (r1.c6 <> r4.c6) AND (r1.c6 <> r5.c6) AND (r1.c6 <> r6.c6) AND (r1.c6 <> r7.c6) AND (r1.c6 <> r8.c6) AND (r1.c6 <> r9.c6) AND
(r1.c7 <> r2.c7) AND (r1.c7 <> r3.c7) AND (r1.c7 <> r4.c7) AND (r1.c7 <> r5.c7) AND (r1.c7 <> r6.c7) AND (r1.c7 <> r7.c7) AND (r1.c7 <> r8.c7) AND (r1.c7 <> r9.c7) AND (r1.c8 <> r2.c8) AND (r1.c8 <> r3.c8) AND (r1.c8 <> r4.c8) AND (r1.c8 <> r5.c8) AND (r1.c8 <> r6.c8) AND (r1.c8 <> r7.c8) AND (r1.c8 <> r8.c8) AND (r1.c8 <> r9.c8) AND
(r1.c9 <> r2.c9) AND (r1.c9 <> r3.c9) AND (r1.c9 <> r4.c9) AND (r1.c9 <> r5.c9) AND (r1.c9 <> r6.c9) AND (r1.c9 <> r7.c9) AND (r1.c9 <> r8.c9) AND (r1.c9 <> r9.c9) AND (r2.c1 <> r3.c1) AND (r2.c1 <> r4.c1) AND (r2.c1 <> r5.c1) AND (r2.c1 <> r6.c1) AND (r2.c1 <> r7.c1) AND (r2.c1 <> r8.c1) AND (r2.c1 <> r9.c1) AND (r2.c2 <> r3.c2) AND
(r2.c2 <> r4.c2) AND (r2.c2 <> r5.c2) AND (r2.c2 <> r6.c2) AND (r2.c2 <> r7.c2) AND (r2.c2 <> r8.c2) AND (r2.c2 <> r9.c2) AND (r2.c3 <> r3.c3) AND (r2.c3 <> r4.c3) AND (r2.c3 <> r5.c3) AND (r2.c3 <> r6.c3) AND (r2.c3 <> r7.c3) AND (r2.c3 <> r8.c3) AND (r2.c3 <> r9.c3) AND (r2.c4 <> r3.c4) AND (r2.c4 <> r4.c4) AND (r2.c4 <> r5.c4) AND
(r2.c4 <> r6.c4) AND (r2.c4 <> r7.c4) AND (r2.c4 <> r8.c4) AND (r2.c4 <> r9.c4) AND (r2.c5 <> r3.c5) AND (r2.c5 <> r4.c5) AND (r2.c5 <> r5.c5) AND (r2.c5 <> r6.c5) AND (r2.c5 <> r7.c5) AND (r2.c5 <> r8.c5) AND (r2.c5 <> r9.c5) AND (r2.c6 <> r3.c6) AND (r2.c6 <> r4.c6) AND (r2.c6 <> r5.c6) AND (r2.c6 <> r6.c6) AND (r2.c6 <> r7.c6) AND
(r2.c6 <> r8.c6) AND (r2.c6 <> r9.c6) AND (r2.c7 <> r3.c7) AND (r2.c7 <> r4.c7) AND (r2.c7 <> r5.c7) AND (r2.c7 <> r6.c7) AND (r2.c7 <> r7.c7) AND (r2.c7 <> r8.c7) AND (r2.c7 <> r9.c7) AND (r2.c8 <> r3.c8) AND (r2.c8 <> r4.c8) AND (r2.c8 <> r5.c8) AND (r2.c8 <> r6.c8) AND (r2.c8 <> r7.c8) AND (r2.c8 <> r8.c8) AND (r2.c8 <> r9.c8) AND
(r2.c9 <> r3.c9) AND (r2.c9 <> r4.c9) AND (r2.c9 <> r5.c9) AND (r2.c9 <> r6.c9) AND (r2.c9 <> r7.c9) AND (r2.c9 <> r8.c9) AND (r2.c9 <> r9.c9) AND (r3.c1 <> r4.c1) AND (r3.c1 <> r5.c1) AND (r3.c1 <> r6.c1) AND (r3.c1 <> r7.c1) AND (r3.c1 <> r8.c1) AND (r3.c1 <> r9.c1) AND (r3.c2 <> r4.c2) AND (r3.c2 <> r5.c2) AND (r3.c2 <> r6.c2) AND
(r3.c2 <> r7.c2) AND (r3.c2 <> r8.c2) AND (r3.c2 <> r9.c2) AND (r3.c3 <> r4.c3) AND (r3.c3 <> r5.c3) AND (r3.c3 <> r6.c3) AND (r3.c3 <> r7.c3) AND (r3.c3 <> r8.c3) AND (r3.c3 <> r9.c3) AND (r3.c4 <> r4.c4) AND (r3.c4 <> r5.c4) AND (r3.c4 <> r6.c4) AND (r3.c4 <> r7.c4) AND (r3.c4 <> r8.c4) AND (r3.c4 <> r9.c4) AND (r3.c5 <> r4.c5) AND
(r3.c5 <> r5.c5) AND (r3.c5 <> r6.c5) AND (r3.c5 <> r7.c5) AND (r3.c5 <> r8.c5) AND (r3.c5 <> r9.c5) AND (r3.c6 <> r4.c6) AND (r3.c6 <> r5.c6) AND (r3.c6 <> r6.c6) AND (r3.c6 <> r7.c6) AND (r3.c6 <> r8.c6) AND (r3.c6 <> r9.c6) AND (r3.c7 <> r4.c7) AND (r3.c7 <> r5.c7) AND (r3.c7 <> r6.c7) AND (r3.c7 <> r7.c7) AND (r3.c7 <> r8.c7) AND
(r3.c7 <> r9.c7) AND (r3.c8 <> r4.c8) AND (r3.c8 <> r5.c8) AND (r3.c8 <> r6.c8) AND (r3.c8 <> r7.c8) AND (r3.c8 <> r8.c8) AND (r3.c8 <> r9.c8) AND (r3.c9 <> r4.c9) AND (r3.c9 <> r5.c9) AND (r3.c9 <> r6.c9) AND (r3.c9 <> r7.c9) AND (r3.c9 <> r8.c9) AND (r3.c9 <> r9.c9) AND (r4.c1 <> r5.c1) AND (r4.c1 <> r6.c1) AND (r4.c1 <> r7.c1) AND
(r4.c1 <> r8.c1) AND (r4.c1 <> r9.c1) AND (r4.c2 <> r5.c2) AND (r4.c2 <> r6.c2) AND (r4.c2 <> r7.c2) AND (r4.c2 <> r8.c2) AND (r4.c2 <> r9.c2) AND (r4.c3 <> r5.c3) AND (r4.c3 <> r6.c3) AND (r4.c3 <> r7.c3) AND (r4.c3 <> r8.c3) AND (r4.c3 <> r9.c3) AND (r4.c4 <> r5.c4) AND (r4.c4 <> r6.c4) AND (r4.c4 <> r7.c4) AND (r4.c4 <> r8.c4) AND
(r4.c4 <> r9.c4) AND (r4.c5 <> r5.c5) AND (r4.c5 <> r6.c5) AND (r4.c5 <> r7.c5) AND (r4.c5 <> r8.c5) AND (r4.c5 <> r9.c5) AND (r4.c6 <> r5.c6) AND (r4.c6 <> r6.c6) AND (r4.c6 <> r7.c6) AND (r4.c6 <> r8.c6) AND (r4.c6 <> r9.c6) AND (r4.c7 <> r5.c7) AND (r4.c7 <> r6.c7) AND (r4.c7 <> r7.c7) AND (r4.c7 <> r8.c7) AND (r4.c7 <> r9.c7) AND
(r4.c8 <> r5.c8) AND (r4.c8 <> r6.c8) AND (r4.c8 <> r7.c8) AND (r4.c8 <> r8.c8) AND (r4.c8 <> r9.c8) AND (r4.c9 <> r5.c9) AND (r4.c9 <> r6.c9) AND (r4.c9 <> r7.c9) AND (r4.c9 <> r8.c9) AND (r4.c9 <> r9.c9) AND (r5.c1 <> r6.c1) AND (r5.c1 <> r7.c1) AND (r5.c1 <> r8.c1) AND (r5.c1 <> r9.c1) AND (r5.c2 <> r6.c2) AND (r5.c2 <> r7.c2) AND
(r5.c2 <> r8.c2) AND (r5.c2 <> r9.c2) AND (r5.c3 <> r6.c3) AND (r5.c3 <> r7.c3) AND (r5.c3 <> r8.c3) AND (r5.c3 <> r9.c3) AND (r5.c4 <> r6.c4) AND (r5.c4 <> r7.c4) AND (r5.c4 <> r8.c4) AND (r5.c4 <> r9.c4) AND (r5.c5 <> r6.c5) AND (r5.c5 <> r7.c5) AND (r5.c5 <> r8.c5) AND (r5.c5 <> r9.c5) AND (r5.c6 <> r6.c6) AND (r5.c6 <> r7.c6) AND
(r5.c6 <> r8.c6) AND (r5.c6 <> r9.c6) AND (r5.c7 <> r6.c7) AND (r5.c7 <> r7.c7) AND (r5.c7 <> r8.c7) AND (r5.c7 <> r9.c7) AND (r5.c8 <> r6.c8) AND (r5.c8 <> r7.c8) AND (r5.c8 <> r8.c8) AND (r5.c8 <> r9.c8) AND (r5.c9 <> r6.c9) AND (r5.c9 <> r7.c9) AND (r5.c9 <> r8.c9) AND (r5.c9 <> r9.c9) AND (r6.c1 <> r7.c1) AND (r6.c1 <> r8.c1) AND
(r6.c1 <> r9.c1) AND (r6.c2 <> r7.c2) AND (r6.c2 <> r8.c2) AND (r6.c2 <> r9.c2) AND (r6.c3 <> r7.c3) AND (r6.c3 <> r8.c3) AND (r6.c3 <> r9.c3) AND (r6.c4 <> r7.c4) AND (r6.c4 <> r8.c4) AND (r6.c4 <> r9.c4) AND (r6.c5 <> r7.c5) AND (r6.c5 <> r8.c5) AND (r6.c5 <> r9.c5) AND (r6.c6 <> r7.c6) AND (r6.c6 <> r8.c6) AND (r6.c6 <> r9.c6) AND
(r6.c7 <> r7.c7) AND (r6.c7 <> r8.c7) AND (r6.c7 <> r9.c7) AND (r6.c8 <> r7.c8) AND (r6.c8 <> r8.c8) AND (r6.c8 <> r9.c8) AND (r6.c9 <> r7.c9) AND (r6.c9 <> r8.c9) AND (r6.c9 <> r9.c9) AND (r7.c1 <> r8.c1) AND (r7.c1 <> r9.c1) AND (r7.c2 <> r8.c2) AND (r7.c2 <> r9.c2) AND (r7.c3 <> r8.c3) AND (r7.c3 <> r9.c3) AND (r7.c4 <> r8.c4) AND
(r7.c4 <> r9.c4) AND (r7.c5 <> r8.c5) AND (r7.c5 <> r9.c5) AND (r7.c6 <> r8.c6) AND (r7.c6 <> r9.c6) AND (r7.c7 <> r8.c7) AND (r7.c7 <> r9.c7) AND (r7.c8 <> r8.c8) AND (r7.c8 <> r9.c8) AND (r7.c9 <> r8.c9) AND (r7.c9 <> r9.c9) AND (r8.c1 <> r9.c1) AND (r8.c2 <> r9.c2) AND (r8.c3 <> r9.c3) AND (r8.c4 <> r9.c4) AND (r8.c5 <> r9.c5) AND
(r8.c6 <> r9.c6) AND (r8.c7 <> r9.c7) AND (r8.c8 <> r9.c8) AND (r8.c9 <> r9.c9) AND
-- Nombres uniques dans chaque maison de 3x3
(r1.c1 <> r2.c2) AND (r1.c1 <> r2.c3) AND (r1.c1 <> r3.c2) AND (r1.c1 <> r3.c3) AND (r1.c2 <> r2.c1) AND (r1.c2 <> r2.c3) AND (r1.c2 <> r3.c1) AND (r1.c2 <> r3.c3) AND (r1.c3 <> r2.c1) AND (r1.c3 <> r2.c2) AND (r1.c3 <> r3.c1) AND (r1.c3 <> r3.c2) AND (r1.c4 <> r2.c5) AND (r1.c4 <> r2.c6) AND (r1.c4 <> r3.c5) AND (r1.c4 <> r3.c6) AND
(r1.c5 <> r2.c4) AND (r1.c5 <> r2.c6) AND (r1.c5 <> r3.c4) AND (r1.c5 <> r3.c6) AND (r1.c6 <> r2.c4) AND (r1.c6 <> r2.c5) AND (r1.c6 <> r3.c4) AND (r1.c6 <> r3.c5) AND (r1.c7 <> r2.c8) AND (r1.c7 <> r2.c9) AND (r1.c7 <> r3.c8) AND (r1.c7 <> r3.c9) AND (r1.c8 <> r2.c7) AND (r1.c8 <> r2.c9) AND (r1.c8 <> r3.c7) AND (r1.c8 <> r3.c9) AND
(r1.c9 <> r2.c7) AND (r1.c9 <> r2.c8) AND (r1.c9 <> r3.c7) AND (r1.c9 <> r3.c8) AND (r2.c1 <> r3.c2) AND (r2.c1 <> r3.c3) AND (r2.c2 <> r3.c1) AND (r2.c2 <> r3.c3) AND (r2.c3 <> r3.c1) AND (r2.c3 <> r3.c2) AND (r2.c4 <> r3.c5) AND (r2.c4 <> r3.c6) AND (r2.c5 <> r3.c4) AND (r2.c5 <> r3.c6) AND (r2.c6 <> r3.c4) AND (r2.c6 <> r3.c5) AND
(r2.c7 <> r3.c8) AND (r2.c7 <> r3.c9) AND (r2.c8 <> r3.c7) AND (r2.c8 <> r3.c9) AND (r2.c9 <> r3.c7) AND (r2.c9 <> r3.c8) AND (r4.c1 <> r5.c2) AND (r4.c1 <> r5.c3) AND (r4.c1 <> r6.c2) AND (r4.c1 <> r6.c3) AND (r4.c2 <> r5.c1) AND (r4.c2 <> r5.c3) AND (r4.c2 <> r6.c1) AND (r4.c2 <> r6.c3) AND (r4.c3 <> r5.c1) AND (r4.c3 <> r5.c2) AND
(r4.c3 <> r6.c1) AND (r4.c3 <> r6.c2) AND (r4.c4 <> r5.c5) AND (r4.c4 <> r5.c6) AND (r4.c4 <> r6.c5) AND (r4.c4 <> r6.c6) AND (r4.c5 <> r5.c4) AND (r4.c5 <> r5.c6) AND (r4.c5 <> r6.c4) AND (r4.c5 <> r6.c6) AND (r4.c6 <> r5.c4) AND (r4.c6 <> r5.c5) AND (r4.c6 <> r6.c4) AND (r4.c6 <> r6.c5) AND (r4.c7 <> r5.c8) AND (r4.c7 <> r5.c9) AND
(r4.c7 <> r6.c8) AND (r4.c7 <> r6.c9) AND (r4.c8 <> r5.c7) AND (r4.c8 <> r5.c9) AND (r4.c8 <> r6.c7) AND (r4.c8 <> r6.c9) AND (r4.c9 <> r5.c7) AND (r4.c9 <> r5.c8) AND (r4.c9 <> r6.c7) AND (r4.c9 <> r6.c8) AND (r5.c1 <> r6.c2) AND (r5.c1 <> r6.c3) AND (r5.c2 <> r6.c1) AND (r5.c2 <> r6.c3) AND (r5.c3 <> r6.c1) AND (r5.c3 <> r6.c2) AND
(r5.c4 <> r6.c5) AND (r5.c4 <> r6.c6) AND (r5.c5 <> r6.c4) AND (r5.c5 <> r6.c6) AND (r5.c6 <> r6.c4) AND (r5.c6 <> r6.c5) AND (r5.c7 <> r6.c8) AND (r5.c7 <> r6.c9) AND (r5.c8 <> r6.c7) AND (r5.c8 <> r6.c9) AND (r5.c9 <> r6.c7) AND (r5.c9 <> r6.c8) AND (r7.c1 <> r8.c2) AND (r7.c1 <> r8.c3) AND (r7.c1 <> r9.c2) AND (r7.c1 <> r9.c3) AND
(r7.c2 <> r8.c1) AND (r7.c2 <> r8.c3) AND (r7.c2 <> r9.c1) AND (r7.c2 <> r9.c3) AND (r7.c3 <> r8.c1) AND (r7.c3 <> r8.c2) AND (r7.c3 <> r9.c1) AND (r7.c3 <> r9.c2) AND (r7.c4 <> r8.c5) AND (r7.c4 <> r8.c6) AND (r7.c4 <> r9.c5) AND (r7.c4 <> r9.c6) AND (r7.c5 <> r8.c4) AND (r7.c5 <> r8.c6) AND (r7.c5 <> r9.c4) AND (r7.c5 <> r9.c6) AND
(r7.c6 <> r8.c4) AND (r7.c6 <> r8.c5) AND (r7.c6 <> r9.c4) AND (r7.c6 <> r9.c5) AND (r7.c7 <> r8.c8) AND (r7.c7 <> r8.c9) AND (r7.c7 <> r9.c8) AND (r7.c7 <> r9.c9) AND (r7.c8 <> r8.c7) AND (r7.c8 <> r8.c9) AND (r7.c8 <> r9.c7) AND (r7.c8 <> r9.c9) AND (r7.c9 <> r8.c7) AND (r7.c9 <> r8.c8) AND (r7.c9 <> r9.c7) AND (r7.c9 <> r9.c8) AND
(r8.c1 <> r9.c2) AND (r8.c1 <> r9.c3) AND (r8.c2 <> r9.c1) AND (r8.c2 <> r9.c3) AND (r8.c3 <> r9.c1) AND (r8.c3 <> r9.c2) AND (r8.c4 <> r9.c5) AND (r8.c4 <> r9.c6) AND (r8.c5 <> r9.c4) AND (r8.c5 <> r9.c6) AND (r8.c6 <> r9.c4) AND (r8.c6 <> r9.c5) AND (r8.c7 <> r9.c8) AND (r8.c7 <> r9.c9) AND (r8.c8 <> r9.c7) AND (r8.c8 <> r9.c9) AND
(r8.c9 <> r9.c7) AND (r8.c9 <> r9.c8);

Maintenant, nous avons tout ce qu’il faut pour débuter! Si vous avez rencontré des difficultés avec le téléchargement du fichier positions.sql, contactez-moi et je me ferai un plaisir de vous envoyez le fichier compressé (format Gzip) par courriel. Consultez ma page « À propos » pour savoir à quelle adresse courriel me rejoindre!

7. Tester une grille

Asteure que toute la poutine (expression québécoise parfois utilisée pour désigner les tâches cléricales, répétitives, procédurales, techniques ou ennuyantes) est complétée, examinons comment notre solution se comporte pour cette grille :

Pour solutionner la grille, nous devons exécuter cette requête SQL :

SELECT *
FROM sudoku_rows_view
WHERE
(r1c1 = 5) AND (r1c2 = 3) AND (r1c5 = 7) AND (r2c1 = 6) AND
(r2c4 = 1) AND (r2c5 = 9) AND (r2c6 = 5) AND (r3c2 = 9) AND
(r3c3 = 8) AND (r3c8 = 6) AND (r4c1 = 8) AND (r4c5 = 6) AND
(r4c9 = 3) AND (r5c1 = 4) AND (r5c4 = 8) AND (r5c6 = 3) AND
(r5c9 = 1) AND (r6c1 = 7) AND (r6c5 = 2) AND (r6c9 = 6) AND
(r7c2 = 6) AND (r7c7 = 2) AND (r7c8 = 8) AND (r8c4 = 4) AND
(r8c5 = 1) AND (r8c6 = 9) AND (r8c9 = 5) AND (r9c5 = 8) AND
(r9c8 = 7) AND (r9c9 = 9);

Évidemment, je l’espère, vous aurez compris que r8c9 signifie « rangée 8 colonne 9 », etc.

Tada!

Victoire!

Voici ce que le EXPLAIN de la requête a à dire :


mysql> explain
-> SELECT *
-> FROM sudoku_rows_view
-> WHERE
-> (r1c1 = 5) AND (r1c2 = 3) AND (r1c5 = 7) AND (r2c1 = 6) AND
-> (r2c4 = 1) AND (r2c5 = 9) AND (r2c6 = 5) AND (r3c2 = 9) AND
-> (r3c3 = 8) AND (r3c8 = 6) AND (r4c1 = 8) AND (r4c5 = 6) AND
-> (r4c9 = 3) AND (r5c1 = 4) AND (r5c4 = 8) AND (r5c6 = 3) AND
-> (r5c9 = 1) AND (r6c1 = 7) AND (r6c5 = 2) AND (r6c9 = 6) AND
-> (r7c2 = 6) AND (r7c7 = 2) AND (r7c8 = 8) AND (r8c4 = 4) AND
-> (r8c5 = 1) AND (r8c6 = 9) AND (r8c9 = 5) AND (r9c5 = 8) AND
-> (r9c8 = 7) AND (r9c9 = 9);
+----+-------------+-------+-------------+----------------------------------------------------------------------------------------------------------------------------------------------------+-------------+---------+------+------+--------------------------------------------------------------+
| id | select_type | table | type        | possible_keys                                                                                                                                      | key         | key_len | ref  | rows | Extra                                                        |
+----+-------------+-------+-------------+----------------------------------------------------------------------------------------------------------------------------------------------------+-------------+---------+------+------+--------------------------------------------------------------+
|  1 | SIMPLE      | r5    | index_merge | i12,i13,i14,i15,i16,i17,i18,i19,i23,i24,i25,i26,i27,i28,i29,i34,i35,i36,i37,i38,i39,i45,i46,i47,i48,i49,i56,i57,i58,i59,i67,i68,i69,i78,i79,i89,i9 | i16,i19,i49 | 2,2,2   | NULL |   68 | Using intersect(i16,i19,i49); Using where                    |
|  1 | SIMPLE      | r2    | index_merge | i12,i13,i14,i15,i16,i17,i18,i19,i23,i24,i25,i26,i27,i28,i29,i34,i35,i36,i37,i38,i39,i45,i46,i47,i48,i49,i56,i57,i58,i59,i67,i68,i69,i78,i79,i89,i9 | i14,i15,i16 | 2,2,2   | NULL |   78 | Using intersect(i14,i15,i16); Using where; Using join buffer |
|  1 | SIMPLE      | r8    | index_merge | i45,i46,i47,i48,i49,i56,i57,i58,i59,i67,i68,i69,i78,i79,i89,i9                                                                                     | i45,i46,i59 | 2,2,2   | NULL |   78 | Using intersect(i45,i46,i59); Using where; Using join buffer |
|  1 | SIMPLE      | r3    | index_merge | i12,i13,i14,i15,i16,i17,i18,i19,i23,i24,i25,i26,i27,i28,i29,i34,i35,i36,i37,i38,i39,i45,i46,i47,i48,i49,i56,i57,i58,i59,i67,i68,i69,i78,i79,i89,i9 | i28,i38     | 2,2     | NULL |  551 | Using intersect(i28,i38); Using where; Using join buffer     |
|  1 | SIMPLE      | r6    | index_merge | i12,i13,i14,i15,i16,i17,i18,i19,i23,i24,i25,i26,i27,i28,i29,i45,i46,i47,i48,i49,i56,i57,i58,i59,i67,i68,i69,i78,i79,i89,i9                         | i19,i59     | 2,2     | NULL |  551 | Using intersect(i19,i59); Using where; Using join buffer     |
|  1 | SIMPLE      | r1    | index_merge | i12,i13,i14,i15,i16,i17,i18,i19,i23,i24,i25,i26,i27,i28,i29,i34,i35,i36,i37,i38,i39,i45,i46,i47,i48,i49,i56,i57,i58,i59,i67,i68,i69,i78,i79,i89,i9 | i12,i15     | 2,2     | NULL |  624 | Using intersect(i12,i15); Using where; Using join buffer     |
|  1 | SIMPLE      | r9    | index_merge | i56,i57,i58,i59,i89,i9                                                                                                                             | i58,i59     | 2,2     | NULL |  624 | Using intersect(i58,i59); Using where; Using join buffer     |
|  1 | SIMPLE      | r4    | index_merge | i12,i13,i14,i15,i16,i17,i18,i19,i23,i24,i25,i26,i27,i28,i29,i34,i35,i36,i37,i38,i39,i45,i46,i47,i48,i49,i56,i57,i58,i59,i67,i68,i69,i78,i79,i89,i9 | i15,i19     | 2,2     | NULL |  625 | Using intersect(i15,i19); Using where; Using join buffer     |
|  1 | SIMPLE      | r7    | index_merge | i23,i24,i25,i26,i27,i28,i29,i45,i46,i47,i48,i49,i56,i57,i58,i59,i67,i68,i69,i78,i79,i89,i9                                                         | i27,i28     | 2,2     | NULL |  625 | Using intersect(i27,i28); Using where; Using join buffer     |
+----+-------------+-------+-------------+----------------------------------------------------------------------------------------------------------------------------------------------------+-------------+---------+------+------+--------------------------------------------------------------+
9 rows in set (0.06 sec)

Voilà!

Tout fonctionne à merveille! Seulement 0.39 secondes pour solutionner la grille!  Savourons le résultat :


mysql> SELECT *
-> FROM sudoku_rows_view
-> WHERE
-> (r1c1 = 5) AND (r1c2 = 3) AND (r1c5 = 7) AND (r2c1 = 6) AND
-> (r2c4 = 1) AND (r2c5 = 9) AND (r2c6 = 5) AND (r3c2 = 9) AND
-> (r3c3 = 8) AND (r3c8 = 6) AND (r4c1 = 8) AND (r4c5 = 6) AND
-> (r4c9 = 3) AND (r5c1 = 4) AND (r5c4 = 8) AND (r5c6 = 3) AND
-> (r5c9 = 1) AND (r6c1 = 7) AND (r6c5 = 2) AND (r6c9 = 6) AND
-> (r7c2 = 6) AND (r7c7 = 2) AND (r7c8 = 8) AND (r8c4 = 4) AND
-> (r8c5 = 1) AND (r8c6 = 9) AND (r8c9 = 5) AND (r9c5 = 8) AND
-> (r9c8 = 7) AND (r9c9 = 9);
+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+
| r1c1 | r1c2 | r1c3 | r1c4 | r1c5 | r1c6 | r1c7 | r1c8 | r1c9 | r2c1 | r2c2 | r2c3 | r2c4 | r2c5 | r2c6 | r2c7 | r2c8 | r2c9 | r3c1 | r3c2 | r3c3 | r3c4 | r3c5 | r3c6 | r3c7 | r3c8 | r3c9 | r4c1 | r4c2 | r4c3 | r4c4 | r4c5 | r4c6 | r4c7 | r4c8 | r4c9 | r5c1 | r5c2 | r5c3 | r5c4 | r5c5 | r5c6 | r5c7 | r5c8 | r5c9 | r6c1 | r6c2 | r6c3 | r6c4 | r6c5 | r6c6 | r6c7 | r6c8 | r6c9 | r7c1 | r7c2 | r7c3 | r7c4 | r7c5 | r7c6 | r7c7 | r7c8 | r7c9 | r8c1 | r8c2 | r8c3 | r8c4 | r8c5 | r8c6 | r8c7 | r8c8 | r8c9 | r9c1 | r9c2 | r9c3 | r9c4 | r9c5 | r9c6 | r9c7 | r9c8 | r9c9 |
+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+
|    5 |    3 |    4 |    6 |    7 |    8 |    9 |    1 |    2 |    6 |    7 |    2 |    1 |    9 |    5 |    3 |    4 |    8 |    1 |    9 |    8 |    3 |    4 |    2 |    5 |    6 |    7 |    8 |    5 |    9 |    7 |    6 |    1 |    4 |    2 |    3 |    4 |    2 |    6 |    8 |    5 |    3 |    7 |    9 |    1 |    7 |    1 |    3 |    9 |    2 |    4 |    8 |    5 |    6 |    9 |    6 |    1 |    5 |    3 |    7 |    2 |    8 |    4 |    2 |    8 |    7 |    4 |    1 |    9 |    6 |    3 |    5 |    3 |    4 |    5 |    2 |    8 |    6 |    1 |    7 |    9 |
+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+
1 row in set (0.39 sec)

Trop facile ? Hmmmm!?!? Peut-être!  À moins que…

C’était beaucoup trop beau! Comme nous le verrons au prochain article, plusieurs problèmes pointent à l’horizon (et espérons-le, plusieurs remèdes!) et nous devrons faire beaucoup mieux et plus vite avec les outils que nous avons pour arriver à résoudre certaines grilles très particulières.

En attendant la seconde partie de cet article, amusez-vous bien et expérimentez !

Note: j’ai déjà publié cet article en anglais sur mes autres blogues dont récemment ici (l’autre blogue est disparu depuis longtemps). Ne vous en faites pas, il ne s’agit pas d’un cas de plagiat!!!

Prochaine étape, chers lecteurs, nous apprendrons à presser un citron et à en extraire tout le jus!

Répondre

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion /  Changer )

Photo Google

Vous commentez à l'aide de votre compte Google. Déconnexion /  Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion /  Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion /  Changer )

Connexion à %s

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur la façon dont les données de vos commentaires sont traitées.