Corrigé EVA programmeur 2010, langage Java.
Transcription
Corrigé EVA programmeur 2010, langage Java.
MINISTÈRE DE L’ÉCOLOGIE, DU DÉVELOPPEMENT DURABLE, DES TRANSPORTS ET DU LOGEMENT EXAMEN PROFESSIONNEL DE VÉRIFICATION D’APTIDUDE AUX FONCTIONS DE PROGRAMMEUR ___________ Session 2010 Correction langage Java Langage: Java Durée : 5 heures Coefficient: 4 Notation: sur 20 Nombre de pages du sujet : 4 (y compris cette page) Remarques générales 1 – le sujet comporte deux parties : • un sujet général commun aux différents langages noté sur 6 (temps estimé 1h30) • un sujet spécifique au langage choisi noté sur 14 (temps estimé 3h30) 2 – si éventuellement il vous manquait des informations particulières pour développer votre sujet, il vous revient de retenir les hypothèses adaptées à votre solution, en les explicitant clairement. 3 – aucun document ou matériel électronique (calculette, ordinateur …) n’est autorisé. EVA Programmeur 2010- Corrigé du sujet Java page 1/11 1ère partie 1. Sujet commun L'algorithme proposé est le suivant : 1. Calcul de la longueur L du mot de passe. 2. Remplissage aléatoire dans le mot de passe des caractères requis : - A minuscules. - B majuscules. - C chiffres. - D caractères spéciaux. 3. Remplissage aléatoire des caractères restants en utilisant l'ensemble de l'alphabet donné. Le détail de cet algorithme est donné ci-dessous. PRÉALABLE Les variables suivantes seront utilisés dans l'algorithme : - P : mot de passe calculé, sous forme d'un tableau de caractères. - L : Longueur du mot de passe P. - MINUSCULES[] : tableau de 26 caractères contenant les minuscules. - MAJUSCULES[] : tableau de 26 caractères contenant les majuscules. - CHIFFRES[] : tableau de 10 caractères contenant les chiffres. - SPECIAUX[] : tableau de S caractères contenant les caractères spéciaux. - TOUS[] : tableau de tous caractères possibles (concaténation des tableaux ci-dessus). Conventions de notation prises : - Un tableau de taille N est indexé de 0 à N-1. - La valeur d'un tableau T à l'emplacement I est noté T[i]. DÉTAIL DU CALCUL DE LA LONGUEUR L DU MOT DE PASSE La longueur doit satisfaire aux critères suivants : - L est d'une longueur aléatoire entre Lmin et Lmax. - L doit valoir au minimum de A+B+C+D. - L doit valoir au maximum de Lmax. Elle est calculée comme suit : L = Lmin +randN(Lmax - Lmin +1) Si L < A+B+C+D alors L = A+B+C+D. Où : randN(n) désigne une fonction qui renvoi un nombre entier aléatoire entre 0 inclus et n exclus : Fonction randN(n : entier) : int randN(n) = Partie_Entiere(n.rand()) DÉTAIL DU REMPLISSAGEALÉATOIRE DANS LE MOT DE PASSE DES CARACTÈRESREQUIS L'insertion des caractères obligatoires se fait comme suit : Initialiser P avec des valeurs nulles. ajouterAleatoirement(A, MINUSCULES[]). ajouterAleatoirement(B, MAJUSCULES[]). ajouterAleatoirement(C, CHIFFRES[]). ajouterAleatoirement(D, SPECIAUX[]). Où : La fonction ajouterAleatoirement(n, alphabet) ajoute aléatoirement n caractères à P, EVA Programmeur 2010- Corrigé du sujet Java page 2/11 parmi ceux fournis dans le tableau "alphabet". Fonction ajouterAleatoirement(n : entier, alphabet : tableau de caractères) : void Pour i=1 à n : Prendre une position j libre au hasard dans P : j = libreAleatoire(). Prendre un caractère c au hasard dans alphabet : c = randA(alphabet) Affecter à P le caractère trouvé à la position trouvée : P[j] = c. Où : La fonction libreAleatoire() recherche un emplacement libre, au hasard, dans P. Pour cela un indice est pris au hasard dans P et l'on boucle sur P depuis cet indice jusqu'à trouver une valeur nulle (disponible). Fonction libreAleatoire() : int Tirer une position i au hasard dans P : i = randN(L). Créer indice iter = 0 pour ne pas parcourir plus d'une fois le tableau Tant que P[i] non nul (déjà pris) et iter<L : iter = iter + 1 Si i = L-1 alors : i = 0 (retourner en début de tableau une fois au bout). Sinon : i = i+1 Si P[i] est nul (emplacement libre trouvé) Alors : Renvoyer i. Sinon : !!Indice libre non trouvé, Erreur (ne se produit pas car A+B+C+D ≤ L.)!! La fonction randA(alphabet) tire un caractère au hasard dans le tableau alphabet. Fonction randA(alphabet : tableau de caractères) : char Tirer une position au hasard dans alphabet : i = randN(longeur(alphabet)) Renvoyer alphabet[i] DÉTAIL DU REMPLISSAGE ALÉATOIRE DES CARACTÈRES RESTANTS C'est l'étape la plus simple. Il suffit de parcourir le tableau P à la recherche des éléments non remplis (nuls) et d'y affecter un élément quelconque sur l'alphabet complet (TOUS[]) du mot de passe : Pour i=0 à L-1 Si P[i] est nul Alors : P[i] = randA(TOUS[]) ALGORITHME COMPLET En reprenant les éléments ci-dessus, l'algorithme général est : // Longueur du mot de passe L = Lmin +randN(Lmax - Lmin +1) Si L < A+B+C+D alors L = A+B+C+D. // Caractères requis Pour i=1 à A: Prendre une positionj libre au hasard dans P j: = libreAleatoire(). Prendre une minuscule au hasard : c = randA(MINUSCULES[]) P[j] = c. Pour i=1 à B: EVA Programmeur 2010- Corrigé du sujet Java page 3/11 Prendre une positionj libre au hasard dans P j: = libreAleatoire(). Prendre une majuscule au hasard : c = randA(MAJUSCULES[]) P[j] = c. Pour i=1 à C: Prendre une positionj libre au hasard dans P j: = libreAleatoire(). Prendre un chiffre au hasard : c = randA(CHIFFRES[]) P[j] = c. Pour i=1 à D: Prendre une positionj libre au hasard dans P j: = libreAleatoire(). Prendre un caractère spécial au hasard : c = randA(SPECIAUX[]) P[j] = c. // Caractères restants Pour i=0 à L-1 Si P[i] est nul Alors : P[i] = randA(TOUS[]) (les fonctions randA et libreAleatoire ne sont re-détaillées pour plus de lisibilité.). EVA Programmeur 2010- Corrigé du sujet Java page 4/11 2ème partie 1. Questions de syntaxe 1.1 Une variable système, ou d'environnement, Java peut être définie au démarrage d'une application comme argument de la machine virtuelle Java, sous la forme -DmaVariable=maValeur, où maVariable et maValeur représentent respectivement la variable et sa valeur: java -DmaVariable=maValeur <autres arguments> . 1.2 Oui, il suffit de n'utiliser que des directives compatibles avec la version 1.4 du langage et de compiler cette classe avec les options -source " 1.4 -target 1.4 " 1.3 Une variable de type ThreadLocal est accessible et modifiable par l'ensemble des objets instanciés dans un thread java. Sa portée est celle du thread courant, chaque thread ayant sa propre valeur de cette variable. Elle permet par exemple de partager une valeur entre tous les objets d'un même thread sans avoir à la transmettre en argument ni à la référencer explicitement. 1.4 Cet extrait de code initialise un objet de type Propriété (liste de clé/valeurs) par lecture du fichier app.cfg dans le classpath de l'application. Le caractère '/' signifie que ce fichier est à la racine du classpath. 2. Problème: Le sudoku 2.0 PRÉALABLE: MODÉLISATIONGÉNÉRALEDE LA CLASSE SUDOKU Le problème posé conduit à la conception de la classe Sudoku, dont la structure générale est la suivante en Java. Sudoku // valeurs courantes du sudoku - sudoku : int[][] = new int[9][9]; + litSudoku(fichier : string) : void + valeurPermise(k : int, x : int, y : int) : boolean + valeursPermises(x : int, y : int) : boolean[] + resoudSudoku(): bolean + ecritSudoku(fichier : string) : void La gestion des exceptions sera simplifiée et réduite aux seules exigences de l'énoncé. 2.1 LECTURE DU FICHIER CONTENANTLE SUDOKU NON RÉSOLU Le format de fichier retenu est simple : un fichier de 9 lignes à 9 chiffres, ces chiffres allant de 0 à 9. Dans l'exemple fourni, les deux premières lignes du fichier sont les suivantes : 006095047 701040000 L'algorithme de lecture est le suivant : Fonction lireSudoku(fichier :Fichier): void Ouvrir le fichier Pour chaque ligne d'indice y du fichier : Lire chaque caractère d'indice x et l'enregistrer dans sudoku[x][y] EVA Programmeur 2010- Corrigé du sujet Java page 5/11 !!Erreur si caractères non compris dans [0-9] !! !!Erreur si plus ou moins de 9 caractères. !! !!Erreur si plus ou moins de 9 lignes. !! Fermer le fichier. Implémentation Java: public void litSudoku(String fichier) throws Exception { String ligne; // la ligne courante int y = 0; // la colonne courante BufferedReader in = null; try { in = new BufferedReader(new FileReader(fichier)); while ((ligne = in.readLine()) != null) { char[] valeurs = ligne.toCharArray(); if(valeurs.length != 9) { throw new Exception("Nombre de colonnes incorrect"); } for (int x=0; x<9; x++) { try { sudoku[x][y] = Integer.parseInt( Character.toString(valeurs[x])); } catch (NumberFormatException e) { throw new Exception("valeur invalide"); } } y++; } if(y != 9) { throw new Exception("Nombre de ligne incorrect"); } } finally { in.close(); } } 2.2 TEST DE LA VALEUR K À L'EMPLACEMENT(X,Y) L'algorithme consiste à parcourir la colonne x et la ligne y à la recherche de la valeur k. Si non trouvée, il faut ensuite parcourir la région (carré 3x3) associée à cette case. Fonction valeurPermise(k: int, x : int, y : int) : boolean // recherche sur la colonne x et la ligne y EVA Programmeur 2010- Corrigé du sujet Java page 6/11 Pour i=1 à 9: Si sudoku[x][i] = k ou sudoku [i][y] =: k Retourner faux (sortie). // recherche dans la région, coordonnées du coin supérieur gauche bx = 3 * PartieEntiere(x/3) by = 3 * PartieEntiere(y/3) Pour i=1 à 9: Pour j = 1 à 9: Si sudoku [i][y] = k Alors: Retourner faux (sortie) Retourner vrai Implémentation Java: private boolean valeurPermise(int k, int x, int y) { // parcours de la colonne x et de la ligne y for (int i = 0; i < 9; i++) { if((sudoku[x][i] == k) || (sudoku[i][y] == k)) { return false; } } // parcours région : bx, by coin supérieur gauche du carré int bx = 3 * (x / 3), by = 3 * (y / 3); for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if(sudoku[bx + i][by + j] == k) { return false; } } } return true; } 2.3 RECHERCHE DE TOUTES LES VALEURS POSSIBLES À UNE POSITION (X,Y) Une solution rapide, non optimale, est de tester toutes les valeurs de 0 à 9 à l'aide de la fonction précédente Fonction valeursPermises(x : int, y : int) : boolean[] (solution 1, non optimale) V : tableau à 10 élements de boolean. V[i] = vrai si i est possible, V[i] = faux sinon. Pour i =0 à 9: V[i] = estPermis(i, x, y) Retourner V Une solution plus optimale consiste à remplir le tableau de valeurs permises directement durant la recherche de contraintes sur une valeur k (fusion des questions 2.2 et 2.3). Fonction valeursPermises(x : int, y : int) : boolean[] (solution 2, optimisé) EVA Programmeur 2010- Corrigé du sujet Java page 7/11 V : tableau à 10 élements de boolean. V[i] = vrai si i est possible, V[i] = faux sinon. Pour i= 0 à 9: V[i] = vrai // par défaut toute valeur est supposée permise // recherche ligne et colonne Pour i = 1 à 9: V[sudoku[x][i]] = faux // valeur sudoku[x][i] prise sur la colonne x V[sudoku[i][y]] = faux // valeur sudoku[i][y] prise sur la ligne y // recherche région Calculer les coordonnées du coin supérieur gauche de la région de :(x,y) bx = 3 * PartieEntiere(x/3) by = 3 * PartieEntiere(y/3) Pour i=1 à 9: Pour j = 1 à 9: V[sudoku[bx + i][by + j]] = faux // valeur à cet emplacement prise Retourner V Implémentation Java private boolean[] valeursPermises(int x, int{ y) // tableau indiquant si une valeur, par son indice, est permise. boolean permis[] = new boolean[10]; // par défaut toutesles valeurs sont permises for (int i = 0; i < 10; i++) { permis[i] = true; } // parcours de la colonne x et de la ligne y for (int i = 0; i < 9; i++) { permis[sudoku[x][i]] = false; permis[sudoku[i][y]] = false; } // bx, by coin supérieur gauche du carré contenant x, y int bx = 3 * (x / 3), by = 3 * (y / 3); // Mise à jour de digits pour le carré for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { permis[sudoku[bx + i][by + j]] = false; } } return permis; } 2.4 RÉSOLUTION DU SUDOKU EVA Programmeur 2010- Corrigé du sujet Java page 8/11 La méthode retenue ici pour résoudre le sudoku passe par la récursivité : Fonction resoudSudoku(): void [R] Rechercher la première case libre (égale à 0) du sudoku S'il existe une valeur possible sur la case trouvée : Placer la première valeur de valeursPermises sur cette case. Passer en récursivité depuis [R] avec les nouvelles valeurs du sudoku. Sinon : Revenir une étape en arrière avec la valeur permise suivante. Implémentation Java: public boolean resoudSudoku() { int coord[] =chercheCaseVide (); int x = coord[0]; int y = coord[1]; if (x == -1) { // toutes les cases sont remplies : sudoku résolu. return true; } boolean[] possibilites = valeursPermises(x, y); // On essaye pour la case x, y les valeurs possibles de 1 à 9 for (int i = 1; i <= 9; i++) { if (possibilites[i]) { sudoku[x][y] = i; // On tente la résolution avec cette valeur sur la case x, y if (resoudSudoku()) { return true; } else { // pas de solution possible : revenir en arrière sudoku[x][y] = 0; } } } return false; } Ce code fait appel à la méthode chercheCaseVide() qui donne les coordonnées de la première case vide (égale à 0) par parcours direct du tableau. Si aucune case n'est trouvée, cette méthode retourne (-1,-1) : private int[] chercheCaseVide() { for (int x = 0; x < 9; x++) { EVA Programmeur 2010- Corrigé du sujet Java page 9/11 for (int y = 0; y < 9; y++) { if (sudoku[x][y] == 0) { return new int[]{x, y}; } } } return new int[]{-1, -1}; } Le programme général demandé dans cette question est alors : Sudoku sudoku = new Sudoku() ; sudoku.litSudoku(); sudoku.resoudSudoku(); sudoku.ecritSudoku(); La méthode ecritSudoku() est décrite ci-après. 2.5 ÉCRITURE DU SODOKU DANS UN FICHIER Il s'agit de l'opération inverse à la lecture du fichier : Fonction ecrireSudoku(fichier : Fichier): void Ouvrir le fichier Pour chaque ligne y=1 à 9 du sudoku Pour chaque colonne x=1 à 9 du sudoku : Écrire sudoku[x][y] dans le fichier. Écrire saut de ligne Fermer le fichier Implémentation Java: public void ecritSudoku(String fichier) throws Exception { BufferedWriter out = null; try { out = new BufferedWriter(new FileWriter(fichier)); for (int y = 0; y < 9; y++) { for (int x = 0; x < 9; x++) { out.append("" + sudoku[x][y]); } out.append("\n"); } out.append("\n"); } finally { out.close(); } EVA Programmeur 2010- Corrigé du sujet Java page 10/11 } ♦♦♦ ♦ EVA Programmeur 2010- Corrigé du sujet Java page 11/11