Bonjour à tous et bienvenue dans ce nouveau Défi de Clem !
Un jeu américain
Eh oui, mesdames et messieurs, je vous parle du célèbre jeu inspiré du carré magique et du problème des 36 officiers d'Euler: le sudoku ! Inventé par un américain puis popularisé par les japonais !
Un rappel des règles du sudoku:
- Il s'agit d'une grille divisée en $3\times3$ carrés, eux-mêmes subdivisés en $3\times3$ autres carrés
- Dans chaque grand carré, les chiffres $1, 2, 3, 4, 5, 6, 7, 8$, et $9$ doivent être présent de manière unique (puisque c'est un carré de $3\times3$)
- Dans chaque ligne, les chiffres $1, 2, 3, 4, 5, 6, 7, 8$, et $9$ doivent être présent de manière unique (il y a $9$ petits carrés)
- Dans chaque colonne, les chiffres $1, 2, 3, 4, 5, 6, 7, 8$, et $9$ doivent être présent de manière unique (il y a $9$ petits carrés)
- Chaque grille a une solution unique.
Le défi
Notre objectif est d'écrire un programme qui résout n'importe quelle grille de sudoku, si cela est possible, de taille $N\times N$. Pour les plus motivés, je proposerai quelques défis qui approfondissent le sujet. Je vais vous guider et vous permettre de tester votre programme sur de nombreux exemples. Avant toute chose, définissons quelques normes pour que tout le monde ait les mêmes entrées et sorties.
I - Quelques normes
On va s'imposer à tous une manière unique de représenter ces grilles par des chaînes de caractères. Tout le monde pourra ainsi copier les grilles de sudoku qui vous seront fournies pour tester efficacement son programme.
Chaque grille se présentera comme suit (les guillemets indiquent qu'il s'agit de texte):
1 | "1234567894567891237891.345623456789156789123489123456734.678912678912345912345678"
|
Vous constatez que cette représentation de grille est peu lisible. Un point signale une case de la grille vide. Chaque ligne de la grille de sudoku est composée de neuf chiffres ou points successifs. Une solution pour implémenter ceci est tout simplement de stocker le sudoku dans un tableau a 2 dimensions (il peut être plus simple de déclarer un tableau 1 dimension, mais de s'en servir comme d'un tableau à deux dimensions. Si vous ne savez pas comment faire, lancez vous et posez vos question. Utiliser un véritable tableau a deux dimensions fonctionne parfaitement aussi). Vous lisez donc les 9 premiers caractères et initialisez la première ligne du tableau d'entiers (fixons que quand le caractère lu est un "." alors on met 0 dans le tableau). Ensuite, passez à la deuxième ligne. Et ainsi de suite. En spoiler un petit pseudo code pour ceux qui aurait du mal à manipuler les tableaux (j'utilise la solution proposée en parenthèse plus haut).
1 2 3 4 5 6 7 8 9 | /** sudoku est un vecteur (= tableau) de longueur 81 **/ Debut Pour i allant de 0 à 8 Pour j allant de 0 à 8 /** E(x) désigne la partie entière de x. E(1.987987) = E(1) = E(1.3232) = 1 sudoku(i*9 + E(j/9)) = saisie_nombre_utilisateur() fin pour fin pour Fin |
Voici une ressource avec pleins de sudokus (un par ligne) réputés difficile pour un humain.
Enfin, nous allons mettre en place une manière d'afficher de telles grilles, pour que l'on puisse aisément les lire:
1 2 3 4 5 6 7 8 9 10 11 12 13 | +---+---+---+ |469|253|871| |426|173|985| |891|725|634| +---+---+---+ |163|587|492| |384|592|167| |495|731|628| +---+---+---+ |518|269|473| |842|719|356| |687|345|219| +---+---+---+ |
Une petite aide:
Nous voulons afficher un sudoku de manière un peu plus esthétique qu'une série de chiffres illisible. Pour ça nous devons définir comment nous voulons l'afficher. Nous voulons donc que la première ligne soit cette chaîne de caractères :
1 | "+---+---+---+" |
Puis pour les 3 lignes qui suivent nous voulons afficher (où "." est un nombre de notre sudoku) :
1 | |...|...|...| |
Et on recommence. En spoiler un petit pseudo code montrant comment faire tout ceci dans un langage utilisant une structure string (comme le C++ ou le Java, mais pas le C)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | Début S = "+---+---+---+" Pour i allant de 0 a 8 si ((i+1) % 3) == 0 alors S = S + "+---+---+---+" fin si S = S + "|" Pour j allant de 0 a 9 si ((j+1) % 3) == 0 alors S = S + "|" fin si S = S + sudoku[i][j] fin pour fin pour |
C'est tout pour uniformiser nos programmes sur les entrées et sorties. Passons aux vrais défis !
II - Vérifier une grille
Pour cette première étape je vous propose que vous fassiez un vérificateur de grille. En entrée vous avez une grille de sudoku et devez dire si la grille est valide. Une grille valide est une grille qui respecte les règles définies plus tôt dans ce défi. Je vous donne en spoiler quelques jeux de tests. Avant de vous lancer dans comment vous allez faire, n'oublier pas de réfléchir à comment vous aller représenter le sudoku dans votre programme.
1 2 3 4 5 6 7 8 9 | "123456789123456789123456789123456789123456789123456789123456789123456789123456789" "123456789123.56789123456789123456789123456789123456789123456789123456789123456789" "1234567894567891237891.345623456789156789123489123456734.678912678912345912345678" "123456789456789123789123456234567891567891234891234567345678912678912345912345678" // FAUX // FAUX // FAUX // VRAI |
Je ne détail pas plus car je pense que c'est relativement simple. Pour ceux qui auraient le plus de mal posez des questions et si vraiment il y à besoin je rajouterais une petite partie.
III - Résoudre une grille de Sudoku !
Ca y est ! On est paré. On a notre représentation, de quoi vérifier et de quoi générer. Allez y !
Bon.. D'accord je vous guide un petit peu. A ceux qui ne savent vraiment pas comment s'y prendre, voici un tuto qu'un membre a rédigé utilisant le backtracking, une bonne méthode pour des petites grilles ! Ceci est une méthode très lourde en calcul mais qui a l'avantage d'être rapide à implémenter et plutôt simple à comprendre.
Je vais vous proposer une autre approche qui me semble plus interessante. Cependant il va falloir sérieusement vous creuser la tête et je vous expliquerais pourquoi. Quand j'ai été confronté au problème (en cours en fait) j'avais le choix de "bêtement" faire le backtracking ou trouver ma propre méthode. Je me suis donc posé avec un stylo et une feuille et je me suis dit : "Comment je fais moi pour résoudre un sudoku ?". S'en est suivi une bonne heure où je décortiquais ma méthode de résolution. J'ai ensuite décidé de l'implémenter et.. Magie ça fonctionnait ! Ou presque. En effet, si vous avez déjà fait des sudokus, vous savez qu'il y à plusieurs méthodes pour savoir où placer un chiffre. Certaines sont simples, d'ailleurs les sudokus de difficulté 1 ne font appel qu'a une simple méthode. Certaines sont plutôt compliqué et demande beaucoup d'interprétation.
Les méthodes de résolutions "humaines" se basent sur l'analyse des nombres. "Comment les nombres sont-ils disposés dans la grille ?". Par exemple quand je fais un sudoku, je regarde ou est-ce que je peux placer un 1, puis un 2 etc. Ce que je fais souvent aussi c'est regarder pour chacune des cases si c'est évident de disposer un nombre (par exemple il ne reste qu'une seule case dans la ligne). Voici à quoi pourrais ressembler une algorithme de résolution (en pseudo code). Attention, tout n'est pas détaillé, il faut que le défi reste présent ;).
1 2 3 4 5 6 7 8 9 10 11 | Début Tant que !est_resolu(sudoku) Pour i allant de 0 à 81 nombres = trouver_nombres(sudoku, i) si nombres.size == 1 alors sudoku[i] = nombres[0] fin si fin pour fin tantque afficher(sudoku) Fin |
Voilà pour les petits conseils. Bien entendu, il ne s'agit pas des seules solutions pour résoudre un sudoku. Si vous voulez en connaître plus, Yoch (l'auteur du tutoriel sur le backtracking) m'a fourni une liste de différentes solutions à utiliser :
- Le module clpfd (Constraint Logic Programming over Finite Domains) de prolog qui est parfaitement adapté à ce type de problème.
- Le sudoku peut être modélisé comme un problème de couverture exacte de la théorie des graphes, pour lequel il existe un algorithme de résolution très efficace imaginé par D. Knuth : l'algoritme DLX. C'est un algorithme intéressant et relativement accessible.
- Le sudoku peut aussi être modélisé comme un problème SAT, et il existe aujourd'hui des dizaines de solveurs SAT extrêmement performants (par exemple minisat, picosat).
- Le sudoku peut encore être modélisé comme un système linéaire sur les entiers, pour lesquels il existe également des solveurs efficaces (par ex. GLPK).
- Et enfin, il est particulièrement intéressant de réaliser un système modélisant les techniques utilisées par des joueurs humains, pour permettre par exemple de classer des grilles en ordre de difficulté. Il y a plein de sites qui traitent ce sujet, par ex. ici.
IV - Générer une grille
Maintenant que vous savez résoudre des grilles, vous pouvez en créer et vérifier qu'elles sont valides ! Je ne met volontairement pas de solution mais uniquement des indications. Un avant goût des challenges !
Peu d'informations sont nécessaires pour pouvoir générer un sudoku. En réalité surtout une : le nombre de cases pleine (ou vide). Nous aurons donc une entrée qui sera le nombre de case pleine (sachez qu'une grille ayant moins de 17 cases pleines possède plusieurs solutions. Ce qui ne veut pas dire que toute les grilles a plus de 17 cases remplies ont une unique solution). Donc adaptez vous pour respecter l'ensemble des règles (y compris l'unicité de la solution !).
Il existe plusieurs méthodes pour créer une grille de sudoku. En spoiler des indices sur deux méthodes, à vos stylo pour réfléchir !
- Une première méthode consiste à générer une grille aléatoire et enlever un certains nombre de case
- Une deuxième méthode consiste à prendre une grille de départ résolue et à effectuer certaines opérations | dessus. Par exemple échanger les colonnes n°1 et n°2. Attention tout de même à ne pas échanger n'importe quoi (petit indice, les colonnes numéro n°3 et n°4 ne sont pas échangeable)
Pour les plus motivés, voici quelques challenges que je vous propose, ce sont des propositions ouverte.
V - Petits challenges supplémentaires
- Résoudre (et générer) des grilles Samouraï
- Résoudre (et générer) des grilles $N*N$
- Résoudre (et générer) des grilles à 3 dimensions de longueurs N ($N*N*N$)
- Résoudre (et générer) des grilles à M dimension de longueur $N$ ($N*N*N...N*N*N$)
Annexes
Voici quelques ressources si vous voulez testez vos programmes. Par exemple en regardant le temps que vous mettez à résoudre tout une série de sudokus, ou encore combien d'opération vous faites (la complexité).
- Voici des grilles lentes pour le backtracking
000000068900000002000400500041000000000035000050000000000800010300000700000100400 000000201004030000000000000370000080600200000000500000540000600000070040002001000 000000360430000000000500700000760000200000005000000100000042008070000040001000000 000000408900300000000100000000000530000005100200060000000720006054000000000000010 000000501100800000000000400005013000040000700000600000620000080000050020000004000 000000592080010000000000000600200400410000000000000000006504000300000010000700008 000000701060050000008000000000104000700000020030800000000070350004000600100000000 000000840000720000000100000200050006000004030100000000000200001040000500030007000 000001305600000100000020000004300070000005000000900000000070480510000000000600000 000010800050000000200000000800000100000207000000500004600300070000000350048000000 000040200030000800007000000040000001200080000000500070001700030600200400000000000 000050600104000000000003000020400000060080000000000010000300206300107000000000500 000060080020000000001000000070000102500030000000000400004201000300700600000000050 000071006500000030000000000000080100800300000000000007007400200010002000000500080 000081000300000500200000000060200300010000000000004000008000042500700000000060010 000100038200005000000000000050000400400030000000700006001000050000060200060004000 000107000900000200800000000002060000000400500000000701000030080070500000050000060 000200030060000040000000000070050900800300000000000000000067100400005070300000800 000200030060000040000000000090050800700300000000000000000069100400005060300000700 000200030070000040000000000060050900800300000000000000000067100400005070300000800 000200030090000040000000000060050800700300000000000000000069100400005060300000700 000300500200080000000000000063500000100000020000030040700002000000100004080000300 000309600200000050000000000500080000000400700000000001013000400000005030070020000 000358000600000100000000000160000005000700030200004000030500070000010200000000000 000400100250000000600050000070300000000800004000000020400020000030000700000006010 000400100250000000600050000070300000000800004000000020400020000030000900000006010 000400200083000000000000006010070000600000400000008300500320000007000080000000010 000500900000703000100000000000400037900020000000000040030060000000008100045000000 000600045300007000000000000600000007000002500040080000001050000020000060000700300 000600200100000000000030000000200609710000000000800000009504000080000300000070010 000600302040001000000000000203050000000000480006000000810000050000020700000300000 000600302040001000000000000203050000000000490006000000810000050000020700000300000 000600720810000000000040000200000801004500000000000200000300060700080000000000050 000620900801000000000000000400108000030000700000200000590070000000005020000000001 000700005300800000000000600000060300700000020000001000430050000060000001000200070 000800600070400000200000000300001000000020070001000400150000002004600000000000030 000810000300000040000000000670100000500000930000200000408003000020000001000000600 000870600200000000000100000060054000000000021400000000070000050000200300500001000 000900200450000000100000000008607000000000001000000040500021000070000600000040030 000940500210000000000008000302100000000000400800000000040000890000200030000060000 001430000700000080000000000000702000014000000000600020000060401850000000000000300 001700500600800000000000000000200004050000030090004000200000800400030000000050900 003100060080500000000000000007060200200000001000400500000082030040000000500000000 003400060500000001000000000000706400800000002000300000120080000000000630050000000 004036000100000500000000000062000000000050700000800200000002004700000030050700000 004600000000000780050000000010300500200000106000400000700010000008000040000000003 005000020000100000400000000000400108050000400009000000800620000030000700000009050 006005030020040000000000000300700000000010800005000400000200056040008000100000000 010009000000700200000000000000300160800400000000000502000010040200050000409000000 010050300000200000080000000020400800700620000300000100500000020000001000000000060 010050300000200000080000000020400800700620000300000100900000020000001000000000060 010050300000200000080000000060400800700620000300000100500000020000001000000000060 010050300000200000080000000060400800700620000300000100900000020000001000000000060 010050300000600000080000000060400800700260000300000100900000020000001000000000060 010050300000800000070000000020400700600280000300000100900000020000001000000000080 010050700000200000080000000020400800700620000300000100900000020000001000000000060 010050700000200000080000000060400800700620000300000100900000020000001000000000060 010050800000200000070000000020400700500620000300000100900000020000001000000000060 010050800000200000070000000060400700500620000300000100900000020000001000000000060 010400700000000280000000000208050000000001600005600000040000003000020050600000000 010400700000000290000000000209050000000001600005800000040000003000020050600000000 010500400000000290000000000209060000000001700006800000040000003000020060700000000 010500800000000290000000000209060000000001700006700000040000003000020060700000000 020001000000070800600000050000000021000630000080000000706000400500004000000200000 020001000000080900600000050000000021000630000040000000708000400500007000000200000 020500000000000308000000900300400050800030000000000010000020700050100000604000000 040000600000070100000080000050400000000000027000003000107200000000100530800000000 040060000000000350000010000009000100300800000000400060400200700060005000000000003 050000801003600000000000000204000060000071000000000008000400390070030000100000000 050060700008400000000000000400102000003000600000500200020070000000000034100000000 060300100007500000000000000000200005800000030040070000000018400002000800500000000 080000010004030000000600500600000003000020400010000000300108000000500070000000200 081020000030009000000000000700600500000180000000000001400005000000000130000000086 082700400000100000000000000310005000500040300000000000004093000600000010000000008 083000070000610000000000000601700000400000080000000002020408000500000100000000300 100000200003700000500400000200010000080000003000000040020300000600000100000007050 100030670720000000000000000000201400300000500000400000008000030040500000000000001 100300520080000000000000000005600000600000008000004070002000300070010000000080600 100600000407000000000800700003000500070000200000010000050302000600000040000000001 140000030000720000000000000080603000006000200000000100007500080200400000000080000 200000080000400030000005000000040006000700400800000500074000100030020000000008000 200000300000800500000001000080002040000030700000000000600070020057400000000000001 200100600004000500000000000030040000800000009000050700050700010000800030007000000 200500070060030000000000000000040501004008000050000020100700000003000400000200000 200500070060030000000000000000040501004009000050000020100800000003000400000200000 300050001000010000000000060002600300000204000000000005850000700000900020100000000 300600100007000008000000000080001000000300200005000060000540070020060000100000000 370000060000100000000000200000003400500000001000060000401500000000070058030000000 400600300200700000010000000000270000000000018000000050306000700050008000000010000 480001000000090200000000000050700008602030000000000010200400300030000000000008000 560100040000800000000000000301040000020000008000000700400000530700008000000200000 600050001000010000000000070002700300000204000000000005850000600000900020100000000 600130000004000800000000000007500000000020060080000030000400709300001000020000000 600400503000030700000000000000370100800000040200000000007000002000600080010000000 700000200030060000000000000060010000300000080000700500002400060500207000000000001 700040800000000390000000000013900000000070600000100000000050010600300000200000005 700081000000000530000000000000605200400300008000000000000070064005200000010000000 700600300000501000800000000040002000000070800010000000000400001006000020300050000 700800200050100000000000000300050060000400001006000080000026300001000000040000000 700800400000290000000000000021000090000500700000000010500000620300006000000010000 706080000900000001000400000000003600040200000000000900680009000000010040000000020 780200000000600500000000000050030000000001070006000020000050301200400000001000000 800093000000000601000000000500200040000701200000000000000020480010600000300000000 800094000000000701000000000300200050000701200000000000000030580010600000400000000 800100000000006400000000000680000020000050300700040000004300000000200080500000009 901006000800500000000000000040200000000000960000050000050000402300010000000009070 020000350000010000040000000800205000000000906000400000901060000700009000000000020 190000200000037000000000000607000800000100000300000000020400010005028000000000003 100000800000060200000300000200070000000001040080000030007000560040803000000000000 708000065000200400000000000000060001300000080040500000270000500000081000000000000 004000200010500000000000060600010000000020070030000000200000140007800000000300600 300500070060020009000000000000300500800000040010009000500470000000000901000000000 060205700000300000000000000300000200700001000001090000820600000000040009000000010 010600420000800000000050000005000030700400000000001000200030700000000504040000000 000010003450000000000000080000400700900000500008000006000701020060000400000080000 040030000000000201000000800001400000200000600000070000000506040600002000070000080 040030000000000801000000200001400000200000600000070000000506040600002000070000080 200304000000000070800000000000610700004000900600500000070000060000200003010000000 200405000000000010800000000000730100005000900700600000030000070000200004010000000 800405000000000010200000000000730100005000900700600000030000070000200004010000000 450060300070010000000000000600000050000003700001800000700000010000200008000009000 006000300040000050000080000000603500400200000100000000005700000000010004090000800 500100006000430000000000080000200300000007040100000000020000500040008000000050001 680010000000000250000000000000004006004500000070000100500200040003070000100000000 620000000000100500000000000200040050000020100070008000100000040005300000000700002 100500400000000680000000000020608000000000700000200000306070000000010020700000009 600200090000150000000000000510000700000800040070000008800000100400003000000070000 500040320000170000000000000830000600000720000000000000000008001020600000007000040 000570013400000000000000008000600420810000000000000000100420000050000600000001000 083000060000720000000000000000003010520000000700050000009004500000000204000100000 083000070000420000000000000000003010640000000200060000009005600000000405000100000 680000200000340000000000000050700080000000023900100000000008500003020000700000000 000600804301000000000000000700500030000200001000007000260000700040010000000000020 800000020000100800000070000420000600500700000000300001000040050000050007010000000 200400600000500010000000000500080004300020000000000080000705300008000002010000000 800600001000400020000000000053000000002050000040700000700001000000000250000020300 050000270000830000100000000020700000000040008500000300301000006000200050000000000 600020500900000030000000000001000007020060000000400010480000600000001002000300000 200500080001020000000000000070008000003000020000070600600200001040000700000300000 100000300060007000000080004500000710010800000000020000200001000000300008000000060 701000000000500020000040600540000010030060000000800000000000306900100000000000400 100800050000040030000600000060020900000003001000000000000900400300000200507000000 020008500000700060000000000400000007000020030000010000308600000000500002600000100 003090400800700000000000000500000904700200000000001600090060000000300020060000000 000060200080300000100000000064000300000500040000001000700100000002000600000040080 007000501200400000000000000820600000000010700000000000000200380005000060010004000 000007401200300000000000000080530000004000700000000000030000650000004080700010000 040000058300010000000000000501008000000000620600000000007000005000600100020300000 603200000000040001000000000002000940700001000000050000070400080150000000000000300 400000670050100000000000000070030000000000260080500000201000005600002000000080000 020000308000010000400000000500060010803000000000700000000800400070400000005000090 000300501080070000000000000000080200100500000004000070000401060350000000000000300 205700000800050300000000000090000100000200000000000030030081000400000020000900007 800600000000000450000001000000040700600000300000020000074000020000800001020300000 000360000020000800000100000100000060000002500000000007008000240300700000000040005 300040500000000001200000000070000300000108000400600000000070230008000060010000000 005000003000700400010080000040005200000300000200000000000020610703000000000004000 000600450070001000000300000000080600100000000000002000038040000000700010400000002 400700005000000070000200000630000800500000200000010000000004100070500000001000090 240000060000051000000000300000040800700500000000000001000200075018000000000900000 260000007000031000000000400000050030800100000000000900000600018039000000000500000 302400006500009000010000000000700400080000010000300000000080060700000300000010000 402500006300009000010000000000700500080000010000300000000080060700000400000010000 760000000000000408000010000000640100300000500008000000000500023000003060040000000 000200040800000300010000000002004000000080050070000100000710600400500000000000001 000300062010000000000000000050400100600050000000000008700208000300000500000040010 000306700820000000000000000006050020200000030050007000300200000000480000000000100 600000903000401000000000000000100080000073000900000004540030000080000010000000200 000940700021000000000000000700001300800000004000200000040005020300080060000000000 130000900000005008000200000000130000400000070000900000002007080000040100090000000 020100000000000050000060000500002070000400600001000000860007000000030001200000400 150000000600000800000900000302000070700000010000200000040017000000050900000000200 634000000000250000000000070250000030000100000300000000001000800070004000000030200 000000209300000010400000000000301000000600400000400500025080000070050000000000040 800300000050000040700010000100605700000400000000000000024000030000020800000001000 041000000600030000000200000700500008000004000000100000020000910000090200800003000 080500300000000240000000000500000001000007060300020000000300700004060000062000000 000401000005000700000000840000610003240000000500000000000200050001070000030000000 800010000000000053060000000000402600100000000000000002023600000000870010000000500 080000400300600000000700005100200000000000052400000000700000300000080060020050000 080000100300500000000600004100200000000000042700000000600000300000080050020040000 000200400005000800030070000000108030100400000000000050024000100000050000600000000 400750000002030000000000000000500064010000000000000080570000300000002100600400000 050003600200700000000000000830400000000020070000000100000001200700000040040060000 000040230800500000000000000060001000000020700300000400000300068002400000010000000