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
Courses d’orientation et choix glouton sur un graphe
Les participants poinçonnent les balises de La forêt des chênes. Trois parcours partent de la balise 1 : vert facile, rouge moyen et noir difficile. Vert et noir terminent en 12 ; rouge termine en 8. Dans le dessin original, triangle signifie vert, carré rouge et pentagone noir. Une balise peut appartenir à plusieurs parcours. Le tableau donne ces mêmes informations sans dépendre des formes.
Lire les connexions du schéma
- 1 relié à 2
- 1 relié à 3
- 2 relié à 4
- 3 relié à 6
- 4 relié à 5
- 4 relié à 6
- 5 relié à 10
- 6 relié à 7
- 6 relié à 11
- 7 relié à 10
- 8 relié à 9
- 9 relié à 11
- 9 relié à 12
- 10 relié à 12
Balise | Itinéraires |
|---|---|
| 1 | vert, rouge, noir |
| 2 | rouge |
| 3 | vert, noir |
| 4 | rouge, noir |
| 5 | noir |
| 6 | vert, rouge, noir |
| 7 | vert |
| 8 | rouge |
| 9 | rouge |
| 10 | vert, noir |
| 11 | rouge |
| 12 | vert, noir |
Chaque Balise possède num_balise, couleurs_balise, voisines (objets Balise) et visitee (booléen initialementFalse). L’annexe fournie est reproduite intégralement :
class Balise:
def __init__(self, numero, couleurs):
self.num_balise = numero
self.couleurs_balise = couleurs
self.voisines = []
self.visitee = False
def methode1(self):
return [b.num_balise for b in self.voisines]
def methode2(self, couleur):
self.couleurs_balise = [c for c in self.couleurs_balise if c != couleur]
def methode3(self, couleur):
self.couleurs_balise.append(couleur)balise1 = Balise(1, ['vert', 'rouge', 'noir'])
balise2 = Balise(2, ['rouge'])
balise3 = Balise(3, ['vert', 'noir'])
balise4 = Balise(4, ['rouge', 'noir'])
balise5 = Balise(5, ['noir'])
balise6 = Balise(6, ['vert', 'rouge', 'noir'])
balise7 = Balise(7, ['vert'])
balise8 = Balise(8, ['rouge'])
balise9 = Balise(9, ['rouge'])
balise10 = Balise(10, ['vert', 'noir'])
balise11 = Balise(11, ['rouge'])
balise12 = ...
balise1.voisines = [balise2, balise3]
balise2.voisines = [balise1, balise4]
balise3.voisines = [balise1, balise6]
balise4.voisines = [balise2, balise5, balise6]
balise5.voisines = [balise4, balise10]
balise6.voisines = [balise3, balise4, balise7, balise11]
balise7.voisines = [balise6, balise10]
balise8.voisines = [balise9]
# Ligne38 : à compléter
balise10.voisines = [balise5, balise7, balise12]
balise11.voisines = [balise6, balise9]
balise12.voisines = [balise9, balise10]Question 1
#Compléter la ligne 28 pour instancier la balise 12.
Indice
Lire les deux symboles de la balise 12 dans la légende.
Comprendre la correction
balise12 = Balise(12, ['vert', 'noir'])La figure place 12 sur les parcours vert et noir, pas sur le rouge. Le constructeur reçoit le numéro puis la liste de couleurs ; il initialise lui-même voisines et visitee. L’ordre des deux couleurs n’affecte pas les tests d’appartenance.
Question 2
#Écrire la ligne 38 manquante pour compléter le graphe.
Indice
Une liste de voisines contient les instances, pas leurs numéros.
Comprendre la correction
balise9.voisines = [balise8, balise11, balise12]La balise 9 est reliée à 8,11 et 12. Les autres affectations de l’annexe ont déjà enregistré les liaisons dans l’autre sens. Les éléments sont des objets Balise ; mettre les entiers 8,11,12 empêcherait ensuite d’accéder à leurs attributs.
Question 3
#Que renvoie balise4.methode1() ?
Indice
Appliquez l’expression b.num_balise à chaque élément.
Comprendre la correction
[2, 5, 6]La compréhension parcourt les trois objets voisins dans l’ordre de la liste et extrait leur num_balise. Elle renvoie une nouvelle liste d’entiers sans modifier le graphe.
Question 4
#Après balise 4.methode2(rouge), puis methode3(vert), que vaut couleurs_balise ?
Indice
La première méthode filtre, la seconde ajoute en fin de liste.
Comprendre la correction
['noir', 'vert']methode2 reconstruit la liste en supprimant les occurrences de rouge ; il reste noir. methode3 ajoute vert à la fin. On ne trie pas les couleurs et l’ajout n’est pas un remplacement. Comme indiqué dans l’énoncé, on rétablit ensuite la balise 4 originale pour traiter la suite.
Les extrémités doivent porter la couleur demandée. Le squelette fourni contient les blancs suivants :
def itineraire(balise_debut, balise_fin, couleur):
assert couleur in balise_debut.couleurs_balise
assert couleur in balise_fin.couleurs_balise
balise = balise_debut
chemin = [balise]
while balise.num_balise != ...:
for b in balise.voisines:
if couleur in ... and b not in ...:
balise = ...
chemin.append(balise)
return [b.num_balise for b in chemin]Question 5
#Compléter itineraire pour relier les balises de début et de fin en suivant la couleur donnée.
Indice
Le test de couleur porte sur le voisin ; le test de répétition porte sur le chemin.
Comprendre la correction
def itineraire(balise_debut, balise_fin, couleur):
assert couleur in balise_debut.couleurs_balise
assert couleur in balise_fin.couleurs_balise
balise = balise_debut
chemin = [balise]
while balise.num_balise != balise_fin.num_balise:
for b in balise.voisines:
if couleur in b.couleurs_balise and b not in chemin:
balise = b
chemin.append(balise)
return [b.num_balise for b in chemin]Les compléments sont balise_fin.num_balise, b.couleurs_balise, chemin et b. Le chemin stocke des objets afin de tester qu’un voisin n’a pas déjà été choisi ; la compréhension finale transforme ces objets en numéros. Pour vert, on suit 1,3,6,7,10,12. Pour noir, on suit 1,3,6,4,5,10,12.
Ce squelette fonctionne pour les parcours colorés de ce graphe, qui fournissent la continuation attendue. Il ne constitue pas une recherche de chemin générale : dans un autre graphe, plusieurs voisins admissibles ou une impasse pourraient provoquer un mauvais choix ou une boucle sans progrès. Une version générale devrait prévoir un échec et éventuellement un retour en arrière.
Question 6
#Donner l’ordre d’un parcours en profondeur depuis 1, en choisissant toujours le plus petit numéro parmi les voisins non visités. Les couleurs ne comptent plus.
Indice
À une impasse, revenez au dernier sommet ayant encore un voisin non découvert.
Comprendre la correction
[1, 2, 4, 5, 10, 7, 6, 3, 11, 9, 8, 12]On s’enfonce jusqu’à 3, qui ne possède plus de voisin non découvert. On revient alors à 6 pour visiter 11, puis 9,8 et 12. La liste donne l’ordre des premières découvertes ; les retours physiques par des balises déjà poinçonnées ne sont pas ajoutés comme de nouvelles visites. Marquer les sommets évite de tourner dans les cycles du graphe.
PartieB : les couleurs sont ignorées. Chaque liaison porte désormais un temps moyen en minutes. Les listes de voisines deviennent des listes de tuples ; par exemple balise 1.voisines=[(balise 2,5),(balise 3,12)].
Lire les connexions du schéma
- 1 relié à 2 : 5
- 1 relié à 3 : 12
- 2 relié à 4 : 13
- 3 relié à 6 : 11
- 4 relié à 5 : 21
- 4 relié à 6 : 15
- 5 relié à 10 : 6
- 6 relié à 7 : 10
- 6 relié à 11 : 8
- 7 relié à 10 : 12
- 8 relié à 9 : 9
- 9 relié à 11 : 16
- 9 relié à 12 : 4
- 10 relié à 12 : 7
Question 7
#Remplacer les voisines de balise 4 par les tuples (balise, temps) de la figure 2.
Indice
Lire les poids des trois arêtes incidentes à 4.
Comprendre la correction
balise4.voisines = [(balise2, 13), (balise5, 21), (balise6, 15)]La première composante reste une instance Balise ; la seconde est un temps moyen en minutes. Les chemins sont plats et les arêtes non orientées : les temps sont les mêmes dans les deux sens.
def mystere(balise):
meilleure_balise = None
mini = -1
for b, t in balise.voisines:
if not b.visitee and (mini == -1 or t < mini):
meilleure_balise, mini = b, t
return meilleure_baliseQuestion 8
#Toutes les balises sont non visitées. Que renvoie mystere(balise 10).num_balise ?
Indice
Comparer les temps, pas les numéros des balises.
Comprendre la correction
Le résultat est 5. Les voisines 5,7 et 12 sont accessibles en 6,12 et 7 minutes ; la durée minimale est 6. mystere renvoie l’objet balise 5, puis l’accès à num_balise fournit l’entier 5. Si aucun voisin n’était disponible, mystere renverraitNone et cet accès à num_balise ne serait plus possible.
Question 9
#Depuis 1 vers 12, donner les balises suivies en choisissant à chaque étape le voisin non visité le plus proche en temps.
Indice
Barrez les voisines déjà visitées avant chaque comparaison.
Comprendre la correction
[1, 2, 4, 6, 11, 9, 12]| Départ | Choix | Temps |
|---|---|---|
| 1 | 2 | 5 |
| 2 | 4 | 13 |
| 4 | 6 | 15 |
| 6 | 11 | 8 |
| 11 | 9 | 16 |
| 9 | 12 | 4 |
Le total de ces décisions locales est61 minutes. À4, on préfère 6 à 5 ; à 6, on préfère 11 à 7 et 3 ; à 9, on préfère 12 à 8. Aucun retour vers une balise visitée n’est autorisé. Ce trajet ne prétend pas être le plus rapide globalement :1-3-6-11-9-12 coûte51 minutes.
def itineraire_trail(balise_debut, balise_fin):
balise_debut.visitee = True
balise = balise_debut
chemin = [balise]
while balise_fin not in chemin:
prochaine = ...
if prochaine != None:
...
else:
return None
return [b.num_balise for b in chemin]Question 10
#Compléter itineraire_trail. Si le choix glouton ne permet plus de continuer, renvoyerNone. Avant l’appel, toutes les balises ont visitee=False.
Indice
Trois mises à jour accompagnent chaque voisin retenu : marquage, position, chemin.
Comprendre la correction
def itineraire_trail(balise_debut, balise_fin):
balise_debut.visitee = True
balise = balise_debut
chemin = [balise]
while balise_fin not in chemin:
prochaine = mystere(balise)
if prochaine is not None:
prochaine.visitee = True
balise = prochaine
chemin.append(balise)
else:
return None
return [b.num_balise for b in chemin]mystere choisit le prochain objet. Il faut alors le marquer, mettre à jour la balise courante et ajouter l’objet au chemin. Oublier le marquage permettrait des retours ; oublier la mise à jour de balise répéterait les choix depuis le même sommet. Le casNone correspond à une impasse pour cette stratégie. Il ne prouve pas qu’aucun autre trajet n’existe dans le graphe.
Le marquage modifie les objets partagés. Il faut donc les réinitialiser avant une nouvelle simulation, conformément à l’hypothèse du sujet. Lorsque départ et arrivée sont identiques, la boucle ne s’exécute pas et la fonction renvoie la liste contenant ce seul numéro.
Question 11
#Choisir le type d’algorithme : A glouton ; B force brute ; C k plus proches voisins.
Indice
Le choix est effectué localement à chaque étape.
Comprendre la correction
PropositionA : algorithme glouton.
Question 12
#Donner un avantage et un inconvénient de cette famille d’algorithmes.
Indice
Comparez le temps du parcours glouton à un autre trajet valide.
Comprendre la correction
Avantage : chaque décision locale est simple et n’exige pas d’énumérer tous les itinéraires, ce qui réduit souvent le travail de recherche. Inconvénient : la succession de meilleurs choix locaux ne garantit pas une solution optimale globale, ni même de trouver une solution lorsqu’il en existe une. Ici, le parcours de 61 minutes est plus long que celui de 51 minutes indiqué précédemment. Un exemple concret montre mieux la limite qu’une affirmation générale.
Une décision locale peut-elle changer toute la course ?Un atelier pour expérimenter
Faites varier le temps du chemin 4-5. Le sportif part de 1 et applique exactement la règle gloutonne sans revisiter une balise. Le tableau expose chaque choix et le temps cumulé.
Lire le résultat de l’expérience initiale
1 → 2 → 4 → 6 → 11 → 9 → 12 :arrivée atteinte.
Les balises déjà visitées sont exclues. Modifier un seul coût peut changer le choix local à4 et toute la suite du trajet.
| Depuis | Vers | Durée | Cumul |
|---|---|---|---|
| 1 | 2 | 5 | 5 |
| 2 | 4 | 13 | 18 |
| 4 | 6 | 15 | 33 |
| 6 | 11 | 8 | 41 |
| 11 | 9 | 16 | 57 |
| 9 | 12 | 4 | 61 |
Une stratégie locale peut être rapide à exécuter sans optimiser le résultat global.
Exercice 2 · 6 points
Soupe de nombres : trouver une chaîne minimale avec une file
Le jeu Soupe de nombres de Javier Dominguez Cruz est adapté ici en n’utilisant que l’addition. Douze cartes forment trois lignes et quatre colonnes. On veut obtenir une cible en additionnant une chaîne de cartes voisines horizontalement, verticalement ou diagonalement, sans utiliser deux fois la même carte. La meilleure chaîne possède le moins de cartes ; une seule peut suffire. Deux cartes de même valeur restent deux positions distinctes.
Trois exemples de la figure sont reconstruits ci-dessous. Les deux premiers ont trois cartes, la longueur minimale pour 27.
12+15 n’est pas une chaîne : ces positions ne sont pas voisines.9+9+8 réutilise la seule carte 9 et est donc interdit, indépendamment de sa somme. Le plateau est une liste de lignes ; on étend ensuite le programme à des dimensions quelconques.
plateau = [[6,7,11,6], [8,14,6,7], [12,9,8,15]]Question 1
#Que vaut v après v=plateau[1][2] ?
Indice
Lire d’abord la ligne, ensuite la colonne.
Comprendre la correction
v vaut 6 : la ligne d’indice 1 est [8,14,6,7], puis l’indice 2 y désigne le troisième élément. Les deux indices commencent à 0.
Le squelette impose la compréhension [cartes[i+j*n] for j in range(m)] ; seules l’initialisation du plateau, la boucle externe et le return sont à compléter.
Question 2
#Compléter plateau_init(n, m, cartes), avec n, m positifs et au moins n×m cartes. shuffle mélange la liste en place ; les cartes sont choisies sans remise.
Indice
La boucle externe crée n lignes et la compréhension m cases par ligne.
Comprendre la correction
def plateau_init(n, m, cartes):
shuffle(cartes)
plateau = []
for i in range(n):
plateau.append([cartes[i + j*n] for j in range(m)])
return plateauLes indices utilisés sont i+j×n, pour 0≤i< n et 0≤j< m. Ils couvrent une seule fois les n×m premières positions, en remplissant les colonnes par paquets de n. C’est une disposition différente d’un parcours ligne par ligne, mais elle respecte les dimensions et le tirage sans remise. Deux cartes peuvent porter la même valeur : sans remise signifie que l’on ne réutilise pas une même position de la liste de cartes.
def cartes_voisines(n, m, i, j):
voisines = []
for i2 in range(i-1, i+2):
for j2 in ...:
if (i2,j2) != (i,j) and i2 in range(n) and ...:
voisines.append(...)
return ...Question 3
#Compléter cartes_voisines(n, m, i, j), qui renvoie les coordonnées des voisines de(i, j), diagonales comprises.
Indice
Le carré 3×3 contient aussi le centre et parfois des cases extérieures.
Comprendre la correction
def cartes_voisines(n, m, i, j):
voisines = []
for i2 in range(i - 1, i + 2):
for j2 in range(j - 1, j + 2):
if (i2, j2) != (i, j) and i2 in range(n) and j2 in range(m):
voisines.append((i2, j2))
return voisinesLes deux boucles explorent le carré 3×3 centré sur la carte. Trois filtres écartent la carte elle-même, les lignes hors plateau et les colonnes hors plateau. Un coin d’un plateau assez grand possède trois voisines ; une carte intérieure en possède huit. Le tuple(i 2, j 2) conserve ensemble ligne et colonne.
Question 4
#Donner la valeur de e 1=[(2,1),(1,0),(0,0),(0,1)], e 2=[(1,1),(1,2),(2,0)] et e 3=[(0,2),(0,3),(1,4)], lorsque la chaîne existe.
Indice
Une chaîne doit respecter à la fois les limites et la proximité de chaque paire consécutive.
Comprendre la correction
e 1 est valide et vaut 9+8+6+7=30. e 2 n’est pas valide : de(1,2) à(2,0), on se déplace de deux colonnes, donc les cartes ne sont pas voisines. e 3 n’est pas valide : la colonne 4 n’existe pas dans un plateau dont les colonnes sont indexées 0 à 3. Il ne faut pas simplement additionner les valeurs sans vérifier les règles de déplacement.
Question 5
#Écrire chaine_evalue(plateau, chaine), qui renvoie la somme d’une chaîne. La chaîne[(0,0),(0,1),(1,2),(2,2)] donne 27.
Indice
La chaîne contient des coordonnées, pas directement les valeurs à additionner.
Comprendre la correction
def chaine_evalue(plateau, chaine):
total = 0
for i, j in chaine:
total += plateau[i][j]
return totalLe déballage for i,j in chaine rend les coordonnées explicites ; on ajoute la valeur de chaque case. La fonction suppose une chaîne valide comme son contrat le demande. Elle ne vérifie ni les voisinages ni les répétitions : ces propriétés sont garanties par la construction dans explore. La chaîne vide donnerait 0, même si les solutions du jeu commencent par une carte.
Question 6
#Pourquoi utiliser un parcours en largeur plutôt qu’en profondeur pour obtenir la chaîne la plus courte ?
Indice
Quelle longueur ont les éléments retirés de la file avant de passer au niveau suivant ?
Comprendre la correction
La file traite d’abord toutes les chaînes d’une carte, puis de deux cartes, puis de trois, etc. La première chaîne dont la somme atteint la cible possède donc une longueur minimale. Une exploration en profondeur peut s’enfoncer dans une chaîne longue et trouver une solution avant d’avoir essayé une autre branche courte. La largeur ne minimise pas la somme : elle organise les candidats par nombre de cartes.
L’interface fournie est :file_init() crée une file vide ;file_ajoute(f, x) enfile ;file_retire(f) retire le plus ancien et lève ValueError si la file est vide ;file_est_vide(f) teste son état. Les blancs concernent l’initialisation, la condition de boucle, la cible comparée et le chemin contrôlé.
Question 7
#Compléter explore à l’aide de file_init, file_ajoute, file_retire et file_est_vide. La fonction doit renvoyer une chaîne minimale ouNone.
PythonTrouver la somme avec le moins de cartesÉcrivez votre solution et mettez-la à l’épreuve
Écrivez explore(plateau, cible). Une chaîne commence sur n’importe quelle case, progresse vers une des huit voisines (diagonales comprises), sans revisiter une case. Renvoyez une liste de coordonnées dont la somme vaut cible, avec le moins de cases possible, ou None si aucune chaîne ne convient. Les helpers de file et de voisinage sont fournis. Plusieurs réponses minimales peuvent être correctes. Les petites grilles de test sont des exemples complémentaires.
def explore(plateau, cible):
# À vous de jouer
passLes cas de test proposés :
- Une carte suffit ailleurs sur le plateau : Tous les départs doivent être explorés avant leurs prolongements. La case 4 donne une chaîne de longueur 1.
- La diagonale est un vrai voisinage : Les cases 1 et 4 se touchent en diagonale et suffisent pour faire 5.
- Trois cases, aucune répétition : Les trois cases d’une ligne donnent 10. Le chemin doit respecter les adjacences.
- La cible est impossible : Aucune chaîne parmi les cases 2 et 4 ne totalise 3 ; une file épuisée doit mener à
None. - Ne pas réutiliser une carte : Faire 2 + 3 + 2 serait possible seulement en reprenant la première case, ce qui est interdit.
- Les branches gardent leur propre historique : 11 demande 1 + 2 + 8. Le tableau ne change pas et une visite dans un candidat ne condamne pas la case pour tous les autres.
Indice
La file contient des chemins ; le test de répétition porte sur le chemin courant.
Comprendre la correction
def explore(plateau, cible):
n, m = len(plateau), len(plateau[0])
a_visiter = file_init()
for i in range(n):
for j in range(m):
file_ajoute(a_visiter, [(i, j)])
while not file_est_vide(a_visiter):
chemin = file_retire(a_visiter)
if chaine_evalue(plateau, chemin) == cible:
return chemin
i0, j0 = chemin[-1]
for i, j in cartes_voisines(n, m, i0, j0):
if (i, j) not in chemin:
file_ajoute(a_visiter, chemin + [(i, j)])
return NoneToutes les positions de départ sont mises en file avant la boucle. Chaque élément de la file est un chemin complet ; on le prolonge par chaque voisine non encore dans ce chemin. Un marquage global des cases serait incorrect ici : la même case peut appartenir à plusieurs candidats avec des sommes et des historiques différents. Seule sa répétition dans une même chaîne est interdite.
L’expression chemin + [(i,j)] crée une nouvelle liste pour chaque branche. Modifier chemin en place ajouterait successivement plusieurs voisines au même candidat et mélangerait les branches. La file FIFO garantit l’ordre des longueurs. La terminaison vient du nombre fini de chemins sans répétition, mais leur quantité peut être très grande ; ce code ne promet pas une recherche rapide pour tous les plateaux.
Question 8
#Sans écrire de code, expliquer comment modifier explore pour renvoyer la liste de tous les chemins solutions.
Indice
Remplacer le retour immédiat par une mémorisation, puis épuiser la recherche.
Comprendre la correction
Créer une liste de solutions vide avant la boucle. Lorsqu’un chemin atteint la cible, l’ajouter à cette liste au lieu de le retourner immédiatement. Continuer le traitement de la file jusqu’à son épuisement, puis retourner la liste, éventuellement vide. On conserve la contrainte de non-répétition dans chaque chemin.
La question demande tous les chemins solutions, pas seulement les plus courts. Sur des cartes strictement positives comme le plateau donné, prolonger une somme déjà égale à la cible ne peut plus fournir une autre solution, mais cette coupure doit être justifiée par la positivité. L’extension parle d’entiers : avec des zéros ou des négatifs, il peut être nécessaire de continuer à prolonger une solution pour en trouver une plus longue. Ne pas appliquer un élagage sans vérifier les valeurs autorisées.
Une cible, quelle chaîne minimale ?Un atelier pour expérimenter
Changez la cible sur le plateau réel. Le moteur explore les chaînes par longueur croissante ; il peut écarter une somme trop grande ici parce que toutes les cartes sont strictement positives.
Lire le résultat de l’expérience initiale
Solution minimale en3 cartes.
Les voisins incluent les diagonales. Une même position ne peut pas réapparaître ; deux positions portant le même nombre restent distinctes.
| Ligne | Colonne | Carte | Somme |
|---|---|---|---|
| 0 | 0 | 6 | 6 |
| 0 | 1 | 7 | 13 |
| 1 | 1 | 14 | 27 |
La file garantit la minimalité en longueur ; les règles de voisinage garantissent la validité de la chaîne.
Revoir les notions de cet exercice
Exercice 3 · 8 points
Radiosondes : nettoyer les données, chercher dans un ABR et interroger SQL
Un club de radioamateurs retrouve des radiosondes après leur vol et répertorie leurs positions. Chaque mesure est horodatée et géolocalisée. Le contexte cite sondehub.org comme site de suivi, mais aucune connexion externe n’est nécessaire pour résoudre les questions. Les données ci-dessous sont celles du sujet ; le dictionnaire enregistrement sert aux premières questions.
enregistrement = {'num_serie':623, 'altitude':1150.0,
'datetime':'2024-06-27T23:36:01.000000Z',
'latitude':38.38825, 'longitude':27.09004}Question 1
#Écrire l’instruction console qui affiche 38.38825 depuis enregistrement.
Indice
Repérez la clé associée à cette valeur.
Comprendre la correction
print(enregistrement['latitude'])Le nom de clé est une chaîne entre guillemets. L’accès utilise le dictionnaire, sans supposer une position numérique des champs.
La chaîne ISO8601 est formée de la date, du séparateurT, de l’heure et de l’indication de fuseauZ. La fonction fournie est :
def nettoyage_datetime(chaine):
date = ''
horaire = ''
for i in range(10):
date += chaine[i]
for i in range(8):
horaire += chaine[i + 11]
return date, horaireQuestion 2
#Expliquer ce que renvoie nettoyage_datetime sur 2024-06-27T23:36:01.000000Z.
Indice
Le second parcours commence onze caractères après le début.
Comprendre la correction
('2024-06-27', '23:36:01')La première boucle copie les indices 0 à 9, soit la date. La seconde copie les indices 11 à 18, en sautant le séparateurT à l’indice 10. Le résultat est un tuple de deux chaînes ; les fractions de seconde et l’indicationZ ne sont pas conservées. Le code extrait un format fixe, sans convertir de fuseau horaire.
frames = [
{
"num_serie": 623,
"altitude": 620.1,
"datetime": "2024-07-01T01:58:55.000Z",
"latitude": 43.20223,
"longitude": -72.05708
},
{
"num_serie": 500,
"altitude": 6375.75,
"datetime": "2024-07-01T23:35:32.000Z",
"latitude": -20.8759,
"longitude": 55.58805
},
{
"num_serie": 623,
"altitude": 622.6,
"datetime": "2024-07-01T01:59:01.000Z",
"latitude": 43.20224,
"longitude": -72.05711
},
{
"num_serie": 700,
"altitude": 60000,
"datetime": "2024-07-01T11:51:23.000000Z",
"latitude": 40.87873,
"longitude": 29.09587
}
]Question 3
#Donner len(frames) et len(frames[1]) sur l’extrait.
Indice
Les deux appels ne portent pas sur le même type de conteneur.
Comprendre la correction
len(frames) vaut 4 : il y a quatre enregistrements. len(frames[1]) vaut 5 : le second enregistrement est un dictionnaire possédant cinq clés. La longueur d’un dictionnaire compte ses associations, pas le nombre de caractères de ses valeurs.
Question 4
#Écrire detecter_anomalie : renvoyer si l’altitude est négative ou strictement supérieure à35000m.
Indice
Tester explicitement les deux bornes et les valeurs juste à l’extérieur.
Comprendre la correction
def detecter_anomalie(enregistrement):
return enregistrement['altitude'] < 0 or enregistrement['altitude'] > 35000Les deux anomalies sont alternatives, d’où or. Les valeurs 0 et 35000 restent acceptées : les comparaisons sont strictes. L’exemple de la sonde 700 à60000m renvoieTrue. Utiliser and rendrait la condition impossible, puisqu’une même altitude ne peut pas être à la fois négative et supérieure à 35000.
Question 5
#Écrire liste_num_serie qui renvoie chaque numéro de sonde une seule fois, dans l’ordre de première rencontre. Sur frames :[623,500,700].
Indice
Tester l’appartenance avant append.
Comprendre la correction
def liste_num_serie(frames):
numeros = []
for enregistrement in frames:
numero = enregistrement['num_serie']
if numero not in numeros:
numeros.append(numero)
return numerosLa liste numeros mémorise les identifiants déjà rencontrés. La seconde mesure de 623 n’ajoute rien, tandis que 500 et 700 créent de nouvelles entrées. On ne modifie pas frames. Un ensemble éliminerait aussi les doublons, mais le retour demandé conserve l’ordre des premières apparitions ; cette version le rend explicite.
Les coordonnées sont des tuples(latitude, longitude). Exemple :distance_haversine((46.815,6.943),(47.049,7.52828)) donne 51.5 km. Les déplacements de référence sont :
[(46.8125,6.9433),(46.91498,7.23039),(47.00661,7.48054)]
[(46.8125,6.9433),(46.91498,7.23039),(47.00661,7.48054),(47.2512,7.5201)]def distance_totale(dep):
total = 0
for i in range(...):
...
return totalQuestion 6
#Compléter distance_totale(dep) en additionnant les distances entre positions successives. distance_haversine(point 1, point 2) est fournie et renvoie des kilomètres.
Indice
Comptez les intervalles entre les points, pas les points.
Comprendre la correction
def distance_totale(dep):
total = 0
for i in range(len(dep) - 1):
total += distance_haversine(dep[i], dep[i + 1])
return totalUne trajectoire de n positions possède n−1 segments consécutifs. Le dernier indice utilisé dans la boucle vaut n−2, de sorte que i+1 reste au plus n−1. On ne relie pas directement chaque point au premier : cela mesurerait autre chose que le chemin parcouru. Les trajectoires vides ou d’une seule position donnent naturellement 0.
Avec les exemples fournis, les trois premières positions donnent 46,1 km ; l’ajout de la quatrième donne 73,5 km. La fonction ne doit pas reconvertir cette unité ni arrondir chaque segment sans que le contrat de distance_haversine le demande.
Chaque Sonde représente un nœud d’ABR, ordonné par num_serie. Les attributs gauche et droit référencent des sous-arbres ; l’arbre vide est un objet Sonde dont tous les attributs valentNone. Le code utilise le nom droit, même si une phrase de l’énoncé emploie droite.
class Sonde:
def __init__(self, num_serie, latitude, longitude, date, gauche, droit):
self.num_serie = num_serie
self.latitude = latitude
self.longitude = longitude
self.date = date
self.gauche = gauche
self.droit = droit
def est_vide(self):
return self.num_serie is NoneQuestion 7
#Créer sonde 623 avec numéro 623, latitude 38.38825, longitude 27.09004 et date 2024-06-27.
Indice
Distinguez None et un objet représentant un arbre vide.
Comprendre la correction
sonde623 = Sonde(623,38.38825,27.09004,'2024-06-27',
Sonde(None,None,None,None,None,None),
Sonde(None,None,None,None,None,None))Les enfants sont des arbres vides représentés par des instances Sonde, conformément à la convention annoncée. Mettre directement None comme enfants d’un nœud non vide ferait ensuite échouer les appels récursifs à rechercher sur une branche absente. Les deux instances vides ont leurs six attributs à None.
Lire les connexions du schéma
- 623 relié à 500 : gauche
- 623 relié à 700 : droit
- 500 relié à 575 : droit
Question 8
#Insérer successivement 900,650 puis 300 dans l’arbre de la figure 2.
Indice
Comparer à la racine, puis recommencer dans le sous-arbre choisi.
Comprendre la correction
Lire les connexions du schéma
- 623 relié à 500 : gauche
- 623 relié à 700 : droit
- 500 relié à 575 : droit
- 500 relié à 300 : gauche
- 700 relié à 650 : gauche
- 700 relié à 900 : droit
900 est supérieur à 623 puis 700 : il devient le fils droit de 700.650 est supérieur à 623 mais inférieur à 700 : il devient le fils gauche de 700.300 est inférieur à 623 puis 500 : il devient le fils gauche de 500. L’ordre d’insertion ne déplace pas les nœuds existants, en particulier 575 reste le fils droit de 500.
Question 9
#Quel parcours lit les numéros dans l’ordre croissant : préfixe, infixe, suffixe ou largeur ?
Indice
La racine doit être lue entre les valeurs inférieures et supérieures.
Comprendre la correction
Le parcours infixe : sous-arbre gauche, racine, sous-arbre droit. Dans un ABR, tous les numéros du sous-arbre gauche sont plus petits que la racine et ceux du droit plus grands. L’ordre obtenu après les insertions est 300,500,575,623,650,700,900.
def rechercher(..., numero):
if self.est_vide():
return ...
if numero == ...:
return self
elif numero < self.num_serie:
return self.....rechercher(numero)
else:
return self.droit.rechercher(numero)Question 10
#Compléter rechercher(self, numero), qui renvoie l’instance Sonde correspondante ouNone si elle n’existe pas.
Indice
Choisir gauche lorsque numero est inférieur à la racine.
Comprendre la correction
def rechercher(self, numero):
if self.est_vide():
return None
if numero == self.num_serie:
return self
elif numero < self.num_serie:
return self.gauche.rechercher(numero)
else:
return self.droit.rechercher(numero)Le cas vide doit précéder toute comparaison de numéro, car un arbre vide porte num_serie=None. Une égalité renvoie l’objet courant, pas son seul numéro. Sinon, la propriété de l’ABR permet d’écarter un sous-arbre entier. Pour chercher 650, on compare à 623 puis 700, puis on trouve 650 à gauche. Pour un numéro absent, la descente atteint un objet vide et renvoieNone.
La recherche prend un nombre d’étapes proportionnel à la hauteur de l’arbre. Elle est rapide sur un arbre équilibré, mais peut devenir linéaire dans un arbre en chaîne : la structure d’ABR ne garantit pas à elle seule l’équilibrage.
Question 11
#Pourquoi rechercher est-elle récursive ?
Indice
Repérez les appels de la méthode à elle-même sur un autre objet.
Comprendre la correction
La méthode appelle rechercher sur l’un de ses sous-arbres. Le problème est de même nature, mais porte sur une partie strictement plus petite de l’arbre. Les cas de base sont l’arbre vide et le numéro trouvé. La descente termine sur un arbre fini sans cycle.
La base InventaireSondesRetrouvees comporte les trois relations ci-dessous. On emploiera les noms exacts affichés dans les tableaux : abonne, sonde,info_recuperation. Les consignes utilisent parfois d’autres graphies ; elles seront signalées lorsque cela affecte l’exécution.
abonne.id_abonne | nom | prenom | |
|---|---|---|---|
| 32 | Détoile | Diane | D. Detoile@nsi.fr |
| 15 | Petit | Claire | P. Claire@nsi.fr |
| 24 | Girard | Antoine | A. Girard@nsi.fr |
| 16 | Lemoine | Martin | M. Lemoine@huit.fr |
sonde.num_serie | modele | constructeur | date_lancement |
|---|---|---|---|
| 623 | RS41 | Vaisala | 2024-07-10 |
| 500 | M10 | Météomodem | 2024-07-05 |
| 900 | M20 | Météomodem | 2024-07-17 |
| 480 | RS41 | Vaisala | 2024-06-20 |
| 810 | WxR | Weathex | 2024-06-05 |
info_recuperation.ref | num_serie | id_abonne | date_recup | latitude | longitude |
|---|---|---|---|---|---|
| 10 | 900 | 15 | 2024-08-05 | 47.999 | 2.549 |
| 11 | 500 | 24 | 2024-07-06 | 47.159 | 3.151 |
| 12 | 623 | 15 | 2024-07-18 | 47.257 | 11.974 |
| 13 | 810 | 32 | 2024-06-10 | 44.374 | 22.448 |
Question 12
#Proposer et justifier une clé primaire pour abonne.
Indice
Choisir l’identifiant, puis expliquer les deux contraintes.
Comprendre la correction
id_abonne convient : il identifie chaque membre du club par une valeur unique et non nulle. Les noms et prénoms peuvent être partagés par plusieurs personnes ; leur apparente unicité dans l’extrait ne constitue pas une garantie générale. Une contrainte de clé primaire formalise cette identification dans la base.
Question 13
#Citer les clés étrangères de la relation de récupération.
Indice
Deux relations sont reliées à chaque ligne de récupération.
Comprendre la correction
num_serie référence sonde.num_serie ; id_abonne référence abonne.id_abonne. Elles relient chaque récupération à une sonde et à la personne qui l’a retrouvée. ref identifie la récupération elle-même et joue le rôle de clé primaire ; ce n’est pas la même fonction.
Question 14
#Donner le résultat de SELECT nom, prenom FROM Abonnes WHERE id_abonne>20 sur les extraits.
Indice
Appliquer le filtre aux identifiants tout en contrôlant le nom réel de la table.
Comprendre la correction
Les lignes visées sont celles d’identifiants 32 et 24 : Détoile, Diane et Girard, Antoine. Leur ordre n’est pas garanti sans ORDER BY.
| nom | prenom |
|---|---|
| Détoile | Diane |
| Girard | Antoine |
Attention au nom de table : l’extrait la nomme abonne, tandis que la requête écrit Abonnes au pluriel. Sur une base créée exactement avec le schéma reproduit, cette requête échouerait faute de table correspondante. La requête cohérente est :
SELECT nom, prenom FROM abonne WHERE id_abonne > 20;Question 15
#Insérer la récupération de la sonde 480 par Antoine Girard le 2024-07-10, latitude 47.230, longitude 12.244, avec ref=14.
Indice
Retrouver l’identifiant du membre dans la table abonne.
Comprendre la correction
INSERT INTO info_recuperation (ref,num_serie,id_abonne,date_recup,latitude,longitude)
VALUES (14,480,24,'2024-07-10',47.230,12.244);Antoine Girard porte l’identifiant 24 dans abonne. Les clés 480 et 24 existent déjà dans leurs tables, ce qui permet la création de cette récupération. Les noms de colonnes protègent contre une inversion de latitude et longitude. Le nom InfosRecuperation employé dans la consigne est remplacé par celui du schéma reproduit.
Question 16
#Écrire la requête affichant pour chaque sonde récupérée son numéro de série, le nom de l’abonné et la date de récupération.
SQLRelier les radiosondes récupérées aux abonnésÉcrivez votre solution et mettez-la à l’épreuve
Pour chaque récupération, affichez le numéro de série de la sonde, le nom de l’abonné et la date de récupération. Une sonde sans récupération ne doit pas apparaître.
SELECT ...
FROM ...
WHERE ...;Les cas de test proposés :
- Récupérations de l’extrait officiel : Les quatre récupérations et les lignes nécessaires sont reprises du sujet. La sonde 480 ne possède aucune récupération dans cet extrait.
- Cas complémentaire : plusieurs récupérations pour une sonde : Jeu complémentaire pédagogique, distinct des données officielles. La clé de récupération est
ref: une sonde peut apparaître dans deux récupérations. Les deux événements et les bons abonnés sont conservés.
Indice
Les deux clés étrangères de la récupération donnent les conditions de jointure.
Comprendre la correction
SELECT s.num_serie, a.nom, r.date_recup
FROM sonde AS s
JOIN info_recuperation AS r ON r.num_serie = s.num_serie
JOIN abonne AS a ON a.id_abonne = r.id_abonne;info_recuperation sert de relation de liaison : elle désigne à la fois la sonde et l’abonné. Les alias abrègent les noms sans changer les tables. Une sonde sans récupération n’apparaît pas dans cette jointure interne ; cela correspond à l’objectif de référencer les radiosondes retrouvées. Plusieurs récupérations éventuelles produiraient plusieurs lignes, sauf contrainte supplémentaire que le sujet ne fournit pas.
Suivre la recherche d’une sondeUn atelier pour expérimenter
Choisissez un numéro recherché dans l’ABR après les trois insertions. Observez chaque comparaison et le sous-arbre éliminé jusqu’au résultat.
Lire le résultat de l’expérience initiale
Sonde 650 trouvée.
Chaque comparaison écarte un sous-arbre entier. L’absence est constatée dans un objet représentant un arbre vide.
| Racine examinée | Comparaison | Action |
|---|---|---|
| 623 | Cible plus grande | Aller à droite |
| 700 | Cible plus petite | Aller à gauche |
| 650 | Égalité | Renvoyer cette instance |
La recherche exploite l’ordre de l’ABR ; sa correction dépend aussi de la représentation des arbres vides.
Du sujet à la méthode
Votre prochaine séance de révision
- Préciser l’objectif de chaque parcours avant de choisir sa structure de données.
- Distinguer les objets stockés et les identifiants affichés.
- Contrôler les noms et les conventions du schéma avant d’exécuter une requête.
Retrouver ces notions dans d’autres sujets
Toutes les annales de NSI · Le guide pour préparer le bac NSI 2027
Énoncé : sujet 25-NSIJ1G11 (PDF) · Publication d’origine (nouvel onglet). Corrigé et explications pédagogiques proposés par Sofien.
