Un corrigé pédagogique pour comprendre et justifier vos réponses. Les conseils de rédaction ne constituent pas un barème officiel détaillé.
Exercice 1 · 6 points
Processus : états, interblocage et ordonnancement tourniquet
Le système d’exploitation répartit le temps d’un processeur entre des processus issus d’une ou plusieurs applications. Dans le modèle à un seul cœur utilisé ici, un processus s’exécute à un instant donné, mais les alternances rapides donnent une impression de simultanéité. Interrompre un processus pour en exécuter un autre s’appelle la préemption.
La figure 1 comporte un état à gauche recevant « création », un à droite menant à « fin », et un état en bas. Une flèche gauche→droite est à nommer, la flèche droite→gauche est déjà nommée préemption, les flèches droite→bas et bas→gauche sont à nommer.
Question 1
#Compléter le schéma avec élu, prêt, bloqué, élection, blocage et déblocage.
Indice
Le processus qui peut se terminer est celui qui est en train de s’exécuter.
Comprendre la correction
| Élément du schéma | Terme à placer |
|---|---|
| État après création (à gauche) | Prêt |
| État avant fin (à droite) | Élu |
| État inférieur | Bloqué |
| Prêt → élu | Élection |
| Élu → prêt | Préemption, déjà indiquée |
| Élu → bloqué | Blocage |
| Bloqué → prêt | Déblocage |
Prêt signifie apte à s’exécuter mais en attente de processeur. Élu signifie actuellement exécuté. Bloqué signifie en attente d’une ressource ou d’un événement. Un déblocage ne donne pas automatiquement le processeur : il remet le processus parmi les prêts.
P1 gère l’interface (listes de morceaux, lecture/pause), P2 télécharge la musique et remplit un cache, P3 décode le son. L’utilisateur cache l’interface en mettant l’application en arrière-plan ; P2 attend la carte Wi-Fi ; P3 décode et envoie le flux au système.
Question 2
#Donner les états de P1, P2 et P3 dans la situation de streaming décrite.
Indice
Une fenêtre en arrière-plan ne définit pas à elle seule un état du processus.
Comprendre la correction
P2 est bloqué en attendant l’accès à la carte Wi-Fi ; P3 est élu puisqu’il effectue le décodage au processeur. Pour P1, la lecture attendue est prêt s’il reste exécutable mais n’a pas le processeur. Le seul fait que sa fenêtre soit cachée ne suffit toutefois pas, dans un système réel, à déterminer son état : un processus d’interface attendant un événement pourrait être bloqué. Il faut donc annoncer l’hypothèse « P1 reste prêt à s’exécuter » plutôt que confondre visibilité de la fenêtre et état système.
Question 3
#Quand un interblocage de processus peut-il survenir ?
Indice
Il faut une dépendance circulaire persistante, pas seulement un processus temporairement bloqué.
Comprendre la correction
Plusieurs processus attendent indéfiniment des ressources détenues par d’autres processus du même groupe, sans qu’aucun puisse avancer jusqu’à la libération attendue. Avec des ressources exclusives, un cycle d’attente peut ainsi bloquer tout le groupe : P1 garde A et attend B, pendant que P2 garde B et attend A. Une simple attente d’une ressource qui sera libérée normalement n’est pas un interblocage.
GPU, MIC, CAM et CAL sont chacune utilisables par un seul processus à la fois. Ordres des opérations :
| P1 | P2 | P3 | P4 |
|---|---|---|---|
| demander MIC | demander CAL | demander CAM | demander GPU |
| demander CAL | demander MIC | libérer CAM | demander CAL |
| libérer CAL | libérer MIC | demander CAL | demander CAM |
| libérer MIC | libérer CAL | demander MIC | libérer CAM |
| demander CAM | demander CAM | libérer MIC | libérer CAL |
| demander GPU | libérer CAM | libérer CAL | demander MIC |
| libérer CAM | - | demander GPU | libérer GPU |
| libérer GPU | - | libérer GPU | libérer MIC |
Question 4
#Justifier qu’un interblocage peut apparaître pour les quatre programmes de demandes de ressources.
Indice
Les deux premières demandes de P1 et P2 sont dans l’ordre inverse.
Comprendre la correction
Un scénario suffit : P1 obtient MIC ; avant qu’il demande CAL, P2 obtient CAL. P1 demande alors CAL et attend P2 ; P2 demande MIC et attend P1. Aucun n’atteint ses instructions de libération. Le cycle MIC/P1 → CAL/P2 → MIC est établi. Il n’est pas nécessaire de bloquer aussi P3 et P4 pour prouver qu’un interblocage est possible. Ce n’est pas garanti pour toutes les exécutions : l’entrelacement choisi le rend possible.
Question 5
#Quel intérêt présente une machine équipée de plusieurs processeurs ?
Indice
Distinguez alternance sur un processeur et exécution simultanée sur plusieurs unités.
Comprendre la correction
Elle peut exécuter réellement plusieurs processus en parallèle, améliorer la réactivité et augmenter le débit de calcul lorsque les tâches sont parallélisables. Cela ne multiplie pas automatiquement la vitesse de tout programme : les parties séquentielles, les ressources partagées et les synchronisations limitent le gain. Plusieurs processeurs n’éliminent pas non plus les interblocages.
Question 6
#Donner un avantage et un inconvénient d’un système sur puce de smartphone.
Indice
L’intégration réduit l’encombrement mais réduit aussi la modularité.
Comprendre la correction
Un SoC intègre plusieurs fonctions sur une même puce, par exemple CPU, GPU et contrôleurs. Avantage : un ensemble compact et économe en énergie, adapté à une batterie et à un appareil léger. Inconvénient : une réparation ou une évolution séparée d’un composant est difficile ; la panne ou l’obsolescence d’un élément peut imposer de remplacer l’ensemble. Les échanges courts entre composants peuvent aussi améliorer les performances.
Partie B : ordonnanceur tourniquet
Le processus en tête est élu pendant au plus 2 ms. S’il termine, on le retire ; sinon il rejoint la queue. D’autres processus arrivent pendant l’exécution. La figure 2 commence par P1 entre 0 et 2 ms et repère les instants d’arrivée ci-dessous.
| Processus | Arrivée (ms) | Exécution totale (ms) |
|---|---|---|
| P1 | 0 | 6 |
| P2 | 1 | 4 |
| P3 | 3 | 5 |
| P4 | 5 | 3 |
Question 7
#Compléter le chronogramme round-robin avec quantum 2 ms, en tenant compte des arrivées.
Indice
Tenez une file des processus prêts à chaque fin de quantum, en intégrant les arrivées survenues pendant le créneau.
Comprendre la correction
| Intervalle (ms) | Processus | Temps restant à la fin |
|---|---|---|
| 0-2 | P1 | 4 |
| 2-4 | P2 | 2 |
| 4-6 | P1 | 2 |
| 6-8 | P3 | 3 |
| 8-10 | P2 | 0 |
| 10-12 | P4 | 1 |
| 12-14 | P1 | 0 |
| 14-16 | P3 | 1 |
| 16-17 | P4 | 0 |
| 17-18 | P3 | 0 |
Les nouveaux processus rejoignent la queue dès leur arrivée. Après 0-2, P2 est donc devant P1 replacé en queue. À la fin du créneau 4-6, P4 arrivé à 5 ms précède le retour de P1. Lorsqu’un processus n’a plus qu’une milliseconde à faire, on passe immédiatement au suivant : le quantum est une durée maximale, pas un créneau qui doit rester inutilisé jusqu’à son terme. Le temps total est 6+4+5+3 = 18 ms, sans inactivité ni surcoût de commutation dans ce modèle.
P1 = {'nom': 'P1', 'arrivee': 0, 'temps': 6}
P2 = {'nom': 'P2', 'arrivee': 1, 'temps': 4}
P3 = {'nom': 'P3', 'arrivee': 3, 'temps': 5}
P4 = {'nom': 'P4', 'arrivee': 5, 'temps': 3}
quantum = 2creer_file_vide() crée ; est_vide(f) teste ; enfiler(f,x) ajoute en queue et renvoie None ; defiler(f) retire la tête et renvoie cet élément.
Question 8
#Créer une file fp contenant P1, P2, P3, P4 par ordre d’arrivée, le premier arrivé en tête.
Indice
Enfilez dans l’ordre chronologique sans affecter le résultat de enfiler.
Comprendre la correction
fp = creer_file_vide()
enfiler(fp, P1)
enfiler(fp, P2)
enfiler(fp, P3)
enfiler(fp, P4)Enfiler ajoute en queue : le premier élément ajouté reste donc en tête. Les dictionnaires de processus sont conservés comme objets dans la file. La fonction enfiler modifie la file et renvoie None ; il ne faut pas écrire fp = enfiler(fp,P1). Cette construction fournit une liste d’attente ordonnée, sans simuler à elle seule les instants d’arrivée du chronogramme.
def execute_un_processus(file_d_attente, t):
processus = defiler(file_d_attente)
if processus["temps"] ... quantum:
processus["temps"] = ...
...
return t + ...
else:
return t + ...Question 9
#Compléter execute_un_processus(file_d_attente,t) pour exécuter un créneau et renvoyer sa date de fin.
PythonExécuter un créneau de tourniquet sans perdre de tempsÉcrivez votre solution et mettez-la à l’épreuve
Complétez execute_un_processus(file_d_attente, t). La file non vide contient des dictionnaires avec nom, arrivee et temps, ce dernier étant le temps restant positif. Le quantum vaut 2. Défilez un processus ; s’il reste plus d’un quantum, réduisez son temps et réenfilez le même objet. Sinon, il termine et reste hors de la file. Renvoyez la nouvelle date. Comme le code du sujet, ne remettez pas à zéro le champ temps de l’objet terminé : cette partie ne conserve pas d’historique et ignore les nouvelles arrivées.
def execute_un_processus(file_d_attente,t):
# À vous de jouer
passLes cas de test proposés :
- Un processus doit revenir en queue : Deux unités sont consommées et le même objet A se place derrière B.
- Un processus court termine avant la fin maximale du créneau : Le quantum est une limite, pas une durée imposée de 2 pour tout processus.
- Exactement un quantum : Une égalité termine le processus : il ne doit pas revenir avec un temps nul.
- Respecter l’objet terminé du code fourni : L’état restant d’un objet retiré n’est pas utilisé par cette simulation. On conserve ici l’effet exact du squelette.
- Deux créneaux successifs : L’ordre FIFO sert B avant le dernier morceau de A ; la date renvoyée remplace t au prochain appel.
Indice
La fonction doit avancer le temps d’une durée différente selon les deux cas.
Comprendre la correction
def execute_un_processus(file_d_attente, t):
processus = defiler(file_d_attente)
if processus["temps"] > quantum:
processus["temps"] -= quantum
enfiler(file_d_attente, processus)
return t + quantum
else:
return t + processus["temps"]Si le temps restant dépasse le quantum, on le diminue puis remet le même dictionnaire en queue. Sinon le processus finit, reste hors de la file et consomme exactement son temps restant. L’égalité temps = quantum appartient à la branche de fin : réenfiler un processus de temps nul serait inutile. Le code demandé ne remet pas explicitement temps à zéro pour l’objet terminé ; il suffit à la simulation puisque cet objet n’est plus planifié. Une application conservant un historique pourrait mettre à jour ce champ après avoir sauvé la durée à ajouter.
Désormais tous les processus à traiter sont déjà présents ; les champs arrivee sont ignorés. On simule jusqu’à vider la file.
def execute_tous_processus(file_d_attente):
t = 0
while ...:
t = ...
return tQuestion 10
#Compléter execute_tous_processus lorsque tous les processus sont déjà arrivés, en commençant à t=0.
Indice
La condition de boucle doit porter sur l’état de la file après les étapes, pas sur sa taille initiale.
Comprendre la correction
def execute_tous_processus(file_d_attente):
t = 0
while not est_vide(file_d_attente):
t = execute_un_processus(file_d_attente, t)
return tL’appel renvoie la nouvelle date courante ; il faut l’affecter à t plutôt que l’ajouter encore une fois. Chaque créneau diminue le travail restant et finit par retirer un processus, donc la boucle termine si les durées et le quantum sont positifs. Une file vide renvoie 0 immédiatement. Cette partie ignore explicitement les arrivées : elle ne reproduit donc pas exactement l’ordre de la question 7, même si le total de travail reste 18 ms.
Un quantum plus grand termine-t-il plus vite ?Un atelier pour expérimenter
Faites varier le quantum et décidez si les arrivées réelles sont prises en compte. Le modèle trace chaque créneau et les instants de fin, sans coût de commutation.
Lire le résultat de l’expérience initiale
Toutes les tâches sont terminées à 18 ms
Le quantum change le partage et les dates individuelles de fin. Sans surcoût et sans temps mort, la somme du travail reste 18 ms. Les nouveaux arrivants sont insérés avant le retour du processus préempté.
| Début (ms) | Fin (ms) | Processus | Reste (ms) |
|---|---|---|---|
| 0 | 2 | P1 | 4 |
| 2 | 4 | P2 | 2 |
| 4 | 6 | P1 | 2 |
| 6 | 8 | P3 | 3 |
| 8 | 10 | P2 | 0 |
| 10 | 12 | P4 | 1 |
| 12 | 14 | P1 | 0 |
| 14 | 16 | P3 | 1 |
| 16 | 17 | P4 | 0 |
| 17 | 18 | P3 | 0 |
Le tourniquet vise le partage et la réactivité. Dans ce modèle, le quantum ne réduit pas la quantité totale de calcul.
Exercice 2 · 6 points
Tennis : objets, tri et arbre du tournoi
L’exercice modélise des matchs de tennis en simple et un tournoi à partir des quarts de finale. Les noms, âges, points et résultats ci-dessous sont les données du sujet, parfois fictives ou datées ; ils ne décrivent pas le classement WTA actuel. La WTA est citée comme contexte de l’organisation du tennis professionnel féminin.
class Joueuse:
def __init__(self, nom, prenom, pays, age, point):
self.nom = nom
self.prenom = prenom
self.pays = pays
self.age = age
self.point = point
self.victoire = 0
self.defaite = 0nom, prenom, pays sont des chaînes ; age et point des entiers.
Question 1
#Instancier pegula : nom Pegula, prénom Jessica, pays USA, 29 ans, 6101 points.
Indice
Le nom précède le prénom dans la signature du constructeur.
Comprendre la correction
pegula = Joueuse("Pegula", "Jessica", "USA", 29, 6101)Les arguments suivent l’ordre du constructeur. Les compteurs de victoire et défaite sont initialisés automatiquement à zéro et ne sont pas à fournir.
| Variable | Nom | Prénom | Pays | Âge | Points du sujet |
|---|---|---|---|---|---|
| gauff | Gauff | Coco | USA | 20 | 6063 |
| paloni | Paloni | Marta | Espagne | 23 | 4843 |
| sabalenka | Sabalenka | Aryna | Biélorussie | 25 | 10541 |
| swiatek | Swiatek | Iga | Pologne | 22 | 7470 |
def ajouter_victoire(self, adversaire):
...
...Question 2
#Compléter ajouter_victoire(self,adversaire), qui incrémente la victoire de la joueuse et la défaite de son adversaire.
Indice
La victoire et la défaite concernent deux objets distincts.
Comprendre la correction
def ajouter_victoire(self, adversaire):
self.victoire += 1
adversaire.defaite += 1self est la gagnante sur laquelle la méthode est appelée. adversaire est un autre objet Joueuse. Les deux compteurs changent sur deux instances différentes ; on ne doit pas incrémenter les défaites de self. Les points WTA ne sont pas modifiés, faute de règle d’attribution fournie.
Question 3
#Marta Paloni a été battue par Iga Swiatek dans le scénario du sujet. Enregistrer ce résultat.
Indice
Appelez la méthode sur celle qui a gagné.
Comprendre la correction
swiatek.ajouter_victoire(paloni)L’objet placé avant le point est la gagnante, puisque la méthode augmente sa victoire. L’adversaire transmis reçoit la défaite.
L’opérateur < des objets Joueuse est supposé redéfini pour comparer leurs points : swiatek
def tri_insertion(liste):
for i in range(1, len(liste)):
j = i
while j > 0 and liste[j] < liste[j - 1]:
liste[j], liste[j - 1] = liste[j - 1], liste[j]
j = j - 1Question 4
#Sans justifier, donner le coût du tri par insertion de n éléments dans le pire cas.
Indice
Le sujet demande uniquement l’ordre de grandeur.
Comprendre la correction
Quadratique : O(n²).
Question 5
#Compléter les étapes du tri, une ligne pour chaque échange.
Indice
Comparez les nombres de points, pas les noms des joueuses.
Comprendre la correction
| Étape | Liste après l’échange |
|---|---|
| 0 | swiatek, gauff, paloni, sabalenka, pegula |
| 1 | gauff, swiatek, paloni, sabalenka, pegula |
| 2 | gauff, paloni, swiatek, sabalenka, pegula |
| 3 | paloni, gauff, swiatek, sabalenka, pegula |
| 4 | paloni, gauff, swiatek, pegula, sabalenka |
| 5 | paloni, gauff, pegula, swiatek, sabalenka |
Le préfixe traité reste trié après chaque itération externe. Paloni, à 4 843 points, remonte deux places ; Sabalenka, à 10 541, ne bouge pas lors de son insertion. Pegula, à 6 101, dépasse Sabalenka puis Swiatek, mais reste après Gauff, à 6 063. On n’ajoute pas de ligne lorsqu’aucun échange n’a lieu, car le tableau demande les échanges, pas les tours de boucle.
Un score est une liste de couples (jeux joueuse1, jeux joueuse2). Exemple de la finale de Madrid 2024 donnée : Swiatek contre Sabalenka, [(7,5),(4,6),(7,6)]. Aucun set ni match terminé ne finit à égalité dans le modèle.
class Match:
def __init__(self, intitule, joueuse1, joueuse2):
self.intitule = intitule
self.joueuse1 = joueuse1
self.joueuse2 = joueuse2
self.gagnante = None
self.perdante = None
self.score = None
def resultat_match(self, score):
self.score = score
nb_set_joueuse1 = 0
nb_set_joueuse2 = 0
# code incompletfinale_mad_24 = Match("finale Madrid 24", swiatek, sabalenka)
finale_mad_24.resultat_match([(7,5), (4,6), (7,6)])Question 6
#Compléter resultat_match : enregistrer le score, trouver gagnante/perdante puis mettre à jour leurs bilans.
Indice
Comptez les sets remportés, puis utilisez ajouter_victoire sur la gagnante.
Comprendre la correction
def resultat_match(self, score):
self.score = score
nb_set_joueuse1 = 0
nb_set_joueuse2 = 0
for jeux1, jeux2 in score:
if jeux1 > jeux2:
nb_set_joueuse1 += 1
else:
nb_set_joueuse2 += 1
if nb_set_joueuse1 > nb_set_joueuse2:
self.gagnante = self.joueuse1
self.perdante = self.joueuse2
else:
self.gagnante = self.joueuse2
self.perdante = self.joueuse1
self.gagnante.ajouter_victoire(self.perdante)Chaque tuple décrit les jeux gagnés dans un set. Le match se décide au nombre de sets gagnés, pas à la somme des jeux. Pour [(7,5),(4,6),(7,6)], la joueuse 1 gagne les sets 1 et 3 : elle remporte le match 2-1. Les références gagnante et perdante pointent vers les objets déjà existants ; on ne crée pas de nouvelles joueuses. L’appel final met à jour les deux bilans une seule fois.
On suppose un score terminé et valide sans égalité, comme l’indique l’énoncé. Cette méthode doit être appelée une fois pour enregistrer le match ; si l’application permet de corriger un score déjà saisi, il faudrait d’abord annuler l’ancien effet sur les compteurs ou gérer explicitement cet état. Cette extension n’est pas fournie par le simple squelette.
Partie B : arbre du tournoi
Finale F
├─ Demi-finale D1
│ ├─ Quart Q1
│ └─ Quart Q2
└─ Demi-finale D2
├─ Quart Q3
└─ Quart Q4class Arbre:
def __init__(self, racine, gauche, droit):
self.racine = racine
self.gauche = gauche
self.droit = droitracine est un Match ; gauche et droit sont des Arbre ou None.
Question 7
#Pourquoi un tournoi se modélise-t-il par un arbre binaire ?
Indice
La finale dépend de deux demi-finales, chacune de deux quarts.
Comprendre la correction
Chaque match d’un tour réunit les gagnantes de deux matchs du tour précédent. Le nœud du match possède donc deux sous-arbres, un par provenance. À partir des quarts seulement, les quatre quarts sont les feuilles, les demi-finales leurs parents et la finale la racine. Ce sont les matchs, et non directement les joueuses, qui constituent les nœuds du modèle.
Q1 = Match("Quart de finale 1", gauff, pilar)
Q2 = Match("Quart de finale 2", paloni, inie)
Q3 = Match("Quart de finale 3", pegula, sabalenka)
Q4 = Match("Quart de finale 4", swiatek, jabeur)
D1 = Match("Demi-finale 1", None, None)
D2 = Match("Demi-finale 2", None, None)
F = Match("Finale", None, None)pilar, inie et jabeur sont supposées instanciées. D1 reçoit les gagnantes de Q1 et Q2 ; D2 celles de Q3 et Q4 ; F celles de D1 et D2.
Question 8
#Instancier tournoi à partir des matchs de Madrid 2025 indiqués.
Indice
Conservez les deux niveaux de modèles : un Arbre contient un Match.
Comprendre la correction
tournoi = Arbre(F,
Arbre(D1, Arbre(Q1, None, None), Arbre(Q2, None, None)),
Arbre(D2, Arbre(Q3, None, None), Arbre(Q4, None, None)))Chaque feuille doit aussi être un objet Arbre contenant son Match. Les enfants absents sont None. L’objet F est la valeur racine de tournoi, pas l’arbre lui-même. Les mêmes objets Match sont partagés avec les variables Q1, D1, etc. : modifier un match par sa variable se voit donc dans l’arbre.
Q1.resultat_match([(6,4), (7,5)])Question 9
#Q1 est gagné par gauff 6-4, 7-5. Compléter tournoi. ... = gauff pour la placer en joueuse1 de D1.
Indice
Descendez d’un niveau d’arbre avant d’accéder à l’objet Match.
Comprendre la correction
tournoi.gauche.racine.joueuse1 = gaufftournoi.gauche est l’arbre de la demi-finale D1 ; .racine est le Match D1 ; .joueuse1 est l’emplacement réservé à la gagnante du quart gauche Q1. Le détour par racine est indispensable car Arbre ne possède pas directement d’attribut joueuse1.
Question 10
#Qu’est-ce qu’un programme récursif ?
Indice
Identifiez l’auto-appel et le cas qui empêche de descendre indéfiniment.
Comprendre la correction
Il comporte une fonction qui s’appelle elle-même, directement ou indirectement, pour résoudre un sous-problème. Un mécanisme de terminaison arrête ces appels, par exemple l’arrivée sur un sous-arbre absent ou un état déjà renseigné. Une simple répétition par boucle n’est pas de la récursivité.
def mise_a_jour(self):
if self.racine.joueuse1 is None:
if self.gauche is not None:
if ...:
... = self.gauche.racine.gagnante
else:
self.gauche.mise_a_jour()
if self.racine.joueuse2 is None:
if self.droit is not None:
if ...:
... = self.droit.racine.gagnante
else:
...Question 11
#Compléter mise_a_jour aux lignes 6,7,13,14,16.
Indice
Le nœud enfant possède un Match dans racine ; sa gagnante doit être non None pour remonter.
Comprendre la correction
def mise_a_jour(self):
if self.racine.joueuse1 is None:
if self.gauche is not None:
if self.gauche.racine.gagnante is not None:
self.racine.joueuse1 = self.gauche.racine.gagnante
else:
self.gauche.mise_a_jour()
if self.racine.joueuse2 is None:
if self.droit is not None:
if self.droit.racine.gagnante is not None:
self.racine.joueuse2 = self.droit.racine.gagnante
else:
self.droit.mise_a_jour()Pour chaque côté, on ne remplit qu’un emplacement encore vide. Si le match enfant a déjà une gagnante, on la copie au tour supérieur. Sinon on descend dans ce sous-arbre pour mettre à jour ses matchs précédents. Les feuilles n’ont pas d’enfants, ce qui arrête naturellement la descente. Cette fonction ne joue pas les matchs et ne détermine pas une gagnante : elle propage seulement les résultats déjà saisis.
Par exemple, après les résultats de Q1 et Q2, un appel sur la racine F descend vers D1 et complète ses deux participantes, mais F n’a toujours pas de joueuse1 tant que la demi-finale D1 n’a pas été jouée. Après saisie de son résultat, un nouvel appel copie sa gagnante dans F. Le test des emplacements déjà renseignés évite d’effacer un résultat existant.
Qui gagne le match : les jeux ou les sets ?Un atelier pour expérimenter
Choisissez un score terminé. Le modèle compte les jeux et les sets séparément pour montrer pourquoi le vainqueur du match se décide aux sets remportés.
Lire le résultat de l’expérience initiale
La joueuse 1 gagne le match
Chaque set remporté compte une unité, quelle que soit sa marge en jeux. On peut gagner davantage de jeux au total et perdre le match.
| Set | Jeux J1 | Jeux J2 | Gagnante du set |
|---|---|---|---|
| 1 | 7 | 5 | Joueuse 1 |
| 2 | 4 | 6 | Joueuse 2 |
| 3 | 7 | 6 | Joueuse 1 |
Un objet Match doit traduire la règle de victoire correcte, puis mettre à jour les objets Joueuse qui lui sont associés.
Exercice 3 · 8 points
Course à pied : inscriptions SQL, moyennes et records
Un club organise deux courses, 5 km et 10 km. Un coureur ne peut s’inscrire qu’à une épreuve. La table coureur identifie chaque inscription par un dossard automatiquement incrémenté ; l’organisateur saisit le nom, prénom, année de naissance, sexe H/F et l’épreuve. Le temps en secondes commence à 0. La table epreuve indique distance en km, horaire de départ, prix d’inscription et juge arbitre.
| Relation | Attributs | Clé primaire | Clé étrangère |
|---|---|---|---|
| coureur | num_dossard, nom, prenom, annee, sexe, id_epreuve, temps | num_dossard | id_epreuve → epreuve.id_epreuve |
| epreuve | id_epreuve, distance, horaire, prix, juge_arbitre | id_epreuve | - |
| Dossard | Nom | Prénom | Année | Sexe | Épreuve | Temps |
|---|---|---|---|---|---|---|
| 1 | DA SILVA | José | 1980 | H | 1 | 0 |
| 2 | HANG LI | Léo | 2005 | H | 2 | 0 |
| 3 | BODIANE | Lola | 2000 | F | 2 | 0 |
| 4 | BRELET | Sandra | 1972 | F | 1 | 0 |
| id_epreuve | distance | horaire | prix | juge_arbitre |
|---|---|---|---|---|
| 1 | 5 | 10 | 10 | GAVEAU Philippe |
| 2 | 10 | 11 | 20 | BOULA Awa |
On peut utiliser SELECT, FROM, WHERE, AND, OR, JOIN ... ON, INSERT, UPDATE, DELETE, DISTINCT, ORDER BY et COUNT.
Question 1
#Pourquoi num_dossard est-il choisi comme clé primaire de coureur ?
Indice
La clé doit distinguer des personnes qui portent le même nom.
Comprendre la correction
Il est attribué automatiquement, unique et non nul pour chaque inscription. Deux coureurs homonymes peuvent donc être distingués. Puisque chaque coureur ne participe qu’à une seule épreuve dans le modèle, ce dossard suffit à identifier la ligne sans le combiner à id_epreuve.
Question 2
#Décrire SELECT nom, prenom FROM coureur ORDER BY nom.
Indice
ORDER BY porte seulement sur nom.
Comprendre la correction
La requête liste les noms et prénoms de tous les inscrits, triés par nom croissant. Dans l’extrait : BODIANE Lola, BRELET Sandra, DA SILVA José, HANG LI Léo. Les personnes portant le même nom ne seraient pas nécessairement triées par prénom, car ce second critère n’est pas demandé. La collation du SGBD détermine les détails de l’ordre alphabétique.
Question 3
#Lister noms et prénoms des femmes inscrites.
Indice
Utilisez la valeur stockée F, pas le mot femme.
Comprendre la correction
SELECT nom, prenom
FROM coureur
WHERE sexe = 'F';On filtre avec la codification F du sujet. Aucun filtre sur la distance n’est demandé, donc les deux épreuves sont concernées.
Question 4
#Connaître le nombre total d’inscrits.
Indice
L’agrégation doit porter sur tous les coureurs, sans WHERE.
Comprendre la correction
SELECT COUNT(*) FROM coureur;COUNT(*) compte les lignes de la table entière. Les quatre lignes imprimées ne sont qu’un extrait : on ne peut déduire le total réel de la seule illustration.
Bulletin reçu : Nom REMY ; prénom Patrice ; civilité homme ; course 5 km ; année de naissance 1973.
Question 5
#Inscrire Patrice REMY, homme né en 1973, sur 5 km.
Indice
Regardez l’identifiant de l’épreuve et laissez la base attribuer le dossard.
Comprendre la correction
INSERT INTO coureur (nom, prenom, annee, sexe, id_epreuve, temps)
VALUES ('REMY', 'Patrice', 1973, 'H', 1, 0);Le dossard n’est pas fourni : il est généré automatiquement par la base. id_epreuve = 1 correspond au 5 km, alors que la valeur 5 décrit une distance, pas un identifiant. Le temps vaut initialement zéro.
Question 6
#Supprimer l’inscription du dossard 137, blessé avant l’épreuve.
Indice
DELETE doit porter sur coureur et être limité par sa clé.
Comprendre la correction
DELETE FROM coureur WHERE num_dossard = 137;La suppression cible la clé de l’inscription. On ne supprime pas l’épreuve à laquelle il était inscrit, qui reste partagée par les autres coureurs.
Question 7
#Donner distance et horaire de départ au dossard 256.
Indice
La clé étrangère relie l’inscription à la fiche de course.
Comprendre la correction
SELECT e.distance, e.horaire
FROM coureur AS c
JOIN epreuve AS e ON c.id_epreuve = e.id_epreuve
WHERE c.num_dossard = 256;La table coureur fournit l’épreuve choisie ; la table epreuve en donne les caractéristiques. Le dossard 256 n’est pas dans l’extrait, donc seule la requête peut être fournie, pas une distance inventée.
À l’arrivée, l’arbitre note par exemple :
| Dossard | Temps (s) |
|---|---|
| 57 | 1242 |
| 72 | 1845 |
| 183 | 1284 |
| 2 | 1285 |
Tous les temps ont ensuite été enregistrés. La catégorie Master Femme du problème regroupe les coureuses nées avant 1986.
Question 8
#Classer les femmes Master du 10 km, nées avant 1986, par temps croissant ; afficher dossard, nom, prénom et temps.
SQLClasser les femmes Master du 10 kmÉcrivez votre solution et mettez-la à l’épreuve
Affichez dossard, nom, prénom et temps des femmes nées avant 1986 inscrites au 10 km, par temps croissant. Les temps des participantes classables sont renseignés.
SELECT ...
FROM ...
WHERE ...;Les cas de test proposés :
- Extrait des inscriptions : aucune coureuse admissible : Les lignes initiales sont celles du PDF. Aucune ne satisfait simultanément sexe, année et distance ; leurs temps initiaux à 0 ne participent donc pas au classement.
- Cas complémentaire : filtrer puis ordonner : Jeu complémentaire pédagogique, distinct des données officielles. 1986 est exclue, la course 5 km est exclue et les hommes sont exclus. Les dossards sont dans un ordre opposé au classement par temps.
- Cas complémentaire : la distance vient de la table épreuve : Jeu complémentaire pédagogique, distinct des données officielles. L’identifiant 9 correspond ici au 10 km. La jointure doit utiliser le sens du champ
distance, pas un identifiant supposé constant.
Indice
Combinez catégorie, année et distance avant le tri.
Comprendre la correction
SELECT c.num_dossard, c.nom, c.prenom, c.temps
FROM coureur AS c
JOIN epreuve AS e ON e.id_epreuve = c.id_epreuve
WHERE c.sexe = 'F' AND c.annee < 1986 AND e.distance = 10
ORDER BY c.temps ASC;La limite « avant 1986 » est stricte ; 1986 est exclu. Le plus petit temps arrive en tête. L’énoncé suppose tous les temps enregistrés : aucun filtre de résultat manquant n’est nécessaire ici. Dans une application conservant des non-partants à temps 0, il faudrait les exclure du classement, sinon ils seraient incorrectement premiers. Filtrer directement id_epreuve=2 serait également valable pour la table donnée, mais la jointure exprime explicitement la distance demandée.
Partie B : performances sur plusieurs années
Émilie, élève de Terminale NSI dans le scénario, associe à chaque année la liste des meilleurs temps dans cet ordre : JH, JF (moins de 18 ans), SH, SF (18 à 40 ans), MH, MF (plus de 40 ans). Ce sont les catégories simplifiées du sujet. Exemple : en 2024, JF vaut 1010 s et MH vaut 1022 s.
dict_perf_5km = {
2022: [
1020,
1050,
900,
1000,
1018,
1040
],
2023: [
1010,
1048,
1100,
1024,
1080,
1108
],
2024: [
1012,
1010,
1000,
1036,
1022,
1098
],
2025: [
998,
1028,
1000,
959,
1002,
980
]
}Question 9
#Donner dict_perf_5km[2025][2].
Indice
Les indices de la liste de catégories commencent à zéro.
Comprendre la correction
1000 secondes, meilleure performance Senior Homme en 2025. La clé externe est l’année ; l’indice 2 sélectionne la troisième catégorie, SH.
Question 10
#Ajouter les temps 2026 : JH 1004, JF 1016, SH 1000, SF 1140, MH 1023, MF 1024.
Indice
Conservez exactement les six positions conventionnelles.
Comprendre la correction
dict_perf_5km[2026] = [1004, 1016, 1000, 1140, 1023, 1024]L’ordre des six catégories doit rester identique d’une année à l’autre. La clé 2026 est un entier, comme les années déjà enregistrées. Si cette clé existait, l’affectation remplacerait sa liste ; elle ne créerait pas une seconde entrée pour la même année.
Question 11
#Écrire scratch(dico,annee), meilleur temps de l’année toutes catégories confondues, sans min ni max. L’année est présente.
Indice
Un record de course minimise le temps.
Comprendre la correction
def scratch(dico, annee):
meilleur = dico[annee][0]
for temps in dico[annee]:
if temps < meilleur:
meilleur = temps
return meilleurLe meilleur temps est le plus petit. Initialiser avec le premier résultat, plutôt qu’avec zéro, garantit une valeur de référence réellement présente. À chaque itération, meilleur est le minimum des temps déjà parcourus. Pour 2024, la liste [1012,1010,1000,1036,1022,1098] donne 1000. La liste contient six catégories selon le contrat, donc son premier élément existe.
def mystere(dico, cat):
categories = ["JH", "JF", "SH", "SF", "MH", "MF"]
s = 0
nb = 0
for i in range(len(categories)):
if cat == categories[i]:
i_cat = i
for annee in dico.keys():
s = s + dico[annee][i_cat]
nb = nb + 1
return s / nbQuestion 12
#Quelle valeur reçoit i_cat dans mystere(dict_perf_5km,"SH") ?
Indice
Repérez SH dans la liste ordonnée des catégories.
Comprendre la correction
2. SH est le troisième élément de categories, donc son indice vaut 2. La boucle repère cet indice avant de consulter chaque liste annuelle.
| Choix | Message proposé |
|---|---|
| 1 | expected an indented block |
| 2 | takes 2 positionnals arguments but 3 were given |
| 3 | local variable 'i_cat' referenced before assignement |
| 4 | list index out of range |
Question 13
#Quel message parmi les quatre proposés correspond à mystere(...,"SG"), catégorie inexistante ?
Indice
La ligne qui affecte i_cat n’a jamais été exécutée.
Comprendre la correction
Le troisième : « local variable 'i_cat' referenced before assignment ». Aucune comparaison n’a réussi, donc i_cat n’a jamais reçu de valeur ; sa lecture lors de la première année provoque une UnboundLocalError. La formulation exacte du message varie selon la version de Python, mais c’est cette erreur de variable locale non initialisée. Ce n’est ni une erreur d’indentation ni un dépassement d’indice de liste.
Question 14
#Ajouter une assertion après categories pour signaler une catégorie mal saisie avec un message.
Indice
assert accepte une condition, puis un message après une virgule.
Comprendre la correction
assert cat in categories, "Catégorie inconnue : " + catLe test interrompt immédiatement la fonction si le code n’appartient pas aux six catégories. Le message explique la faute plutôt que de laisser apparaître une erreur technique plus loin. Dans une application de production, une validation explicite avec exception contrôlée serait préférable car Python peut désactiver les assertions avec l’option -O ; ici l’assertion est précisément demandée.
Question 15
#Que renvoie mystere(dict_perf_5km,"SH") ?
Indice
La fonction additionne une valeur par année puis divise par le nombre d’années.
Comprendre la correction
1000.0 : la fonction calcule la moyenne des meilleurs temps SH enregistrés chaque année. Après ajout de 2026 : (900 + 1100 + 1000 + 1000 + 1000) / 5 = 1000. Avant cet ajout, les quatre premières valeurs avaient déjà la même moyenne. L’opérateur / renvoie un flottant en Python. Cette valeur n’est ni le record global SH (900) ni la moyenne de tous les coureurs, puisque le dictionnaire ne contient que les meilleurs temps annuels.
Question 16
#Écrire records(dico), liste des meilleurs temps de chaque catégorie sur toutes les années, sachant que les temps ne dépassent pas 24 h.
Indice
Gardez six minima indépendants, un à chaque indice de catégorie.
Comprendre la correction
def records(dico):
meilleurs = [24 * 60 * 60] * 6
for annee in dico:
for i in range(6):
if dico[annee][i] < meilleurs[i]:
meilleurs[i] = dico[annee][i]
return meilleursLe plafond 86 400 secondes sert de sentinelle supérieure ou égale à tous les temps admis. Chaque catégorie possède son propre minimum, mis à jour pour chaque année. On obtient [998,1010,900,959,1002,980], y compris après ajout de 2026, qui ne bat aucun de ces records. Réinitialiser meilleurs à l’intérieur de la boucle annuelle ferait perdre les records des années précédentes.
Le dictionnaire doit contenir au moins une année pour que la notion de record soit définie. Sur un dictionnaire vide, ce code renvoie les sentinelles ; une version d’application devrait refuser ce cas ou représenter explicitement l’absence de record. On pourrait aussi initialiser à une copie de la première année lorsqu’elle est garantie, ce qui éviterait la sentinelle.
Record et moyenne ne racontent pas la même choseUn atelier pour expérimenter
Sélectionnez une catégorie et choisissez d’inclure les données 2026. Le tableau fait apparaître les meilleurs temps annuels, leur record et leur moyenne.
Lire le résultat de l’expérience initiale
SH : record 900 s
La moyenne porte seulement sur les meilleurs temps annuels. Un temps plus petit est meilleur ; ajouter une année peut modifier la moyenne sans améliorer le record.
| Année | Meilleur temps de la catégorie (s) | Record atteint |
|---|---|---|
| 2022 | 900 | Oui |
| 2023 | 1100 | Non |
| 2024 | 1000 | Non |
| 2025 | 1000 | Non |
| 2026 | 1000 | Non |
Le sens d’une statistique dépend des données réellement stockées : ici des records annuels par catégorie, pas tous les temps individuels.
Revoir les notions de cet exercice
Du sujet à la méthode
Votre prochaine séance de révision
- Suivez les mutations des objets : une victoire change deux joueuses, une mise à jour du tournoi ne rejoue aucun match.
- Avant une agrégation, expliquez quelle population de lignes ou de valeurs est réellement incluse.
- Distinguez le chronogramme avec arrivées du modèle où tous les processus sont déjà présents.
Retrouver ces notions dans d’autres sujets
Toutes les annales de NSI · Le guide pour préparer le bac NSI 2027
Énoncé : sujet 26-NSIJ2AN1 (PDF). Corrigé et explications pédagogiques proposés par Sofien.
