Rapport sur Killer Sudoku Solver
Table of Contents
Présentation du problème
Killer Sudoku Solver est un problème du site Codingame. Il consiste en la création d’un programme permettant la résolution d’une grille de Killer Sudoku. Voici tout d’abord un point sur ce qu’est un Sudoku, et sur les spécificitées du Killer sudoku:
Le sudoku, est un jeu en forme de grille, créé en 1979 par l’Américain Howard Garns qui s’est inspiré de jeux plus anciens afin de le créer. Le but du jeu est de compléter la grille selon les règles suivantes (on décrira ici les règles pour une grille classique de 9*9):
- Chaque ligne doit contenir de façon unique les nombres de 1 à 9.
- Chaque colonne doit contenir de façon unique les nombres de 1 à 9.
- Chaque carré de 3*3 délimité sur la grille doit contenir de façon unique les nombres de 1 à 9.
De plus, le Killer Sudoku, variante du Sudoku classique, a une spécificité. En plus de la grille classique de Sudoku, on délimite des zones à l’intérieur de celle-ci. On assignera à chaque zone une valeur, et la somme des nombres dans la zone devra être égale à la valeur de la zone. Avec cette règle en plus, cela a pour effet qu’il n’existe qu’une seule solution à chaque grille (ce qui n’était pas forcement le cas dans le cadre d’un Sudoku classique).
Alors, suivant ces règles, notre programme doit compléter et renvoyer la grille de Sudoku donnée en entrée, ligne par ligne. Chaque ligne est donnée sous la forme d’un tableau de caractère. Les cases restantes à compléter sont représentées par des .. De plus, il nous est fourni un second tableau de caractère servant à délimiter les zones de la grille. Chaque zone est définie par une lettre (majuscule ou minuscule). Les différents élements d’une zone sont forcement adjacent. Enfin, le site nous fourni une dernière entrée, une ligne avec les valeurs de chaque zone. Afin de mieux comprendre le format des entrées, voici un exemple d’entrée fournie par le site.
56..1..2. aabbccdde ..72..68. afghhiide ..2.87.15 jfggklmme ......3.9 jjgnklopp .7....2.. qqgnooorr 9.634.8.. stuuvwwxx 2.9..8... stuuvvwyz ..41.2... sAuuByyyz .8.4...3. CADDBEEFF a=12 b=17 c=4 d=14 e=15 f=13 g=19 h=7 i=10 j=16 k=10 l=13 m=10 n=15 o=15 p=13 q=11 r=11 s=18 t=3 u=28 v=15 w=20 x=8 y=22 z=12 A=11 B=13 C=6 D=9 E=10 F=5
Métode de résolution
Description de la stratégie
Afin de résoudre ce problème, il me fallait reprendre le principe du premier puzzle sudoku, et l’améliorer pour qu’il prenne en compte les spécificitées de cette variante. Ainsi, j’ai pu découper mon code en plusieurs parties afin de pouvoir décrire au mieux ma démarche de résolution.
Partie 1: Fonction affiche
void affiche(){ for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { fprintf(stderr,"%d", tab[i][j]); // Affiche la case de coordonnée i,j dans le tableau } fprintf(stderr,"\n"); } }
Cette fonction permet d’afficher la grille de sudoku. Elle m’a notamment été utile lors du débugage de mon code.
Partie 2: Fonction find_cage
int find_cage(int x,int y,int n){ // Définition des variables int somme=n; int k=0; int stop=0; int i=0; int j=0; int count=0; // Boucle qui permet de récupérer des informations sur la cage dans laquelle se situe la case que l'on traite while((lettre[k]!='\0')&&(stop!=1)){ if(lettre[k]==cage[y][x]){ stop=1; } k++; } // Tant que l'on a pas parcouru le tableau ou que l'on a pas trouvé tout les élements d'une cage while((i<9)&&(compteur[k-1]!=count)){ while((j<9)&&(compteur[k-1]!=count)){ if((cage[i][j]==cage[y][x])&&(tab[i][j]!=0)){ // Si on a trouvé un élement de la cage et qu'il est rempli somme+=tab[i][j]; // On ajoute sa valeur à la somme count++; if(somme>valeur[k-1]){ // Si la somme des cases est plus grande que la somme attendue return -1; } }else if((cage[i][j]==cage[y][x])&&(tab[i][j]==0)&&((i!=y)||(j!=x))){ // Si on trouve une case qui appartient à la cage est qui est vide return 1; } j++; } j=0; i++; } if(somme==valeur[k-1]){ // Si la somme est bien la somme attendue return 1; } return 0; }
Cette fonction permet de vérifier si le nombre que l’on souhaite ajouter respecte bien les conditions liées à sa cage. On cherche donc à savoir si la somme des nombres de la cage en question (ajoutés au nouveau nombre) est bien la bonne. Pour cela, on va parcourir la grille dans une double boucle while ce qui pourra nous permettre de stopper le parcour lorsque l’on aura trouvé tout les élements dont on a besoin. Ainsi, on va définir une variable count qui sera incrémentée de 1 dès lors que l’on aura trouvé un élement de la cage. Elle permettra à la boucle de s’arréter lorsque l’on aura trouvé tout les élements de la cage. Mais ce n’est pas la seule condition qui fera s’arréter la boucle. En effet, si un des élements de la cage n’est pas encore complété, il ne servira à rien de chercher les autres élements, puisqu’il nous sera impossible de compléter la somme de la cage. Alors, on renvoi 1, ce qui signifit que le nombre que l’on souhaite ajouter peut être bon, même si l’on en est pas assuré. Enfin, à chaque tour de boucle, on va tester si la somme actuelle que l’on connait (celle qui n’est pas encore complète puisque l’on a pas trouvé toutes les cases), ne dépasse pas la somme attendue. Si tel est le cas, on renvoi le code -1, qui signifit que le nombre que l’on souhaite tester fait que la somme dépasse de la valeur attendu. Cela pourra nous aider à gagner en nombre d’opération plus tard dans le programme. Enfin, si l’appel de la fonction ne s’est pas arrété avant la fin du parcour, on compare la somme obtenue à celle attendue pour la cage. Si elles sont égales, on renvoi le code 1 signifiant que le nombre est éligible pour compléter cette case.
Partie 3: Fonction test
int test(int y, int x, int n) { int x0=0, y0=0; int count=1; // Vérifie si le nombre testé est déja sur la ligne while (x0 < 9 && count==1) { if (tab[y][x0] == n) { count=0; } x0++; } // Vérifie si le nombre testé est déja sur la colonne while (y0 < 9 && count==1) { if (tab[y0][x] == n) { count=0; } y0++; } // Vérifie si le nombre testé est déja dans le carré de 3 par 3 x0 = (x / 3) * 3; y0 = (y / 3) * 3; int i=0; while (i < 3 && count==1) { int j = 0; while (j < 3 && count==1) { if (tab[y0+i][x0+j] == n) { count=0; } j++; } i++; } return count; }
Dans cette troisième partie, on définie une fonction test, qui va permettre de tester la validité des nombres que l’on souhaite ajouter à la grille. Cette fonction prend en argument les coordonnées de la position à laquelle on souhaite ajouter le nombre, ainsi que le nombre que l’on souhaite ajouter. Premièrement, la fonction va tester si le nombre testé est présent sur la ligne où l’on souhaite l’ajouter. Si c’est le cas, on renverra le code 0 qui, dans le cadre de ce code représentera un échec. Ensuite, la fonction va tester de la même manière, si le nombre que l’on souhaite ajouter est déjà présent sur la colonne. Enfin, dans une troisième boucle, qui est une double boucle while, on va parcourir le tableau afin de vérifier si le nombre est déjà présent dans le cube de 3*3 délimité par les grilles de sudoku. A la fin de tout ces test, on renverra 1 si tout les tests sont passés, sinon la fonction renverra 0.
Partie 4: Fonction solve
void solve() { int y, x,n; for (y= 0; y < 9; y++) { // On parcour le tableau for (x = 0; x < 9; x++) { if (tab[y][x] == 0) { // Si on tombe sur une case non remplie for(n = 1; n < 10; n++){ int find=find_cage(x,y,n); if ((test(y, x, n)==1)&&(find==1)){ // Test si le nombre n peut aller dans la case tab[y][x] = n; // Mise à jour du tableau solve(); // On relance la fonction tab[y][x] = 0; // Remise à 0 de la case si mauvais choix }else if((test(y, x, n)==1)&&(find==-1)){ break; } } return; } } } for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { printf("%d", tab[i][j]); } printf("\n"); } exit(0); }
Dans cette partie, on retrouve la fonction solve, utilisant le mécanisme de récursion, qui va nous permettre de trouver la solution à la grille de Sudoku. Si le nombre que l’on souhaite ajouter respecte les conditions, on va à nouveau appeler la fonction solve pour continuer à compléter la grille. Si la fonction find renvoie le code -1, cela signifit que la somme de la cage est plus grande que la somme attendu. Il faut donc revenir en arrière sur le positionnement des autres chiffres, puisque comme on test les chiffres par ordre croissant, le chiffre suivant ne sera forcement pas valide (la somme sera forcement trop grande).
Partie 5: Fonction voisin
int voisin(int i, int j, char name, int count, int teste[10][10]){ // Si l'élement testé ne fait pas parti de la cage if (cage[i][j]!=name){ return count; } count++; // On ajoute 1 au compteur si on détecte un élement de la cage teste[i][j]=1; // Indique que la case a déja été testé if((i!=0)&&(teste[i-1][j]!=1)){ // Si on est pas en haut du tableau, alors on regarde la case du dessus count=voisin(i-1 , j, name, count, teste) ; } if((j!=0)&&(teste[i][j-1]!=1)){ // Si on est pas sur le bord droit du tableau, alors on regarde la case de gauche count=voisin(i , j-1, name, count, teste ) ; } if((j!=8)&&(teste[i][j+1]!=1)){ // Si on est pas en haut du tableau, alors on regarde la case de droite count=voisin(i , j+1, name, count, teste ) ; } if((i!=8)&&(teste[i+1][j]!=1)){ // Si on est pas en bas du tableau, alors on regarde la case du dessous count=voisin(i+1 , j, name, count, teste ) ; } return count; }
Dans cette partie, on définit la fonction voisin, qui a pour but de calculer le nombre d’élement de chaque cage. Ainsi, on pourra se servir de cette donnée afin de créer les nouvelles conditions vu au dessus. Cela diminuera donc la complexité de notre code, et donc son temps d’execution. On reviendra plus en détail sur cette fonction lors du détail algorithmique.
Partie 6: Boucle principale
for (int i = 0; i < 9; i++) { char grid_line[10]; char grid_cages[10]; scanf("%s%s", grid_line, grid_cages); fgetc(stdin); for(int j=0;j <9 ;j++){ // Récupération des valeurs des cases du sudoku if(grid_line[j]!='.'){ // Si la valeur est indiqué tab[i][j]=grid_line[j]-'0'; // On l'inscrit }else{ tab[i][j]=0; // Sinon on lui assigne automatiquement la valeur 0 } cage[i][j]=grid_cages[j]; // On crée un tableau avec les cages } } char cages[251]; scanf("%[^\n]", cages); int j; int i=0; int k=0; int y=0; int x=0; char stock_nb[10]; stock_nb[0]='\0'; stock_nb[1]='\0'; stock_nb[2]='\0'; int count_tot=0; int trouve=0; int xsave,ysave; while(cages[i]!='\0'){ // Tant que l'on a pas lu toute la ligne if(((cages[i]<='Z')&&(cages[i]>='A'))||((cages[i]<='z')&&(cages[i]>='a'))){ // Si on détecte une lettre j=2; while((cages[i+j]<='9')&&(cages[i+j]>='0')){ // On récupère la valeur correspondante stock_nb[j-2]=cages[i+j]; j++; } valeur[k]=atoi(stock_nb); count_tot++; lettre[k]=cages[i]; while(trouve!=1){ while((trouve!=1)||(x<9)){ if(lettre[k]==cage[y][x]){ trouve=1; xsave=x; ysave=y; } x++; } x=0; y++; } y=0; trouve=0; int teste[10][10]={0}; // Remplissage d'un tableau de 0 compteur[k]=voisin(ysave,xsave,cage[ysave][xsave],0, teste); // Création d'un tableau avec le nombre d'élement de chaque cage. stock_nb[0]='\0'; stock_nb[1]='\0'; stock_nb[2]='\0'; k++; } i++; } i=0; j=0; solve(); return 0;
Dans le programme principal, on va récupérer les entrées fournies par Codingame, puis on va appeler la fonction solve, qui va résoudre le Sudoku. Pour récupérer les entrées, on va stocker dans un premier tableau la grille de sudoku, vide, avec uniquement les chiffres déjà préremplis dans les test. On va remplacer les . représentant les cases vides par des 0. Simultanemment, on stockera les cages dans un autre tableau. Ensuite, on va récupérer les valeurs des sommes pour chaque cage. Pour se faire, on va parcourir la ligne qui nous est fournie. Lorsque l’on trouvera une lettre, on la stockera, puis on cherchera la valeur correspondante. Les entrées sont données en une seule ligne sous la forme suivante: lettre=nombre et sont séparées par un espace. Pour récupérer le nombre il suffira donc de récupérer tout les caractères qui sont des chiffres jusqu’à rencontrer un espace. Ensuite, à l’aide de la fonction atoi(), on convertira la chaine de caractère obtenue en un entier. Enfin, on fera appel à la fonction voisin dans une double boucle afin d’obtenir le nombre d’élement de chaque cage.
Détail algorithmique de la méthode
Ce programme se base sur le principe de récursion, et appelle un certain nombre d’autres fonctions auxiliaires. Je vais tout d’abord présenter le principe générale de la récursion, puis je reviendrai plus en détail sur l’une des fonctions de mon code.
Principe général et fonction solve
Cliquez ici pour agrandir
Dans le schéma ci dessus, les flèches rouges représentent un test négatif, et les vertes un test positif.
Tout d’abord revenons sur ce qu’est un mécanisme de récursion. Ce mécanisme consiste en l’appel d’une fonction à l’intérieur d’elle même. Une fonction récursive mal définie/codée peut donc engendrer un nombre infini d’appel. Mais ce mécanisme bien utilisé peut être très puissant et extrémement utile.
C’est le cas de cette fonction qui l’utilise afin de trouver la solution au Sudoku de manière brute. C’est-à-dire qu’elle teste toutes les solutions possibles jusqu’à trouver la bonne. Dans cette fonction (solve()), on parcour le tableau, et dès que l’on trouve un 0, on essaye de remplir la case. Alors, on test tout les nombres de 1 à 9. Pour que le test soit validé il faut que la fonction test, et la fonction find_cage renvoient 1. Dès que l’on trouve un nombre qui fonctionne, on le place dans la grille, puis on appelle à nouveau la fonction, jusqu’à ce que la grille soit remplie. Alors, si les nombres placés bloquent la grille de Sudoku par la suite, les cases précedement remplies seront remises à 0, et les test seront fait avec d’autres nombres. Si la fonction find_cage renvoie -1 et que la fonction test renvoie 1, alors on sait qu’il n’est pas nécessaire de tester d’autres nombres pour cette case, et qu’il faut directement revenir en arrière.
Ainsi, suivant ce processus, la fonction ne s’arrétera que lorsque toute la grille sera remplie.
Fonction voisin
Nous allons maintenant nous intéresser à la fonction voisin et à son fonctionnement. Afin d’illustrer mes propos, voici ci dessous un schéma explicatif du fonctionnement de la fonction.
Cliquez ici pour agrandir
Dans le schéma ci dessus, les cases rouges représentent la cage dont on compte les élements, les cases plus clairs représentent les cases à tester, et les cases plus foncées représentent les cases testé au moment de l’étape. La seconde grille remplie de cases noirs et blanches représente le tableau test, servant à sauvegarder les cases rouges déja testées.
Pour rappel, la fonction voisin a pour but de compter le nombre d’élement d’une cage. On voudra ici compter les élements de la cage rouge.
- Etape 0:
Cette étape représente l’état initial de nos tableaux, lorque aucune opération n’a encore été effectuée.
- Etape 1:
Lors de l’étape 1, on appelle la fonction pour une case donnée. On marque alors cette case dans le tableau test pour montrer que l’on a déja appelé la fonction voisin pour cette case. Puis on ajoute 1 au compteur.
- Etape 2:
On va alors appeller récursivement la fonction voisin, pour chaque case adjacente à la première case testée. Lors de cette étape on test la case de gauche, comme on ne détecte pas qu’elle appartient à la cage dont on souhaite compter les élements, alors on ne modifie rien.
- Etape 3:
Sous le même principe que l’étape 2, on va tester cette fois ci la case du dessus. Comme elle n’appartient pas à la cage, on ne va rien modifier.
- Etape 4:
On va alors procéder de la même manière jusqu’à tester la case en dessous de la case de départ. Cette fois ci, comme on détecte l’appartenance à la cage, on va ajouter 1 au compteur, puis marquer la case dans le tableau test, et tester les cases adjacentes.
- Etape 5:
L’opération précedente nous mène à l’étape 5. En effet, on a testé les cases adjacentes à la case détectée précedement, sans tester la plus haute case rouge puisqu’elle a été marqué dans le tableau test. Alors, on a fini par détecter que la case que l’on test actuellement (la plus basse) est une case appartenant à la cage rouge. On a alors ajouté de nouveau 1 au compteur, et marqué la case dans le tableau test. Ainsi après avoir testé toutes ses cases adjacentes, la fonction ne détectera pas d’autres cases appartenant à la cage testée. Donc cette fonction finira par renvoyer 3, la valeur du compteur.
Remarque: On observe encore une fois, dans le cadre de cette fonction, l’utilitée de la récursion.
Code commenté ayant résolu le problème
Voici ci dessous le code complet m’ayant permis de résoudre le problème.
#include <stdlib.h> #include <stdio.h> #include <string.h> #include <stdbool.h> int tab[10][10]; char cage[10][10]; int valeur[100]; char lettre[100]; int compteur[100]; void affiche(){ for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { fprintf(stderr,"%d", tab[i][j]); // Affiche la case de coordonnée i,j dans le tableau } fprintf(stderr,"\n"); } } int find_cage(int x,int y,int n){ // Définition des variables int somme=n; int k=0; int stop=0; int i=0; int j=0; int count=0; // Boucle qui permet de récupérer des informations sur la cage dans laquelle se situe la case que l'on traite while((lettre[k]!='\0')&&(stop!=1)){ if(lettre[k]==cage[y][x]){ stop=1; } k++; } // Tant que l'on a pas parcouru le tableau ou que l'on a pas trouvé tout les élements d'une cage while((i<9)&&(compteur[k-1]!=count)){ while((j<9)&&(compteur[k-1]!=count)){ if((cage[i][j]==cage[y][x])&&(tab[i][j]!=0)){ // Si on a trouvé un élement de la cage et qu'il est rempli somme+=tab[i][j]; // On ajoute sa valeur à la somme count++; if(somme>valeur[k-1]){ // Si la somme des cases est plus grande que la somme attendue return -1; } }else if((cage[i][j]==cage[y][x])&&(tab[i][j]==0)&&((i!=y)||(j!=x))){ // Si on trouve une case qui appartient à la cage est qui est vide return 1; } j++; } j=0; i++; } if(somme==valeur[k-1]){ // Si la somme est bien la somme attendue return 1; } return 0; } int test(int y, int x, int n) { int x0=0, y0=0; int count=1; // Vérifie si le nombre testé est déja sur la ligne while (x0 < 9 && count==1) { if (tab[y][x0] == n) { count=0; } x0++; } // Vérifie si le nombre testé est déja sur la colonne while (y0 < 9 && count==1) { if (tab[y0][x] == n) { count=0; } y0++; } // Vérifie si le nombre testé est déja dans le carré de 3 par 3 x0 = (x / 3) * 3; y0 = (y / 3) * 3; int i=0; while (i < 3 && count==1) { int j = 0; while (j < 3 && count==1) { if (tab[y0+i][x0+j] == n) { count=0; } j++; } i++; } return count; } void solve() { int y, x,n; for (y= 0; y < 9; y++) { // On parcour le tableau for (x = 0; x < 9; x++) { if (tab[y][x] == 0) { // Si on tombe sur une case non remplie for(n = 1; n < 10; n++){ int find=find_cage(x,y,n); if ((test(y, x, n)==1)&&(find==1)){ // Test si le nombre n peut aller dans la case tab[y][x] = n; // Mise à jour du tableau solve(); // On relance la fonction tab[y][x] = 0; // Remise à 0 de la case si mauvais choix }else if((test(y, x, n)==1)&&(find==-1)){ break; } } return; } } } for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { printf("%d", tab[i][j]); } printf("\n"); } exit(0); } int voisin(int i, int j, char name, int count, int teste[10][10]){ // Si l'élement testé ne fait pas parti de la cage if (cage[i][j]!=name){ return count; } count++; // On ajoute 1 au compteur si on détecte un élement de la cage teste[i][j]=1; // Indique que la case a déja été testé if((i!=0)&&(teste[i-1][j]!=1)){ // Si on est pas en haut du tableau, alors on regarde la case du dessus count=voisin(i-1 , j, name, count, teste) ; } if((j!=0)&&(teste[i][j-1]!=1)){ // Si on est pas sur le bord droit du tableau, alors on regarde la case de gauche count=voisin(i , j-1, name, count, teste ) ; } if((j!=8)&&(teste[i][j+1]!=1)){ // Si on est pas en haut du tableau, alors on regarde la case de droite count=voisin(i , j+1, name, count, teste ) ; } if((i!=8)&&(teste[i+1][j]!=1)){ // Si on est pas en bas du tableau, alors on regarde la case du dessous count=voisin(i+1 , j, name, count, teste ) ; } return count; } int main(){ for (int i = 0; i < 9; i++) { char grid_line[10]; char grid_cages[10]; scanf("%s%s", grid_line, grid_cages); fgetc(stdin); for(int j=0;j <9 ;j++){ // Récupération des valeurs des cases du sudoku if(grid_line[j]!='.'){ // Si la valeur est indiqué tab[i][j]=grid_line[j]-'0'; // On l'inscrit }else{ tab[i][j]=0; // Sinon on lui assigne automatiquement la valeur 0 } cage[i][j]=grid_cages[j]; // On crée un tableau avec les cages } } char cages[251]; scanf("%[^\n]", cages); int j; int i=0; int k=0; int y=0; int x=0; char stock_nb[10]; stock_nb[0]='\0'; stock_nb[1]='\0'; stock_nb[2]='\0'; int count_tot=0; int trouve=0; int xsave,ysave; while(cages[i]!='\0'){ // Tant que l'on a pas lu toute la ligne if(((cages[i]<='Z')&&(cages[i]>='A'))||((cages[i]<='z')&&(cages[i]>='a'))){ // Si on détecte une lettre j=2; while((cages[i+j]<='9')&&(cages[i+j]>='0')){ // On récupère la valeur correspondante stock_nb[j-2]=cages[i+j]; j++; } valeur[k]=atoi(stock_nb); count_tot++; lettre[k]=cages[i]; while(trouve!=1){ while((trouve!=1)||(x<9)){ if(lettre[k]==cage[y][x]){ trouve=1; xsave=x; ysave=y; } x++; } x=0; y++; } y=0; trouve=0; int teste[10][10]={0}; // Remplissage d'un tableau de 0 compteur[k]=voisin(ysave,xsave,cage[ysave][xsave],0, teste); // Création d'un tableau avec le nombre d'élement de chaque cage. stock_nb[0]='\0'; stock_nb[1]='\0'; stock_nb[2]='\0'; k++; } i++; } i=0; j=0; solve(); return 0; }
Présentation des tests
Afin de coder ce programme et de le valider, il a fallut passer plusieurs tests. Alors voici ci dessous les tests qui m’ont permi de résoudre ce problème.
Test 1
- Entrée
56..1..2. aabbccdde ..72..68. afghhiide ..2.87.15 jfggklmme ......3.9 jjgnklopp .7....2.. qqgnooorr 9.634.8.. stuuvwwxx 2.9..8... stuuvvwyz ..41.2... sAuuByyyz .8.4...3. CADDBEEFF a=12 b=17 c=4 d=14 e=15 f=13 g=19 h=7 i=10 j=16 k=10 l=13 m=10 n=15 o=15 p=13 q=11 r=11 s=18 t=3 u=28 v=15 w=20 x=8 y=22 z=12 A=11 B=13 C=6 D=9 E=10 F=5
- Sortie
568913427 197254683 342687915 851726349 473891256 926345871 219538764 734162598 685479132
Ce premier test permet de comprendre le problème et de tester notre code sur un cas simple.
Test 2
- Entrée
.1..65..7 abcccdeee 6.7....15 fbbgddhie .54..1..3 fjggdkhil ......... fjmgkknil 16..3.5.. ooppqqnrr ..8..2... sotuvqnnw 3......7. xxtuyyzzA ...3..... BCDDEFzAA .2.1..349 BBDGGGzzz a=9 b=16 c=17 d=21 e=18 f=12 g=18 h=15 i=12 j=12 k=15 l=5 m=9 n=19 o=10 p=6 q=12 r=17 s=5 t=13 u=15 v=1 w=4 x=7 y=11 z=33 A=12 B=17 C=9 D=10 E=7 F=4 G=14
- Résultat
============Test passe============ ==============Sortie============== 913865427 687243915 254791683 479586132 162437598 538912764 345629871 891374256 726158349 ==================================
Ce second test nécessite un minimum d’optimisation sur le code afin de s’assurer qu’il traite tout les cas possibles.
Test 3
- Entrée
........1 abbcdddef ....5.... agccchfff ...3..... ijjjhhkkl .......9. ijmmnnokp ..6...... qrrsttuvv ....9.7.. qwxsyuuzA .....3... qwxxyBBCA ......... qDxEEBBCC ..8..5... DDFFFBGHH a=14 b=9 c=17 d=22 e=3 f=20 g=3 h=9 i=6 j=21 k=19 l=7 m=9 n=11 o=4 p=6 q=16 r=15 s=11 t=11 u=10 v=9 w=9 x=20 y=16 z=5 A=5 B=22 C=19 D=17 E=11 F=15 G=3 H=11
- Résultat
============Test passe============ ==============Sortie============== 672489531 831752649 549316827 157238496 396547218 284691753 415873962 763924185 928165374 ==================================
Dans ce troisième test, on traite de nombreux cas, ce qui permet de corriger les erreurs encore présentes dans le code, mais qui n’affectaient pas des tests moins importants. Cela permet également de se pencher sur l’optimisation du programme.
Test 4
- Entrée
......... abbcccddd ......... aabefghii ......... jkkefghil ......... jmnnnooil ......... mmmppqqrl ......... sttpuuqrr ......... svwwwxxxy ......... zvvABBCCy ......... zDDAAEEFF a=7 b=23 c=13 d=13 e=5 f=10 g=17 h=11 i=28 j=12 k=8 l=16 m=20 n=9 o=11 p=22 q=14 r=8 s=15 t=13 u=4 v=17 w=11 x=16 y=8 z=4 A=19 B=10 C=7 D=13 E=15 F=6
Mon code ne passe pas le dernier test. En effet, lorsqu’il n’y a aucun nombre au départ, mon code devient inefficace. En effet, lorsque j’execute mon code sur machine, au bout d’un très long moment, on arrive à obtenir une solution. Malgré de nombreux essais, je n’ai malheuresement pas réussi à suffisemment l’améliorer afin qu’il fonctionne dans le temps imparti.
Remarque: N’ayant pas réussi le dernier test, je n’ai pas pu présenter la solution d’un autre utilisateur.
Valgrind
Afin d’être certain de ne pas avoir de problèmes de mémoire et autres, que le compilateur n’aurait pas détecté, on utilise valgrind.
valgrind ./killer < killertest/test3
Cliquez ici pour agrandir
Le programme présenté passe bien valgrind pour les 3 premiers tests.
Bilan de ce travail
Malgré que je n’ai pas réussi à aboutir à un code passant tout les tests, ce travail a tout de même eu un certain nombre d’aspect positifs. En effet, la conception de ce programme m’a forcé à réfléchir à la complexité temporelle de mon algorithme. J’ai donc dû l’optimiser un minimum afin qu’il puisse passer les premiers tests. De plus, les fonctions solve et voisin m’ont permi d’approfondir mes connaissances au niveau des mécanismes de récursion.