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
Mesures de qualité de l’air : modélisation et requêtes
Dans le contexte décrit par le sujet, le BQA du ministère de la Transition écologique et solidaire mandate le LCSQA pour publier nationalement les mesures de qualité de l’air en temps réel. Les observations sont des moyennes horaires produites par des appareils automatiques sur des stations fixes. Les 18 AASQA citées par l’énoncé gèrent ces stations en métropole et outre-mer et sont responsables des données. Les polluants incluent O3, NO, NO2, SO2, PM10 (particules de diamètre inférieur à 10 µm), PM2.5 (inférieur à 2,5 µm), CO, etc. Une station ne mesure pas nécessairement tous les polluants.
Une AASQA gère zéro, une ou plusieurs stations ; chaque station dépend d’une seule AASQA. Une station réalise zéro, une ou plusieurs mesures ; chaque mesure vient d’une seule station. Les clés primaires sont repérées ci-dessous ; les clés étrangères ne sont volontairement pas repérées dans l’énoncé.
| Relation | Attributs et types |
|---|---|
| AASQA | [clé primaire : Id] CHAR(7), Nom VARCHAR(50), FuseauHoraire VARCHAR(50), Siteweb VARCHAR(50) |
| Station | [clé primaire : Id] CHAR(7), Nom VARCHAR(50), Latitude DECIMAL(10,6), Longitude DECIMAL(10,6), Altitude INT, TypeImplantation VARCHAR(50), AASQAId CHAR(7) |
| Mesure | [clé primaire : StationId] CHAR(7), [clé primaire : DateDebut] DATETIME, [clé primaire : DateFin] DATETIME, [clé primaire : Polluant] VARCHAR(50), TypeInfluence VARCHAR(50), Valeur DECIMAL(10,1) |
Les quatre attributs repérés de Mesure forment ensemble sa clé primaire. Les valeurs des exemples sont reproduites comme dans le sujet, y compris les fuseaux de stockage.
| AASQA.Id | Nom | FuseauHoraire | Siteweb |
|---|---|---|---|
| FR072A | AIR BREIZH | UTC | http://www.airbreizh.asso.fr |
| FR004A | AIRPARIF | UTC | http://www.airparif.asso.fr |
| FR061A | LIG’AIR | UTC | https://www.ligair.fr |
| FR065A | QUALITAIR CORSE | UTC | http://www.qualitaircorse.org |
| FR064A | GWAD’AIR | UTC-4 | http://www.gwadair.fr |
| FR077A | HAWA MAYOTTE | UTC+3 | http://www.hawa-mayotte.fr |
| FR069A | ATMO NORMANDIE | UTC | http://www.atmonormandie.fr/ |
| FR068A | ATMO OCCITANIE | UTC | http://atmo-occitanie.org/ |
| FR071A | ATMO AUVERGNE-RHONE-ALPES | UTC | http://www.atmo-auvergnerhonealpes.fr/ |
| FR076A | ATMO BOURGOGNE-FRANCHE-COMTE | UTC | https://atmo-bfc.org/ |
| FR074A | ATMO HAUTS DE FRANCE | UTC | http://www.atmo-hdf.fr |
| Station.Id | Nom | Latitude | Longitude | Altitude | TypeImplantation | AASQAId |
|---|---|---|---|---|---|---|
| FR37040 | ABYMES RN1 | 16.254086 | -61.53713 | 13 | Périurbaine | FR064A |
| FR05083 | Gonfreville l Orcher | 49.50273 | 0.232486 | 80 | Urbaine | FR069A |
| FR33203 | Annecy Rocade | 45.9097 | 6.11825 | 452 | Urbaine | FR071A |
| FR07056 | Pays du Mezenc | 44.98377 | 4.226006 | 1191 | Rurale régionale | FR071A |
| FR04143 | Paris Centre | 48.859 | 2.351 | 37 | Urbaine | FR004A |
| StationId | DateDebut | DateFin | Polluant | TypeInfluence | Valeur (µg/m³) |
|---|---|---|---|---|---|
| FR05083 | 15/04/2022 00:00 | 15/04/2022 01:00 | SO2 | Industrielle | 3.3 |
| FR05083 | 15/04/2022 01:00 | 15/04/2022 02:00 | SO2 | Industrielle | 3.3 |
| FR33203 | 15/04/2022 07:00 | 15/04/2022 08:00 | PM10 | Trafic | 66.5 |
| FR33203 | 15/04/2022 08:00 | 15/04/2022 09:00 | PM10 | Trafic | 45 |
| FR07056 | 15/04/2022 13:00 | 15/04/2022 14:00 | O3 | Fond | 101.2 |
| FR07056 | 15/04/2022 14:00 | 15/04/2022 15:00 | O3 | Fond | 102.7 |
Mots-clés autorisés : SELECT, FROM, WHERE, JOIN ON, INSERT INTO VALUES, UPDATE SET, COUNT, AND, OR, DISTINCT. DISTINCT retire les lignes en double du résultat.
Question 1
#Expliquer le rôle d’une clé primaire.
Indice
Une clé composée doit être considérée comme un tuple complet.
Comprendre la correction
Elle identifie chaque ligne d’une relation de manière unique. Elle peut comporter plusieurs attributs : c’est alors la combinaison de leurs valeurs qui doit être unique, et ses composantes ne peuvent être NULL. Sans cette identité, deux mesures pourtant différentes pourraient être confondues.
Question 2
#Donner les quatre attributs de la clé primaire de Mesure.
Indice
Lisez les quatre attributs soulignés dans le schéma.
Comprendre la correction
StationId, DateDebut, DateFin et Polluant. Une station peut mesurer plusieurs polluants sur le même intervalle et un même polluant à des heures différentes. Ni la station ni la valeur mesurée ne suffisent seules à identifier une observation.
Question 3
#Expliquer le rôle d’une clé étrangère.
Indice
Une mesure ne doit pas désigner une station inexistante.
Comprendre la correction
Une clé étrangère relie une ligne à une ligne référencée d’une autre relation, ou éventuellement de la même relation. La valeur doit correspondre à une clé existante dans la table cible, sauf NULL si le schéma l’autorise. Cette contrainte évite les références orphelines.
Question 4
#Donner un exemple de clé étrangère dans ce modèle.
Indice
Suivez le lien un-à-plusieurs d’une association vers ses stations.
Comprendre la correction
Station.AASQAId référence AASQA.Id. Autre exemple : Mesure.StationId référence Station.Id. Mentionner la colonne et sa cible rend la relation non ambiguë.
Partie B : alimenter la base
De nouvelles mesures doivent être insérées quotidiennement.
Question 5
#Ajouter la mesure PM10 de valeur 34, influence Trafic, le 20 juin 2022 de 10 h à 11 h, pour Annecy Rocade.
Indice
Cherchez d’abord l’identifiant de la station ; son nom ne va pas dans StationId.
Comprendre la correction
INSERT INTO Mesure
(StationId, DateDebut, DateFin, Polluant, TypeInfluence, Valeur)
VALUES ('FR33203', '2022-06-20 10:00:00', '2022-06-20 11:00:00',
'PM10', 'Trafic', 34);La table des stations donne l’identifiant FR33203. Les dates sont écrites au format ISO usuel pour DATETIME, même si le tableau pédagogique les affiche au format jour/mois/année. La mesure vaut 34 µg/m³ ; la valeur numérique ne nécessite pas de guillemets.
La clé composée explique pourquoi on peut conserver deux valeurs identiques de3,3 aux deux premières lignes de l’extrait : leurs heures diffèrent, donc ce sont deux observations distinctes. Inversement, réinsérer exactement la même station, le même intervalle et le même polluant violerait la clé primaire même si la valeur mesurée était différente. Une correction de mesure relèverait alors d’une mise à jour, pas d’un ajout concurrent.
Question 6
#L’ajout d’une mesure O3 de valeur 13,5, influence Fond, le 20 juin 2022 de 00 h à 01 h pour FR41017 produit IntegrityError: FOREIGN KEY constraint failed. Expliquer.
Indice
Le message porte sur une référence, pas sur la valeur du polluant.
Comprendre la correction
La valeur FR41017 ne correspond pas à un identifiant de station présent dans la table Station au moment de l’insertion. La référence de Mesure.StationId serait orpheline ; le SGBD refuse donc l’écriture. Il faut vérifier l’identifiant ou créer préalablement la station avec une association AASQA valide. Le seul extrait des données ne suffirait pas à conclure à son absence ; c’est l’erreur d’intégrité explicitement fournie qui la révèle ici.
Partie C : exploiter la base
Question 7
#Afficher le nom et le site web des AASQA.
Indice
Une seule table contient déjà les deux informations.
Comprendre la correction
SELECT Nom, Siteweb
FROM AASQA;Aucun filtre n’est demandé : toutes les associations sont concernées. La projection contient seulement les deux attributs utiles.
Question 8
#Afficher le nombre total de stations.
Indice
Ne confondez pas une table complète et son extrait imprimé.
Comprendre la correction
SELECT COUNT(*)
FROM Station;COUNT(*) compte toutes les lignes de la table, pas seulement celles montrées dans l’extrait. On ne peut donc pas annoncer 5 comme total de la base : l’énoncé ne donne que quelques occurrences.
SELECT Station.Nom, Station.TypeImplantation
FROM AASQA JOIN Station ON AASQA.Id = Station.AASQAId
WHERE AASQA.Nom = 'ATMO AUVERGNE-RHONE-ALPES';Question 9
#Expliquer la requête donnée.
Indice
Le SELECT décrit les colonnes affichées, le WHERE le groupe de stations retenu.
Comprendre la correction
Elle renvoie le nom et le type d’implantation de toutes les stations gérées par ATMO AUVERGNE-RHONE-ALPES. La jointure associe une station à son AASQA, puis WHERE ne retient que l’association nommée. Dans l’extrait, cela donne Annecy Rocade / Urbaine et Pays du Mezenc / Rurale régionale.
Pour les questions 10 à 12, Air Pays de la Loire et la station Mazagran sont connues dans la base, même si elles ne figurent pas dans les extraits.
Question 10
#Afficher DateDebut, DateFin et Valeur des mesures PM10 de la station Mazagran.
Indice
Reliez Mesure.StationId à Station.Id.
Comprendre la correction
SELECT m.DateDebut, m.DateFin, m.Valeur
FROM Mesure AS m
JOIN Station AS s ON m.StationId = s.Id
WHERE s.Nom = 'Mazagran' AND m.Polluant = 'PM10';Le nom de station appartient à Station alors que le polluant et les valeurs appartiennent à Mesure. La jointure est donc nécessaire, sauf à connaître et utiliser l’identifiant exact, qui n’est pas fourni.
Question 11
#Afficher les polluants de Mazagran sans doublons.
Indice
DISTINCT porte ici sur une seule colonne : Polluant.
Comprendre la correction
SELECT DISTINCT m.Polluant
FROM Mesure AS m
JOIN Station AS s ON m.StationId = s.Id
WHERE s.Nom = 'Mazagran';Un polluant peut être mesuré de nombreuses fois. DISTINCT retire ses répétitions dans la projection afin d’obtenir la liste des polluants observés, pas une ligne par heure de mesure.
L’emplacement de DISTINCT est lié à la projection. SELECT DISTINCT Polluant donne un polluant par ligne ; SELECT DISTINCT Polluant,DateDebut conserverait plusieurs lignes du même polluant à des heures différentes. Le SGBD déduplique les lignes complètes du résultat, pas chaque colonne indépendamment.
Question 12
#Afficher le nom des stations gérées par Air Pays de la Loire ainsi que valeur, début et fin de leurs mesures de NO2.
SQLRelier les stations aux mesures de NO2Écrivez votre solution et mettez-la à l’épreuve
Affichez le nom des stations gérées par Air Pays de la Loire, ainsi que Valeur, DateDebut et DateFin de chacune de leurs mesures de NO2. Ne regroupez pas les mesures distinctes.
SELECT ...
FROM ...
WHERE ...;Les cas de test proposés :
- Extraits officiels : association demandée non affichée : Les six mesures et les trois stations correspondantes proviennent des extraits officiels. Air Pays de la Loire est annoncée comme présente dans la base complète mais absente des extraits ; ce jeu restreint est donc vide pour la requête.
- Cas complémentaire : association et polluant : Jeu complémentaire pédagogique, distinct des données officielles. Une mesure du bon polluant dans une autre association est exclue, comme un autre polluant dans la bonne station.
- Cas complémentaire : deux mesures distinctes de stations homonymes : Jeu complémentaire pédagogique, distinct des données officielles. Les noms de stations ne sont pas des clés. Deux mesures distinctes peuvent donner les mêmes colonnes projetées :
DISTINCTles fusionnerait à tort.
Indice
Les trois tables apportent chacune une information nécessaire.
Comprendre la correction
SELECT s.Nom, m.Valeur, m.DateDebut, m.DateFin
FROM AASQA AS a
JOIN Station AS s ON s.AASQAId = a.Id
JOIN Mesure AS m ON m.StationId = s.Id
WHERE a.Nom = 'Air Pays de la Loire'
AND m.Polluant = 'NO2';Les deux jointures parcourent la chaîne association → station → mesure. Les stations sans mesure NO2 ne produisent aucune ligne avec cette jointure interne, ce qui correspond aux mesures demandées. Les alias évitent l’ambiguïté entre les différentes colonnes Nom.
Une projection suffit-elle à retirer les répétitions ?Un atelier pour expérimenter
Filtrez les six mesures de l’extrait par polluant et choisissez de conserver les observations ou uniquement les polluants distincts. Comparez le nombre de lignes retournées.
Lire le résultat de l’expérience initiale
6 ligne(s) dans le résultat
Le filtre porte sur les lignes de mesures. DISTINCT s’applique ensuite aux colonnes sélectionnées ; deux mesures du même polluant ne deviennent une ligne que si les autres colonnes ne sont pas projetées.
| Station | Début | Polluant | Valeur |
|---|---|---|---|
| FR05083 | 00:00 | SO2 | 3.3 |
| FR05083 | 01:00 | SO2 | 3.3 |
| FR33203 | 07:00 | PM10 | 66.5 |
| FR33203 | 08:00 | PM10 | 45 |
| FR07056 | 13:00 | O3 | 101.2 |
| FR07056 | 14:00 | O3 | 102.7 |
Une base sépare l’identité d’une mesure, son classement par polluant et les liens vers sa station.
Revoir les notions de cet exercice
Exercice 2 · 6 points
Taquin et recherche de texte : deux parcours à ne pas interrompre trop tôt
Le taquin déplace une tuile voisine dans la case vide jusqu’à ranger les nombres. Pour une grille 3 × 3, le vide vaut ici 0 et la position finale le place en haut à gauche. Les deux parties de l’exercice sont indépendantes.
| Ligne | Position initiale de la figure 1 | Position finale |
|---|---|---|
| 1 | 3, 1, 2 | vide, 1, 2 |
| 2 | 7, 5, vide | 3, 4, 5 |
| 3 | 4, 6, 8 | 6, 7, 8 |
La figure 2 présente les douze positions atteignables sur un taquin 2 × 2, en cycle. Chaque ligne ci-dessous est reliée à la suivante, et la dernière à la première. « 0 » désigne le vide :
| Position | Ligne haute | Ligne basse | Position suivante |
|---|---|---|---|
| 1 | 0, 1 | 2, 3 | 2 |
| 2 | 1, 0 | 2, 3 | 3 |
| 3 | 1, 3 | 2, 0 | 4 |
| 4 | 1, 3 | 0, 2 | 5 |
| 5 | 0, 3 | 1, 2 | 6 |
| 6 | 3, 0 | 1, 2 | 7 |
| 7 | 3, 2 | 1, 0 | 8 |
| 8 | 3, 2 | 0, 1 | 9 |
| 9 | 0, 2 | 3, 1 | 10 |
| 10 | 2, 0 | 3, 1 | 11 |
| 11 | 2, 1 | 3, 0 | 12 |
| 12 | 2, 1 | 0, 3 | 1 |
Question 1
#Représenter une position des trois tuiles inaccessible depuis la position finale.
Indice
Conservez le vide en place et échangez seulement deux tuiles numérotées.
Comprendre la correction
| Ligne | Colonne 1 | Colonne 2 |
|---|---|---|
| 1 | Vide | 2 |
| 2 | 1 | 3 |
Par exemple [0, 2, 1, 3]. Cette position n’apparaît pas parmi les douze sommets du cycle des positions accessibles. Elle échange seulement les tuiles 1 et 2 de la solution. La question peut se résoudre par comparaison exhaustive avec la figure, sans invoquer une formule de parité non donnée.
Question 2
#Quelle structure modélise le mieux les passages d’une position à l’autre ? Justifier.
Indice
Une configuration possède plusieurs voisines et on peut revenir à une configuration déjà visitée.
Comprendre la correction
Un graphe non orienté : un sommet représente une configuration et une arête représente un glissement autorisé. Chaque coup est réversible, ce qui donne les deux sens. Des cycles existent ; une structure d’arbre ne représenterait pas directement tous ces retours.
Une position 3 × 3 est lue de gauche à droite et de haut en bas. Les deux positions de la figure 1 deviennent [3, 1, 2, 7, 5, 0, 4, 6, 8] et [0, 1, 2, 3, 4, 5, 6, 7, 8].
Question 3
#Écrire une assertion qui interrompt le programme si pos ne contient pas neuf éléments.
Indice
assert reçoit une condition qui doit être vraie.
Comprendre la correction
assert len(pos) == 9Cette vérification porte uniquement sur la longueur. Elle ne prouve pas à elle seule que tous les nombres de 0 à 8 sont présents exactement une fois.
def est_finale(pos):
for i in range(len(pos)):
if ...:
return ...
return ...Question 4
#Compléter est_finale pour tester si pos est la position finale.
Indice
Cherchez un contre-exemple à l’égalité case par case.
Comprendre la correction
def est_finale(pos):
for i in range(len(pos)):
if pos[i] != i:
return False
return TrueDans la grille finale, chaque case contient exactement son indice. Une seule différence suffit à conclure False. True est renvoyé seulement après toutes les vérifications ; le placer dans la boucle validerait à tort des listes dont seul le premier élément est correct. On suppose la longueur validée auparavant.
def case_vide(pos):
res = ...
for i in range(9):
...
return rescase_vide([3, 1, 2, 7, 5, 0, 4, 6, 8]) → 5
case_vide([3, 0, 2, 7, 5, 0, 4, 6, 8]) → -1Question 5
#Compléter case_vide : renvoyer l’unique indice de 0, ou -1 si zéro est absent ou apparaît plusieurs fois.
Indice
Il faut mémoriser une première découverte sans arrêter trop tôt le parcours.
Comprendre la correction
def case_vide(pos):
res = -1
for i in range(9):
if pos[i] == 0:
if res != -1:
return -1
res = i
return resres = -1 signifie qu’aucun zéro n’a encore été trouvé. Au premier zéro, on conserve son indice. Au second, on peut immédiatement refuser la position. Ne pas renvoyer l’indice dès le premier zéro : cela empêcherait de détecter un doublon plus loin.
Trois familles de tests sont nécessaires : un zéro unique, aucun zéro et deux zéros. Une fonction qui renvoie au premier zéro réussit le premier test mais échoue à détecter le troisième. Le stockage temporaire de l’indice permet précisément de continuer à vérifier l’unicité tout en conservant la réponse à renvoyer.
Question 6
#Si le vide est à l’indice i, donner les quatre indices susceptibles de désigner une tuile déplaçable, sans éliminer les indices invalides aux bords.
Indice
La largeur de la grille est trois.
Comprendre la correction
i - 1, i + 1, i - 3, i + 3. Les écarts de 1 correspondent aux colonnes adjacentes, ceux de 3 aux lignes adjacentes. Le sujet demande les candidats seulement ; en code réel, il faudrait rejeter les dépassements et les faux voisins entre la fin d’une ligne et le début de la suivante.
La fonction voisins(pos) est disponible et renvoie toutes les positions obtenues en un déplacement.
def est_possible(pos):
if est_finale(pos):
return True
visites = []
for v in voisins(pos):
if v not in visites:
visites.append(v)
if est_possible(v):
return TrueQuestion 7
#Pourquoi est_possible semble-t-elle ne jamais terminer ? Proposer une solution sans la programmer.
Indice
Le mouvement inverse est toujours autorisé : que se passe-t-il dans l’appel suivant ?
Comprendre la correction
Chaque appel recrée sa propre liste vide visites. Le souvenir des positions déjà rencontrées n’est donc pas partagé : un coup peut revenir vers la position précédente puis recommencer indéfiniment. Il faut un ensemble ou une liste de positions visitées commun à tout le parcours, passé en paramètre, et y marquer la position courante avant d’explorer ses voisins. Il faut aussi renvoyer False lorsque toutes les branches échouent. Une version itérative avec pile ou file évite de surcroît la limite de récursion sur ce grand graphe.
Pour un état A ayant pour voisin B, B possède aussi A comme voisin, puisque le déplacement inverse est légal. Avec une nouvelle liste vide à chaque appel, l’appel sur B n’a aucun souvenir de A et repart immédiatement vers lui. Une collection partagée ne suffit pas si l’on oublie d’y placer le sommet courant avant d’explorer : le moment du marquage fait partie de la preuve de terminaison.
Partie B : recherche de mots
Des mots acceptés sont stockés dans un texte, séparés par des espaces. Pour rechercher un mot entier, on cherche le mot encadré d’espaces. Cela suppose que le texte est aussi encadré par des espaces si l’on veut traiter uniformément ses premier et dernier mots.
Question 8
#Nommer un algorithme de recherche de motif plus efficace que la recherche naïve.
Indice
Les décalages doivent être calculés à partir de ce qui a été comparé.
Comprendre la correction
Boyer-Moore. Il compare le motif depuis la droite et utilise les informations d’une discordance pour justifier des décalages parfois supérieurs à un caractère. Son intérêt ne se réduit pas à décaler systématiquement de la longueur du motif, ce qui peut manquer une occurrence.
def recherche(motif, texte):
"""Renvoie le premier indice du motif, ou -1 si absent."""
n = len(texte)
m = len(motif)
i = 0
while i + m - 1 < n:
j = m - 1
while texte[i+j] == motif[j]:
if j == 0:
return i
j = j - 1
i = i + m
return -1Question 9
#Quel est le résultat de recherche("na", "banana") ?
Indice
Suivez le code fourni, même s’il sera ensuite remis en question.
Comprendre la correction
2. À i = 0, le a final correspond puis n diffère de b, donc échec. Le code décale de m = 2. À i = 2, les caractères texte[3] = a puis texte[2] = n correspondent : la fonction renvoie 2. L’autre occurrence à l’indice 4 n’est pas examinée.
| Alignement i | Comparaison depuis la droite | Décision |
|---|---|---|
| 0 | texte[1]=a égale motif[1]=a, puis texte[0]=b diffère de n | Décaler de2 |
| 2 | texte[3]=a puis texte[2]=n correspondent | Renvoyer2 |
Question 10
#Donner un motif et un texte pour lesquels la fonction ne renvoie pas le résultat attendu, en justifiant.
Indice
Placez une occurrence à un indice que les sauts de longueur m ne visiteront jamais.
Comprendre la correction
Avec motif = "ab" et texte = "xaby", la réponse correcte est 1 mais la fonction renvoie -1. Elle teste i = 0 puis i = 2 et ne teste jamais l’alignement i = 1. Décaler de toute la longueur du motif après n’importe quel échec est donc injustifié. Remplacer le décalage par 1 rétablit une recherche correcte pour un motif non vide, mais perd les optimisations de Boyer-Moore. Le code nécessite aussi un traitement séparé du motif vide.
Détecter une occurrence sautée par le programmeUn atelier pour expérimenter
Choisissez un cas et comparez les alignements espacés de m caractères aux alignements espacés d’un seul caractère. Les résultats affichent la première occurrence détectée.
Lire le résultat de l’expérience initiale
Le programme renvoie -1
La première occurrence réelle est à l’indice 1. Un saut doit être justifié pour ne pas ignorer une occurrence intermédiaire.
| Alignement i | Fenêtre testée | Verdict |
|---|---|---|
| 0 | xa | Discordance |
| 2 | by | Discordance |
L’optimisation ne doit jamais supprimer un cas possible sans preuve. Un contre-exemple court rend une erreur immédiatement visible.
Exercice 3 · 8 points
Routes IP et arbre de préfixes par octets
Une IPv4 comporte quatre octets, soit 32 bits. Dans a.b.c.d/n, les n bits de gauche forment la partie réseau, les autres la partie machine. Voici la table de R0 :
$ ip route show scope global table 100
default via 10.0.0.5 dev out2
172.16.0.0/25
nexthop via 10.0.0.7 dev out3 weight 1
nexthop via 10.0.0.9 dev out4 weight 1
172.16.0.10 nexthop via 10.0.0.3 dev out1
172.16.0.20 nexthop via 10.0.0.3 dev out1
172.16.0.30 nexthop via 10.0.0.3 dev out1
172.16.0.40 nexthop via 10.0.0.3 dev out1La figure 1 du calculateur IPv4 pour 172.16.0.0/25 fournit :
| Propriété | Valeur |
|---|---|
| CIDR | 25 |
| Masque | 255.255.255.128 |
| Masque inverse | 0.0.0.127 |
| Adresse réseau | 172.16.0.0 |
| Première adresse hôte | 172.16.0.1 |
| Dernière adresse hôte | 172.16.0.126 |
| Broadcast | 172.16.0.127 |
| Adresses hôtes disponibles | 126 |
| Plage de filtre incluant réseau et broadcast | 172.16.0.0 à 172.16.0.127, soit 128 adresses |
Question 1
#Donner le nombre maximal de machines sur 172.16.0.0/25.
Indice
Le nombre d’adresses d’un filtre n’est pas le nombre d’hôtes utilisables.
Comprendre la correction
126 : il reste 32 - 25 = 7 bits machine, donc 2⁷ = 128 adresses, moins réseau et diffusion. La figure distingue à juste titre les 128 adresses du bloc de ses 126 adresses hôtes.
| IP destination | Prochain saut |
|---|---|
| 172.16.0.10 | À compléter |
| 172.16.0.100 | À compléter |
| 172.16.0.200 | À compléter |
Question 2
#Donner le prochain saut pour 172.16.0.10, 172.16.0.100 et 172.16.0.200.
Indice
Une route hôte exacte l’emporte sur une route de réseau plus générale.
Comprendre la correction
| Destination | Prochain saut | Justification |
|---|---|---|
| 172.16.0.10 | 10.0.0.3 | Route exacte, prioritaire sur /25 |
| 172.16.0.100 | 10.0.0.7 ou 10.0.0.9 | Deux prochains sauts de poids égal pour /25 |
| 172.16.0.200 | 10.0.0.5 | Hors du /25 et aucune route spécifique : défaut |
Le routeur privilégie la route la plus spécifique, c’est-à-dire le préfixe le plus long. Pour .100, la table autorise deux sorties ; on ne peut en choisir une seule à partir de ces informations.
Lire les connexions du schéma
- PC .10 relié à S2
- S2 relié à R2
- R2 relié à R0
- R0 relié à R1
- R0 relié à R3
- R0 relié à R4
- R3 relié à S1
- R4 relié à S1
S2 relie les machines 172.16.0.10, .20 et .30 à R2. R1 dessert Internet. S1 dessert 172.16.0.0/25. R5 dessert déjà 192.168.0.0/24 mais n’a pas encore de lien vers le reste du réseau.
Question 3
#Donner les adresses des interfaces de R1, R2, R3 et R4 à joindre depuis R0.
Indice
Associez chaque destination de la table à sa branche du dessin.
Comprendre la correction
| Routeur | Adresse côté R0 |
|---|---|
| R1 | 10.0.0.5 |
| R2 | 10.0.0.3 |
| R3 | 10.0.0.7 |
| R4 | 10.0.0.9 |
R1 donne accès à Internet, donc correspond à la route par défaut. R2 dessert les hôtes .10, .20 et .30 du switch S2 (ainsi que la route hôte .40 de la table). R3 et R4 atteignent le réseau derrière S1 ; les noms out3/out4 permettent l’association représentée dans le schéma.
Question 4
#Compléter le schéma en ajoutant R5-S1, avec R5 à 172.16.0.3, et R5-R2, avec R5 à 10.0.0.1.
Indice
Un routeur possède une adresse distincte sur chaque réseau auquel il est directement connecté.
Comprendre la correction
Lire les connexions du schéma
- PC .10 relié à S2
- S2 relié à R2
- R2 relié à R0
- R0 relié à R1
- R0 relié à R3
- R0 relié à R4
- R3 relié à S1
- R4 relié à S1
| Nouvelle liaison | Adresse de l’interface R5 |
|---|---|
| S1 ↔ R5 | 172.16.0.3 |
| R2 ↔ R5 | 10.0.0.1 |
Ces deux liens s’ajoutent à l’architecture initiale ; la liaison de R5 vers 192.168.0.0/24 est conservée. Les adresses désignent deux interfaces différentes du même routeur.
RIP minimise le nombre de routeurs par lesquels les paquets transitent.
Question 5
#Avec RIP, donner et justifier le coût du chemin de la machine 172.16.0.10 vers 192.168.0.0/24.
Indice
Le sujet exprime ici la métrique comme le nombre de routeurs traversés depuis la machine.
Comprendre la correction
Le chemin passe par machine → S2 → R2 → R5 → réseau 192.168.0.0/24. Il traverse deux routeurs, R2 et R5, donc son coût vaut 2 selon la convention explicitement donnée ici (nombre de routeurs traversés). La portion entre R2 et R5 contient un seul lien inter-routeurs. Les commutateurs ne se comptent pas comme routeurs.
OSPF minimise le coût total. Toutes les liaisons sont Ethernet, sauf R0-R2, R0-R4, R4-S1 et S1-R5 qui sont en fibre optique. Ethernet : 1 Gbit/s ; fibre : 10 Gbit/s.
Question 6
#Calculer le coût Ethernet à 1 Gbit/s et fibre à 10 Gbit/s, avec coût = 10⁹ / BP.
Indice
La bande passante de la fibre est dix fois plus grande.
Comprendre la correction
| Liaison | Calcul | Coût |
|---|---|---|
| Ethernet | 10⁹ / 10⁹ | 1 |
| Fibre optique | 10⁹ / 10¹⁰ | 0,1 |
Le débit doit être converti en bit/s. La constante de référence vaut ici 10⁹, pas 10⁸ comme dans d’autres sujets.
Question 7
#Avec OSPF, donner et justifier le coût du chemin de 172.16.0.10 à 192.168.0.0/24.
Indice
Comparez le lien Ethernet R2-R5 à la chaîne de quatre fibres ; les autres liaisons sont communes.
Comprendre la correction
Le chemin favorise la fibre : machine → S2 → R2 → R0 → R4 → S1 → R5 → réseau destination. La partie variable de R2 à R5 utilise quatre liaisons fibre, donc coûte 0,4, contre 1 pour le lien Ethernet direct R2-R5. En additionnant toutes les liaisons physiques dessinées, machine-S2, S2-R2 et R5-réseau destination apportent trois liaisons Ethernet communes, soit un coût total 3,4 contre 4 pour le chemin direct. L’essentiel du choix est la comparaison 0,4 < 1 ; préciser les extrémités évite de confondre coût de transit inter-routeurs et coût physique de bout en bout. Dans un réseau OSPF réel, un segment Ethernet partagé est modélisé au niveau des interfaces de routage, pas comme un routeur supplémentaire S1 : on applique ici le modèle de liaisons du dessin.
Partie B : arbre de préfixes
| IP destination | Prochain saut |
|---|---|
| 168.16.0.100 | 10.0.0.3 |
| 168.16.0.200 | 10.0.0.4 |
| 203.18.17.10 | 10.0.0.5 |
| 0.0.0.0 | 10.0.0.6 |
| 168.16.0.10 | 10.0.0.7 |
| 203.18.15.30 | 10.0.0.8 |
| 203.18.15.20 | 10.0.0.9 |
| 203.18.15.10 | 10.0.0.10 |
Chaque nœud contient un dictionnaire associant une étiquette à un enfant. Le chemin depuis la racine reconstitue une adresse ; si cette adresse est une destination, le nœud contient son prochain saut. Le texte parle de lettres et de mots, mais le code et la figure découpent effectivement les adresses par octets. Un nœud ayant des enfants représente un préfixe de destinations, et non de prochains sauts comme l’indique par erreur une phrase du sujet.
Figure après les quatre premières insertions :
racine
├─ 168 → 16 → 0
│ ├─ 100 : 10.0.0.3
│ └─ 200 : 10.0.0.4
├─ 203 → 18 → 17 → 10 : 10.0.0.5
└─ 0 → 0 → 0 → 0 : 10.0.0.6Question 8
#Compléter l’arbre pour les huit entrées de la table de routage.
Indice
Ajoutez les nouveaux suffixes sans recopier les branches de préfixe déjà communes.
Comprendre la correction
racine
├─ 168 → 16 → 0
│ ├─ 100 : saut 10.0.0.3
│ ├─ 200 : saut 10.0.0.4
│ └─ 10 : saut 10.0.0.7
├─ 203 → 18
│ ├─ 17 → 10 : saut 10.0.0.5
│ └─ 15
│ ├─ 30 : saut 10.0.0.8
│ ├─ 20 : saut 10.0.0.9
│ └─ 10 : saut 10.0.0.10
└─ 0 → 0 → 0 → 0 : saut 10.0.0.6On partage les préfixes d’octets identiques. Les adresses commencent bien par 168 dans cette partie, et non par 172 comme dans la partie A. Le prochain saut est stocké au nœud terminal ; ce n’est pas une étiquette de branche.
Dans cet arbre, une branche 168-16-0 est partagée par les trois destinations terminées par100,200 et10. Les caractères "1" et "0" ne sont pas des niveaux distincts : l’étiquette "100" est un seul octet stocké comme chaîne. Cela distingue un arbre de préfixes par octets d’un arbre binaire par bits et explique pourquoi les adresses IPv4 complètes utilisent quatre niveaux.
class Noeud:
def __init__(self):
self.saut = ""
self.dict_adjacence = {}
def rechercher_enfant(self, octet):
if not (octet in self.dict_adjacence):
return ...
return ...saut est une chaîne non vide sur un nœud correspondant à une destination. dict_adjacence contient les couples octet/enfant.
Question 9
#Compléter les lignes 7 et 8 de rechercher_enfant.
Indice
La méthode renvoie le nœud enfant, pas l’octet qui l’identifie.
Comprendre la correction
return None
# Après le if :
return self.dict_adjacence[octet]Si la clé octet est absente, on renvoie None. Sinon on lit le nœud associé à cette clé. Le test évite une exception KeyError sur un enfant inexistant.
class Arbre:
def __init__(self):
self.racine = Noeud()
def rechercher(self, ip_dest):
...
def mystere(self, ip_dest, saut):
noeud = self.racine
octets = ip_dest.split(".")
i = 0
while i < len(octets):
noeud_suivant = noeud.rechercher_enfant(octets[i])
if noeud_suivant is not None:
noeud_suivant = Noeud()
noeud.dict_adjacence[octets[i]] = noeud_suivant
noeud = noeud_suivant
i = i + 1
noeud.saut = sautsplit découpe selon un séparateur. Exemple opérationnel : "Bonjour-le-monde".split("-") donne ["Bonjour", "le", "monde"]. Le séparateur doit être exactement le même caractère que celui du texte.
Question 10
#Compléter le corps de Arbre.rechercher(ip_dest), qui renvoie le nœud de destination ou None.
Indice
Conservez le nœud courant et remplacez-le par son enfant à chaque octet.
Comprendre la correction
noeud = self.racine
for octet in ip_dest.split("."):
noeud = noeud.rechercher_enfant(octet)
if noeud is None:
return None
if noeud.saut == "":
return None
return noeudOn descend d’un niveau pour chaque octet. Dès qu’une branche manque, l’adresse est absente. Le contrôle final de saut évite de considérer un simple préfixe non terminal comme une destination. Dans le cadre des IPv4 complètes de quatre octets fournies, tous les terminaux insérés sont bien marqués.
Rechercher 203.18.15.20 visite successivement les enfants "203", "18", "15", "20", puis renvoie le nœud dont saut vaut10.0.0.9. Rechercher 203.18.15.99 partage trois niveaux puis échoue sur "99". Les clés de dict_adjacence sont des chaînes issues de split ; utiliser l’entier203 comme clé ne retrouverait pas la branche "203".
Question 11
#Expliquer et justifier le rôle de mystere.
Indice
Une branche doit être créée quand elle manque, pas quand elle existe déjà.
Comprendre la correction
Son intention est d’insérer une destination et son prochain saut : parcourir les octets, créer les branches manquantes, puis affecter saut au terminal. Mais la condition imprimée est inversée : if noeud_suivant is not None remplace les branches existantes et laisse None quand une branche manque, ce qui provoque une erreur. Il faut la corriger en if noeud_suivant is None:. Avec cette correction, les préfixes existants sont conservés ; réinsérer une même destination met à jour son prochain saut.
Question 12
#Proposer un algorithme qui inverse la table de routage stockée dans l’arbre.
PythonRetrouver toutes les destinations derrière un même prochain sautÉcrivez votre solution et mettez-la à l’épreuve
Implémentez en Python l’algorithme d’inversion demandé : inverser(arbre) renvoie un dictionnaire associant chaque saut à la liste de ses adresses de destination. La racine n’a pas d’octet ; chaque arête est étiquetée par un octet sous forme de chaîne dans dict_adjacence. Un saut non vide désigne une route enregistrée. Le regroupement en listes conserve toutes les destinations si plusieurs utilisent le même saut. Les exemples complémentaires sont construits avec la représentation du sujet, sans modifier l’arbre.
def inverser(arbre):
# À vous de jouer
passLes cas de test proposés :
- Un arbre sans route : Les nœuds sans saut ne créent pas d’entrée vide dans le résultat.
- La destination 203.18.15.20 du sujet : Les quatre octets sont réunis avec des points ; le saut devient la clé de la réponse.
- Partager un préfixe sans mélanger les branches : 100, 200 et 10 sont chacun un octet, pas des niveaux composés de chiffres.
- Deux destinations partagent un saut : Une affectation simple
inverse[saut]= destination écraserait la première route. - Des branches éloignées gardent leur propre chemin : Le préfixe doit être indépendant pour chaque branche sœur. L’arbre reste intact après la lecture.
Indice
Il faut conserver le chemin de la racine au terminal pour retrouver l’adresse complète.
Comprendre la correction
Effectuer un parcours en profondeur en mémorisant la liste des octets du chemin. Lorsqu’un nœud a un saut non vide, reconstituer la destination avec ".".join(prefixe), puis l’ajouter au dictionnaire inverse sous la clé saut. Continuer récursivement pour chaque enfant en ajoutant son octet au chemin. Comme plusieurs destinations peuvent partager un même prochain saut, utiliser une liste de destinations par saut afin de ne rien perdre ; dans la table du sujet, les huit sauts sont distincts.
def inverser(arbre):
inverse = {}
def visiter(noeud, prefixe):
if noeud.saut != "":
destination = ".".join(prefixe)
if noeud.saut not in inverse:
inverse[noeud.saut] = []
inverse[noeud.saut].append(destination)
for octet, enfant in noeud.dict_adjacence.items():
visiter(enfant, prefixe + [octet])
visiter(arbre.racine, [])
return inverseL’algorithme visite chaque nœud une fois. Il ne suffit pas d’échanger aveuglément une clé et une valeur dans un dictionnaire si les valeurs peuvent se répéter.
Pour la table fournie, l’entrée inverse 10.0.0.9 contient la destination203.18.15.20. Si un second réseau utilisait le même prochain saut, sa destination serait ajoutée à cette liste plutôt que d’écraser la première. Le préfixe transmis à chaque appel est construit avec prefixe+[octet], une nouvelle liste : les branches sœurs n’altèrent donc pas réciproquement leurs chemins.
Suivre les octets dans la table de routageUn atelier pour expérimenter
Choisissez une destination existante ou absente, puis avancez dans ses quatre octets. Repérez précisément où deux adresses partagent leurs branches et où une recherche échoue.
Lire le résultat de l’expérience initiale
Préfixe partagé : 203
Le dictionnaire de chaque nœud choisit le prochain octet. Plusieurs adresses partagent une branche tant que leurs préfixes sont identiques.
| Destination encore compatible | Prochain saut |
|---|---|
| 203.18.17.10 | 10.0.0.5 |
| 203.18.15.30 | 10.0.0.8 |
| 203.18.15.20 | 10.0.0.9 |
| 203.18.15.10 | 10.0.0.10 |
Un arbre de préfixes n’est pas un ABR : chaque octet choisit une branche par clé de dictionnaire, sans comparaison gauche/droite.
Du sujet à la méthode
Votre prochaine séance de révision
- Distinguez une valeur absente d’un préfixe existant mais non terminal.
- Une figure de routage et sa table se lisent ensemble : reliez chaque destination à la bonne branche.
- Pour prouver qu’un programme est incorrect, donnez une entrée minimale et tracez son exécution exacte.
Retrouver ces notions dans d’autres sujets
Toutes les annales de NSI · Le guide pour préparer le bac NSI 2027
Énoncé : sujet 26-NSIJ2PO1 (PDF). Corrigé et explications pédagogiques proposés par Sofien.
