2025-Epita-Info-MpPcPsi-Corrigé

2025 – EPITA – Sciences numériques MP-PC-PSI-PT-TSI

Le sujet se compose d’un problème et d’un questionnaire à choix multiples. Le questionnaire à choix multiples devra être inséré dans votre copie.

Les fonctions à produire dans ce sujet devront être rédigées en langage Python et ne pas avoir recours à l’usage de bibliothèques. Il est possible d’écrire des fonctions auxiliaires non explicitement demandées à condition de les documenter et de les définir avant d’en faire usage.

Problème – Le KenKen

Le KenKen est un jeu constitué d’une grille carrée de n cases de coté (en général {n\geq3}) ; n sera alors appelé taille du jeu. La grille comporte des ensembles de cases, délimités par un contour plus épais, que l’on appellera des zones.

Dans chacune de ces zones est précisé une contrainte : un nombre, généralement suivi d’un opérateur. Ces contraintes et leur format seront détaillés plus loin.

Voici l’exemple d’une grille de taille 4 :

Figure 1

Le but du jeu est de remplir chaque case de la grille avec un entier, de sorte que chaque ligne et chaque colonne comporte chaque entier de 1 à n et de façon à ce que les entiers présents dans les cases d’une même zone respectent la contrainte de la zone définie comme suit :

— si la zone contient une seule case : la contrainte est un simple nombre indiquant la valeur que doit contenir cette case ;

— si la zone comporte au moins deux cases : la contrainte est un chiffre r suivi d’un opérateur parmi +, ×, − et / dont la signification est donnée par la tableau ci-après : + : la somme des entiers dans les cases de la zone doit donner {r}. × : le produit des entiers dans les cases de la zone doit donner {r}. − : la différence des deux entiers des cases de la zone doit donner {r} (dans un sens ou dans l’autre).

Attention, cette contrainte ne s’applique qu’à des zones de 2 cases. / le quotient des deux entiers des cases de la zone doit donner r (dans un sens ou dans l’autre). Attention, cette contrainte ne s’applique qu’à des zones de 2 cases.

Une grille de KenKen bien formée ne comporte qu’une seule solution satisfaisant ces contraintes. Voici la solution de l’exemple précédent :

Figure 2

On choisit de représenter chaque zone par un dictionnaire contenant les champs val, op et cases ; val et op contiennent respectivement le nombre et l’opérateur associés à la zone tandis que cases est la liste des cases de la zone, représentées par le couple de leurs coordonnées.

On choisit de repérer les cases en ligne puis colonne en commençant par la case supérieure gauche. Ainsi, pour une grille de taille n, la case supérieure gauche aura pour coordonnées (0, 0), la case inférieure gauche (n − 1, 0) et la case inférieure droite : (n − 1, n − 1).

Pour les zones ne comportant qu’une seule case, le champ op sera une chaîne de caractères vide.

Dans l’exemple précédent, la zone comportant la case de coordonnées (0, 0) pourra être représentée par le dictionnaire {« val »:12, « op »: »× », « cases »: [(0,0),(1,0),(1,1)]} et la zone réduite à la case de coordonnées (0, 3) pourra être représentée par : {« val »:1, « op »: » », « cases »: [(0,3)]}.

Un jeu de KenKen sera alors intégralement décrit par sa taille n et par la liste des zones qui le composent.

De plus, on représente l’état du remplissage d’une grille par une liste de listes d’entiers de sorte que la sous-liste d’indice k corresponde à la valeur des cases situées sur la ligne k (parcourues de gauche à droite). Les cases non remplies de la grille se verront attribuer la valeur 0.

Dès lors, la grille de l’exemple dont on aurait effectué un remplissage partiel (en ne remplissant que la première ligne) est représentée par la liste [[2,3,4,1],[0,0,0,0],[0,0,0,0],[0,0,0,0]].

Autour des contraintes

Question 1 Écrire une fonction test_somme qui, étant donné une liste d’entiers et un entier v, retourne True si la somme des éléments de la liste vaut v et False sinon.
Cliquer ici pour voir (ou cacher) la réponse
Pour voir la suite de ce contenu, vous devez : Pour poursuivre votre exploration, vous pouvez :
Question 2 Écrire une fonction test_quotient qui, étant donné une liste de deux entiers non nuls et un entier v, retourne True si la valeur v peut-être obtenue en calculant le quotient des deux entiers (dans un sens ou dans l’autre) et False sinon.
Cliquer ici pour voir (ou cacher) la réponse
Pour voir la suite de ce contenu, vous devez : Pour poursuivre votre exploration, vous pouvez :

On considère à présent disposer des fonctions suivantes :

— test_produit qui, étant donné une liste d’entiers et un entier v, retourne True si le produit des éléments de la liste vaut v et False sinon ;

— test_difference qui, étant donné une liste de deux entiers et un entier v, retourne True si la valeur v peut-être obtenue en calculant la différence des deux entiers (dans un sens ou dans l’autre) et False sinon.

Question 3 Écrire une fonction test_zone qui, étant donné une grille remplie et une zone, retourne True si la contrainte de zone est respectée et False sinon.
Cliquer ici pour voir (ou cacher) la réponse
Pour voir la suite de ce contenu, vous devez : Pour poursuivre votre exploration, vous pouvez :
Question 4 Écrire une fonction test_grille qui, étant donné une grille (complètement ou partiellement remplie) et une liste de zones, retourne True si la grille respecte les contraintes de chacune des zones, et False sinon.

On considèrera que cette fonction ne sera appelée que pour des zones portant sur des cases remplies de la grille.

Cliquer ici pour voir (ou cacher) la réponse
Pour voir la suite de ce contenu, vous devez : Pour poursuivre votre exploration, vous pouvez :
Question 5 Écrire une fonction test_conflit qui, étant donné une grille dont les i premières lignes sont supposées remplies et l’entier i, retourne False si l’un au moins des éléments de la ligne i est déjà présent parmi les éléments des lignes précédentes situés sur la même colonne que lui.

Dans le cas contraire la fonction renverra True.

Cliquer ici pour voir (ou cacher) la réponse
Pour voir la suite de ce contenu, vous devez : Pour poursuivre votre exploration, vous pouvez :

On souhaite à présent pouvoir regrouper les contraintes de zones selon le plus grand numéro de ligne des cases qu’elles comportent.

Question 6 Écrire une fonction ligne_max qui, étant donné une zone, retourne le plus grand numéro de ligne parmi ceux des cases qu’elle comporte.

On interdit pour cette question l’usage des fonctions min et max de Python.

Cliquer ici pour voir (ou cacher) la réponse
Pour voir la suite de ce contenu, vous devez : Pour poursuivre votre exploration, vous pouvez :
Question 7 Écrire une fonction zones_par_ligne_max qui, étant donné la taille n d’un jeu et la liste de ses zones, retourne une liste comportant n sous-listes.

La sous-liste d’indice k sera la liste des zones du jeu telles que le numéro de ligne maximal des cases qui la composent est égal à k.

Cliquer ici pour voir (ou cacher) la réponse
Pour voir la suite de ce contenu, vous devez : Pour poursuivre votre exploration, vous pouvez :

Afin de vérifier si les zones sont bien formées, on souhaite construire une fonction permettant de déterminer si une liste de cases est connexe ; c’est-à-dire s’il est possible de se déplacer entre n’importe quelles cases de la liste en passant uniquement par des cases de cette liste et au moyen de déplacements horizontaux et/ou verticaux.

Question 8 Écrire une fonction test_connexite qui, étant donné une liste de cases (exprimées par leurs coordonnées), retourne True lorsqu’elle est connexe et False sinon.

Cette fonction pourra opérer comme si elle parcourait le graphe formé par les cases de la liste dont seraient adjacentes les cases se touchant horizontalement ou verticalement.

Cliquer ici pour voir (ou cacher) la réponse
Pour voir la suite de ce contenu, vous devez : Pour poursuivre votre exploration, vous pouvez :

Itérateur de permutations

On rappelle qu’une permutation de {\llbracket1,n\rrbracket} est une bijection de {\llbracket1,n\rrbracket} dans {\llbracket1,n\rrbracket}. On choisit de représenter une permutation {\sigma} de {\llbracket1,n\rrbracket} par la liste d’entiers {[\sigma(1),\ldots,\sigma(n)]}.

Ainsi les listes comportant une et une seule fois chaque entier de 1 à n représentent toutes les permutations de {\llbracket1,n\rrbracket}. On considère l’ordre des permutations de {\llbracket1,n\rrbracket} comme étant l’ordre lexicographique des listes qui les représentent : {[a_1]\lt[b_1]\iff a_1\lt b_1.} {\begin{array}{l}\forall p\geq2,\ [a_1,\ldots,a_p]\lt[b_1,\ldots,b_p]\\[8pt]\iff a_1\lt b_1\text{ ou }\left\{\begin{array}{l}a_1=b_1,\\[8pt][a_2,\ldots,a_p]\lt[b_2,\ldots,b_p].\end{array}\right.\end{array}}Ainsi {[2,4,1,3]\lt[2,4,3,1]\lt[3,1,2,4]\lt[3,1,4,2]}.

Question 9 Donner les 3 permutations qui suivent [3, 1, 4, 2] dans l’ordre lexicographique.
Cliquer ici pour voir (ou cacher) la réponse
Pour voir la suite de ce contenu, vous devez : Pour poursuivre votre exploration, vous pouvez :
Question 10 Écrire une fonction identite prenant en paramètre un entier n et retournant la permutation [1, . . . , n].
Cliquer ici pour voir (ou cacher) la réponse
Pour voir la suite de ce contenu, vous devez : Pour poursuivre votre exploration, vous pouvez :
Question 11 Écrire une fonction inverse prenant en paramètres une permutation p de {\llbracket1,n\rrbracket} et 2 entiers i et j. Cette fonction modifiera la permutation p en inversant l’ordre des éléments dont l’indice des cases appartient à {\llbracket i,j\rrbracket}.

Cette fonction ne fera rien dans le cas où {i\geq j} et on supposera que cette fonction n’est appelée que lorsque i et j sont dans {\llbracket1,n\rrbracket}.

Cliquer ici pour voir (ou cacher) la réponse
Pour voir la suite de ce contenu, vous devez : Pour poursuivre votre exploration, vous pouvez :
Question 12 Écrire une fonction indice_pps prenant comme paramètres une permutation p de {\llbracket1,n\rrbracket} et un entier i de {\llbracket1,n\rrbracket}. Cette fonction renverra, parmi les cases d’indice strictement supérieur à i et de valeur strictement supérieure à p[i], l’indice de celle de valeur minimale.

On suppose que cette fonction ne sera appelée que lorsqu’un tel indice existe.

Cliquer ici pour voir (ou cacher) la réponse
Pour voir la suite de ce contenu, vous devez : Pour poursuivre votre exploration, vous pouvez :
Question 13 Écrire une fonction suivante prenant en paramètre une permutation p de {\llbracket1,n\rrbracket}. Cette fonction modifiera la permutation pour la transformer par la suivante dans l’ordre lexicographique.

Lorsque la permutation suivante existe, la fonction renverra la valeur True ; dans le cas contraire, elle renverra la valeur False sans modifier la permutation.

Cliquer ici pour voir (ou cacher) la réponse
Pour voir la suite de ce contenu, vous devez : Pour poursuivre votre exploration, vous pouvez :

Résolution

On cherche à présent à résoudre des jeux de KenKen. On propose d’essayer de remplir ligne après ligne la grille en commençant par attribuer à la première ligne, la première permutation des entiers de {\llbracket1,n\rrbracket} dans l’ordre lexicographique.

On fait de même pour la seconde ligne, puis les suivantes, en veillant à vérifier à chaque étape que le remplissage partiel de la grille est conforme aux contraintes du jeu : les zones portant sur les cases remplies doivent voir leurs contraintes vérifiées et 2 mêmes entiers ne peuvent se trouver sur une même colonne.

En cas d’échec, on essaie la permutation suivante de la ligne faisant défaut. Si aucune des permutations ne convient, c’est que l’une des lignes précédentes est en cause et on essaie alors la permutation suivante de la ligne qui précède celle faisant défaut.

Question 14 Écrire une fonction récursive continue_remplissage qui, étant donné une grille dont les i premières lignes sont intégralement remplies, la liste des listes de zones rangées par indice de ligne maximal de ses cases, et l’entier i, essaie tous les remplissages possibles des lignes suivant i jusqu’à aboutir à une solution.

Cette fonction modifiera la grille en la remplissant par la solution obtenue. La fonction renverra True si elle aboutit à un remplissage valide et False sinon.

Cliquer ici pour voir (ou cacher) la réponse
Pour voir la suite de ce contenu, vous devez : Pour poursuivre votre exploration, vous pouvez :
Question 15 Écrire une fonction resoudre prenant en paramètres la taille n d’un jeu et la liste de ses zones.

Cette fonction retournera l’unique solution au problème et None si elle ne l’a pas trouvée.

Cliquer ici pour voir (ou cacher) la réponse
Pour voir la suite de ce contenu, vous devez : Pour poursuivre votre exploration, vous pouvez :
Question 16 Décrire le pire des cas en termes de découpage des formes des zones pour cette stratégie de résolution.
Cliquer ici pour voir (ou cacher) la réponse
Pour voir la suite de ce contenu, vous devez : Pour poursuivre votre exploration, vous pouvez :
Question 17 Écrire une fonction nb_solutions qui, étant donné la taille n d’un jeu possiblement mal formé (en ce sens qu’elle pourrait admettre plusieurs solutions) et la liste de ses zones, retourne le nombre de solutions respectant les contraintes du jeu.
Cliquer ici pour voir (ou cacher) la réponse
Pour voir la suite de ce contenu, vous devez : Pour poursuivre votre exploration, vous pouvez :

Gestion d’un site en ligne

On cherche à présent à élaborer la base de données d’un site internet proposant des grilles de KenKen.

On choisit de stocker les informations des comptes utilisateurs, la liste des grilles disponibles et les informations des temps de résolution des grilles par les utilisateurs. On répartit ces informations en 3 tables dont on présente des extraits ci-après.

La première ligne représente les noms des champs et les suivantes les enregistrements. La clé primaire de chaque table sera indiquée en première colonne. Les clés étrangères seront soulignées.

Une première table utilisateurs permet de stocker les informations personnelles des utilisateurs :

Figure 3

Une seconde table grilles permet de stocker les grilles disponibles pour les joueurs, en particulier leur taille et leur niveau de difficulté.

Figure 4

Une troisième table parties permet de déterminer les temps de résolution des grilles par les joueurs.

On stocke l’identifiant idUtilisateur de l’utilisateur concerné dans u_idUtilisateur qui sera donc une clé étrangère ; de même pour la clé étrangère g_idGrille permettant d’identifier la grille concernée.

Le champ resolue contient 0 pour une grille non encore résolue et 1 sinon tandis que debut (respectivement fin) contient l’instant de début (respectivement de fin) de résolution de la grille par l’utilisateur (exprimé en secondes écoulées depuis le 1er janvier 1970 à minuit).

Enfin le champ note comporte un indice de satisfaction de la grille que fournit le joueur après avoir résolu la grille. Les champs non remplis se verront attribuer la valeur NULL.

Figure 5

Question 18 Écrire une requête SQL retournant le meilleur temps de résolution (en secondes) obtenu pour la grille d’identifiant 159.
Cliquer ici pour voir (ou cacher) la réponse
Pour voir la suite de ce contenu, vous devez : Pour poursuivre votre exploration, vous pouvez :
Question 19 Écrire une requête SQL qui donne la meilleure note attribuée à une grille par la joueuse « Elizabeth Harmon ».
Cliquer ici pour voir (ou cacher) la réponse
Pour voir la suite de ce contenu, vous devez : Pour poursuivre votre exploration, vous pouvez :
Question 20 Écrire une requête SQL donnant les surnoms et les temps de résolution des trois joueurs les plus rapides parmi ceux ayant résolu la grille d’identifiant 421.

On ordonnera les réponses par temps croissant de résolution de la grille.

Cliquer ici pour voir (ou cacher) la réponse
Pour voir la suite de ce contenu, vous devez : Pour poursuivre votre exploration, vous pouvez :
Question 21 Écrire une requête SQL donnant les noms, prénoms et surnoms des joueurs ayant donné la meilleure note à une grille de difficulté 2 (plus une note est élevée, meilleure elle est).
Cliquer ici pour voir (ou cacher) la réponse
Pour voir la suite de ce contenu, vous devez : Pour poursuivre votre exploration, vous pouvez :