Épreuve écrite · 2025 · Jour 1 - 17 juin 2025

Bac NSI 2025 Métropole jour 1

Le sujet de Métropole du 17 juin 2025 combine un inventaire de guitares, un planning Pomodoro et le réseau d’une chaîne de cafés. Il faut savoir raisonner sur une clé étrangère, préserver l’ordre des tâches de même priorité, puis passer du routage à un arbre binaire de recherche. Chaque correction explique ces transitions et les ateliers montrent les conséquences concrètes d’un changement de paramètre.

Les exercices valent 6, 6 et 8 points. Durée officielle : 3 h 30, sans calculatrice. Les 37 questions sont reprises dans leur ordre.

Dans ce sujet

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

La collection de guitares de Slash : SQL et modèle relationnel

Maud veut inventorier la collection de guitares du guitariste Slash. Les clauses SQL proposées sont SELECT, FROM, WHERE, AND, OR, JOIN ... ON, UPDATE, INSERT, DELETE, DISTINCT et ORDER BY. Dans les schémas, la clé primaire est soulignée et les clés étrangères sont précédées de #.

Partie A : une première table

inventaire(id, marque, modele, annee, num_ser, prix), avec id clé primaire. Le numéro de série est unique parmi les guitares d’une même marque. Les prix sont en euros.

idmarquemodeleanneenum_serprix
1GibsonLes Paul Goldtop1956@70562100000
2GibsonLes Paul Goldtop19888173834920000
3GibsonLes Paul Standard1959@90663250000
4GibsonLes Paul Standard19878175753225000
5FenderTelecaster1952000230150000
6FenderTelecaster19658134567310000
7FenderStratocaster1956001359200000
8FenderStratocaster19658175753215000

Question 1

#

Expliquer pourquoi num_ser ne peut pas être une clé primaire de inventaire.

Comprendre la correction

Le numéro n’est unique qu’à l’intérieur d’une marque. Dans l’extrait, 81757532 apparaît pour la Gibson d’identifiant 4 et la Fender d’identifiant 8. Une clé primaire doit identifier une ligne de manière unique dans toute la relation, pas seulement dans un sous-ensemble.

Voir la question dans le sujet PDF, p. 3 (nouvel onglet)
SELECT marque, modele
FROM inventaire
WHERE annee = 1956;

Question 2

#

Donner, sous forme de tableau, le résultat de la requête appliquée à l’extrait fourni.

Comprendre la correction
marquemodele
GibsonLes Paul Goldtop
FenderStratocaster

On sélectionne les lignes d’année 1956, puis on conserve uniquement les deux colonnes demandées. Aucun ordre de lignes n’est garanti en l’absence de ORDER BY.

Voir la question dans le sujet PDF, p. 3 (nouvel onglet)

Question 3

#

Écrire une requête SQL permettant d’obtenir toutes les années du modèle Les Paul Standard dans la collection.

Comprendre la correction
SELECT annee
FROM inventaire
WHERE modele = 'Les Paul Standard';

La projection porte sur l’année et le filtre sur le modèle. Si l’on veut éliminer les années répétées dans un inventaire plus grand, on peut ajouter DISTINCT ; l’extrait renvoie ici 1959 et 1987.

Voir la question dans le sujet PDF, p. 3 (nouvel onglet)

Question 4

#

Écrire une requête SQL permettant d’obtenir tous les modèles de guitares de la marque Gibson par ordre croissant de l’année dans la collection.

Comprendre la correction
SELECT modele
FROM inventaire
WHERE marque = 'Gibson'
ORDER BY annee ASC;

On trie les guitares par année, puis on affiche leur modèle. Un même modèle peut apparaître plusieurs fois, puisqu’il existe plusieurs guitares de ce modèle. Ajouter DISTINCT sans préciser quelle année représenterait un modèle rendrait le sens du tri ambigu.

Voir la question dans le sujet PDF, p. 3 (nouvel onglet)

Question 5

#

L’année de la guitare d’identifiant 1 est en réalité 1957. Écrire la requête qui corrige cette erreur.

Comprendre la correction
UPDATE inventaire
SET annee = 1957
WHERE id = 1;

Le filtre par clé primaire isole la guitare voulue. Sans WHERE, toutes les guitares recevraient l’année 1957.

Voir la question dans le sujet PDF, p. 3 (nouvel onglet)

Partie B : trois relations

Maud adopte désormais marque(id, nom), modele(id, nom, #id_marque) et guitare(id, #id_modele, annee, num_ser, prix). Chaque id est la clé primaire de sa table ; modele.id_marque référence marque.id, et guitare.id_modele référence modele.id. Les extraits de cette partie sont indépendants de la correction d’année effectuée en partie A.

marque.idnom
1Gibson
2Fender
modele.idnomid_marque
1Les Paul Goldtop1
2Les Paul Standard1
3Telecaster2
4Stratocaster2
guitare.idid_modeleanneenum_serprix
111956@70562100000
2119888173834920000
321959@90663250000
4219878175753225000
531952000230150000
6319658134567310000
741956001359200000
8419658175753215000

Question 6

#

Expliquer brièvement et en justifiant dans quel ordre les trois tables doivent être créées.

Comprendre la correction

marque, puis modele, puis guitare. modele.id_marque référence la clé de marque, et guitare.id_modele celle de modele. Créer les tables référencées d’abord permet de déclarer ensuite les contraintes sans dépendance manquante. Le même ordre est nécessaire pour insérer des données respectant immédiatement ces références.

Voir la question dans le sujet PDF, p. 4 (nouvel onglet)

Question 7

#

Écrire une requête SQL permettant d’obtenir le numéro de série et l’année de toutes les guitares Les Paul Standard.

SQLRetrouver les Les Paul StandardÉcrivez votre solution et mettez-la à l’épreuve

Renvoyez le numéro de série et l’année de toutes les guitares dont le modèle porte le nom Les Paul Standard. Ne confondez pas le nom de la marque et celui du modèle.

SELECT ...
FROM ...
WHERE ...;

Les cas de test proposés :

  • Extrait officiel de la collection : Les marques, modèles et huit guitares sont les données de la partie B du sujet, avant toute modification.
  • Cas complémentaire : le modèle change d’identifiant : Jeu complémentaire pédagogique, distinct des données officielles. Le modèle recherché porte cette fois l’identifiant 9. Deux guitares de même année doivent toutes deux être conservées.
Indice

Le nom du modèle est dans modele, l’année dans guitare.

Comprendre la correction
SELECT guitare.num_ser, guitare.annee
FROM guitare JOIN modele ON guitare.id_modele = modele.id
WHERE modele.nom = 'Les Paul Standard';

Le nom du modèle et les caractéristiques d’une guitare sont dans deux tables différentes. La jointure les rapproche grâce à la référence, sans supposer que le modèle gardera toujours l’identifiant 2.

Voir la question dans le sujet PDF, p. 4 (nouvel onglet)

Question 8

#

Slash a offert une guitare à un ami. Écrire une requête pour retirer de la collection la guitare d’identifiant 3.

Comprendre la correction
DELETE FROM guitare WHERE id = 3;

On retire uniquement l’instrument. Son modèle et sa marque peuvent encore décrire d’autres guitares et doivent rester présents.

Voir la question dans le sujet PDF, p. 4 (nouvel onglet)

Question 9

#

Écrire l’ensemble des requêtes pour ajouter la guitare de marque BC Rich, modèle Mockingbird, année 1992, numéro de série 92 R, prix 5000. On peut attribuer les identifiants 3 à la marque, 5 au modèle et 9 à la guitare.

Indice

Une clé étrangère doit référencer une ligne déjà créée.

Comprendre la correction
INSERT INTO marque (id, nom) VALUES (3, 'BC Rich');
INSERT INTO modele (id, nom, id_marque) VALUES (5, 'Mockingbird', 3);
INSERT INTO guitare (id, id_modele, annee, num_ser, prix)
VALUES (9, 5, 1992, '92R', 5000);

On ajoute la marque avant son modèle, puis le modèle avant la guitare. 92R est une chaîne, alors que les identifiants, l’année et le prix sont numériques.

Voir la question dans le sujet PDF, p. 4 (nouvel onglet)

David conseille à Maud la fonction SUM. La syntaxe SELECT SUM(nom_colonne) FROM tab calcule la somme des valeurs d’une colonne.

Question 10

#

Écrire une requête SQL permettant de calculer la valeur totale des Stratocaster de la collection.

Comprendre la correction
SELECT SUM(guitare.prix)
FROM guitare JOIN modele ON guitare.id_modele = modele.id
WHERE modele.nom = 'Stratocaster';

SUM additionne les prix après jointure et filtrage. Dans l’extrait, on obtient 200000 + 15000 = 215000 euros. COUNT compterait seulement les deux instruments.

Voir la question dans le sujet PDF, p. 5 (nouvel onglet)
Du filtre à la jointure, retrouvez les guitaresUn atelier pour expérimenter

Choisissez une marque et une limite d’année. Retrouvez les instruments retenus et la somme de leurs prix ; les identifiants rendent visibles les relations entre tables.

Lire le résultat de l’expérience initiale

8 guitare(s) sélectionnée(s)

Le filtre conserve les lignes ; ORDER BY change seulement leur ordre ; SUM agrège leurs prix. Aucune de ces opérations de lecture ne modifie la collection.

idMarqueModèleAnnéePrix (€)
5FenderTelecaster1952150000
1GibsonLes Paul Goldtop1956100000
7FenderStratocaster1956200000
3GibsonLes Paul Standard1959250000
6FenderTelecaster196510000
8FenderStratocaster196515000
4GibsonLes Paul Standard198725000
2GibsonLes Paul Goldtop198820000

Filtrer des lignes, projeter des colonnes, joindre des relations et agréger des valeurs sont quatre opérations différentes : formulez d’abord ce que vous voulez obtenir.

Revoir les notions de cet exercice

Exercice 2 · 6 points

Files de priorité et planning Pomodoro d’Alice

Alice planifie sa journée dans une liste de tâches. Chaque tâche a un numéro, un nom, une durée estimée entière en minutes, et une durée restante initialement égale à la durée totale. Avancer de n minutes diminue la durée restante de n. Une tâche est terminée dès que cette durée est négative ou nulle.

NuméroNomDuréeDurée restante
1Répondre aux e-mails4545
2Ranger ma chambre6060
3Réviser la NSI9090
4S’entraîner aux échecs3030
5Apprendre le vocabulaire de chinois3030
6Lire Fondation6060
7Écrire ma lettre au Père Noël2020

La classe fournie nomme l’attribut de durée totale duree_initiale, bien que l’introduction l’appelle duree. On conserve ici le nom du code.

class Tache:
    def __init__(self, numero, nom, duree):
        self.numero = numero
        self.nom = nom
        self.duree_initiale = duree
        self.duree_restante = duree

    def __repr__(self):
        return '<t' + str(self.numero) + '>'

Question 1

#

Donner le code qui instancie tache1 pour « Répondre aux e-mails », numéro 1, durée 45, et tache2 pour « Ranger ma chambre », numéro 2, durée 60.

Comprendre la correction
tache1 = Tache(1, 'Répondre aux e-mails', 45)
tache2 = Tache(2, 'Ranger ma chambre', 60)

Le constructeur initialise lui-même la durée restante. On ne passe pas self explicitement : Python fournit l’objet en construction.

Voir la question dans le sujet PDF, p. 7 (nouvel onglet)

On suppose les sept variables tache1 à tache7 créées. Grâce à __repr__, print(tache1) affiche <t1>.

def avancer(self, n):
    ...

Question 2

#

Compléter la méthode avancer(self, n) qui avance la tâche de n minutes.

Comprendre la correction
def avancer(self, n):
    self.duree_restante = self.duree_restante - n

On modifie la durée restante, jamais la durée initiale. Le sujet autorise une valeur négative : elle signifie que le bloc de temps a dépassé le travail nécessaire.

Voir la question dans le sujet PDF, p. 7 (nouvel onglet)

Question 3

#

Compléter la méthode est_terminee, qui renvoie si la tâche est terminée.

Comprendre la correction
def est_terminee(self):
    return self.duree_restante <= 0

Tester seulement l’égalité à zéro serait faux : une tâche de 20 minutes avancée de 25 minutes a une durée restante de -5 et est terminée.

Voir la question dans le sujet PDF, p. 7 (nouvel onglet)

Une priorité est un entier : 1 est minimale et une plus grande valeur est plus prioritaire. Une file stocke des tuples (tache, priorite). Condition 1 : priorités décroissantes du début à la fin. Condition 2 : à priorité égale, premier arrivé, premier servi.

f = [début] (t3,4) (t1,3) (t2,3) (t4,1) (t5,1) [fin]

t3 est la plus prioritaire. t1 précède t2 car elle a été ajoutée avant ; de même t4 précède t5. Aucune tâche n’a la priorité 2.

Question 4

#

Représenter l’état de la file f quand on ajoute successivement la tâche 6 avec la priorité 2 puis la tâche 7 avec la priorité 4, en respectant les deux conditions.

Comprendre la correction
Après t6 : [début] (t3,4) (t1,3) (t2,3) (t6,2) (t4,1) (t5,1) [fin]
Après t7 : [début] (t3,4) (t7,4) (t1,3) (t2,3) (t6,2) (t4,1) (t5,1) [fin]

La tâche 6 s’insère entre les priorités 3 et 1. La tâche 7 a la priorité maximale, mais se place après t3, déjà présente avec la même priorité : l’ordre d’arrivée départage les égalités.

Voir la question dans le sujet PDF, p. 8 (nouvel onglet)
Opération FileEffet
File()Crée une file vide
enfiler(e)Ajoute e à la fin
defiler()Renvoie et retire le premier élément si possible
examiner()Renvoie le premier élément sans le retirer si possible
est_vide()Renvoie si la file est vide
File initiale : [début] (t3,4) (t1,3) (t2,3) (t4,1) (t5,1) [fin]

Question 5

#

En repartant de la file initiale, donner la valeur de f.defiler()[0] et représenter la file après cette instruction.

Comprendre la correction
tache3
[début] (t1,3) (t2,3) (t4,1) (t5,1) [fin]

defiler retire et renvoie le premier tuple ; l’indice 0 en extrait l’objet tâche, représenté par <t3>. On ne renvoie pas la priorité 4.

Voir la question dans le sujet PDF, p. 8 (nouvel onglet)

Question 6

#

En repartant de la même file initiale, donner la valeur de f.examiner()[1] et représenter son contenu après l’instruction.

Comprendre la correction

La valeur est 4. examiner() ne retire rien et l’indice 1 désigne la priorité.

[début] (t3,4) (t1,3) (t2,3) (t4,1) (t5,1) [fin]
Voir la question dans le sujet PDF, p. 8 (nouvel onglet)

On utilise une file auxiliaire. On y transfère les éléments de tête de f dont la priorité est supérieure ou égale à p, on y ajoute (t, p), puis le reste de f. Enfin on transfère tout le contenu auxiliaire dans f.

def ajouter_file_prio(f, t, p):
    f_aux = File()
    while ...:
        ...
    ...enfiler(...)
    while not ...:
        ...
    while not ...:
        ...

Question 7

#

Compléter ajouter_file_prio(f, t, p), qui ajoute le tuple (t, p) à la bonne position dans une file respectant les deux conditions.

PythonInsérer une tâche sans doubler celles qui attendentÉcrivez votre solution et mettez-la à l’épreuve

Écrivez ajouter_file_prio(f, t, p) avec l’interface File fournie. Les couples (tâche, priorité) sont servis par priorité décroissante. À égalité, les tâches déjà en attente passent avant la nouvelle. Modifiez l’objet f existant et ne renvoyez rien. Utilisez examiner, enfiler, defiler et est_vide, sans accéder à la représentation interne de File.

def ajouter_file_prio(f, t, p):
    # À vous de jouer
    pass

Les cas de test proposés :

  • Une file vide : La première insertion ne doit jamais examiner une file vide.
  • La nouvelle tâche est la plus urgente : Une priorité 5 doit précéder 4 et 2.
  • La priorité s’insère au milieu : Les deux morceaux de l’ancienne file doivent être conservés.
  • Les anciennes tâches restent devant à égalité : La condition de transfert inclut les priorités égales pour préserver FIFO.
  • Insérer en queue sans changer l’objet : Réaffecter localement f à une nouvelle file laisserait l’appelant avec la mauvaise file.
Indice

Les anciennes tâches de même priorité doivent passer avant la nouvelle.

Comprendre la correction
def ajouter_file_prio(f, t, p):
    f_aux = File()
    while not f.est_vide() and f.examiner()[1] >= p:
        f_aux.enfiler(f.defiler())
    f_aux.enfiler((t, p))
    while not f.est_vide():
        f_aux.enfiler(f.defiler())
    while not f_aux.est_vide():
        f.enfiler(f_aux.defiler())

La première boucle déplace les tâches de priorité supérieure ou égale avant la nouvelle. Le >= préserve donc l’ordre d’arrivée. Le test de vacuité passe avant examiner pour ne jamais consulter une file vide. On transfère ensuite le reste de f, puis on remet tous les éléments dans l’objet f d’origine.

Voir la question dans le sujet PDF, p. 9 (nouvel onglet)

Question 8

#

Donner le coût temporel dans le pire des cas de ajouter_file_prio en fonction du nombre m d’éléments de f.

Comprendre la correction

Le coût est linéaire, O(m), en supposant les opérations primitives de file de coût constant. Les m anciens éléments sont tous transférés vers la file auxiliaire, puis les m + 1 éléments sont remis dans f. Les deux premières boucles se partagent les m éléments ; leurs coûts s’additionnent au lieu de se multiplier.

Avec une implémentation naïve de defiler qui décale une liste entière, ce coût pourrait augmenter. La réponse porte sur l’interface abstraite de file du sujet.

Voir la question dans le sujet PDF, p. 9 (nouvel onglet)

Technique Pomodoro imposée : retirer la première tâche, l’avancer de 25 minutes, la réinsérer à sa priorité si elle est inachevée ; sinon attendre la fin du bloc en se reposant. Continuer jusqu’à file vide. Les tâches sont initialement ajoutées dans l’ordre suivant :

NuméroNomDuréePriorité
3Réviser la NSI904
7Écrire ma lettre au Père Noël204
1Répondre aux e-mails453
2Ranger ma chambre603
6Lire Fondation602
4S’entraîner aux échecs301
5Apprendre le vocabulaire de chinois301

Question 9

#

Indiquer pour chaque bloc de 25 minutes la tâche qui avance, jusqu’à la fin de toutes les tâches. Une tâche inachevée est réinsérée avec sa priorité initiale en respectant l’ordre d’arrivée parmi les égalités.

Indice

Une tâche inachevée revient derrière les autres de même priorité.

Comprendre la correction
BlocIntervalle (min)Tâche
10 à 25t3
225 à 50t7
350 à 75t3
475 à 100t3
5100 à 125t3
6125 à 150t1
7150 à 175t2
8175 à 200t1
9200 à 225t2
10225 à 250t2
11250 à 275t6
12275 à 300t6
13300 à 325t6
14325 à 350t4
15350 à 375t5
16375 à 400t4
17400 à 425t5

Les priorités 4 passent avant toutes les autres : t3, puis t7, puis les trois blocs restants de t3. Aux priorités 3 et 1, les tâches alternent tant qu’elles restent toutes deux inachevées. Une tâche terminée au milieu d’un bloc ne permet pas de démarrer la suivante plus tôt : Alice se repose jusqu’à sa fin.

On obtient 17 blocs, soit 425 minutes planifiées pour 335 minutes de travail estimé ; les 90 minutes restantes sont les fins de blocs inutilisées.

Voir la question dans le sujet PDF, p. 10 (nouvel onglet)

Exemple fourni :

file = File()
for t, p in [(tache1, 3), (tache2, 3), (tache3, 4)]:
    ajouter_file_prio(file, t, p)
print(planning(file))
[<t3>, <t3>, <t3>, <t3>, <t1>, <t2>, <t1>, <t2>, <t2>]

Question 10

#

Écrire planning(f), qui renvoie la liste de tâches dans l’ordre d’exécution des blocs de 25 minutes avec la méthode Pomodoro.

Comprendre la correction
def planning(f):
    resultat = []
    while not f.est_vide():
        tache, priorite = f.defiler()
        resultat.append(tache)
        tache.avancer(25)
        if not tache.est_terminee():
            ajouter_file_prio(f, tache, priorite)
    return resultat

Chaque entrée du résultat correspond à un bloc, donc une même tâche peut apparaître plusieurs fois. La fonction consomme la file et modifie les durées restantes des objets : pour refaire une simulation, il faut recréer des tâches fraîches.

Voir la question dans le sujet PDF, p. 10 (nouvel onglet)
Animez le planning d’AliceUn atelier pour expérimenter

Faites varier la durée d’un bloc, puis avancez dans le planning. Observez les réinsertions à priorité égale et le temps de repos quand une tâche se termine en cours de bloc.

Lire le résultat de l’expérience initiale

17 blocs de 25 minutes, 90 minutes de repos

Une tâche réinsérée passe après les tâches déjà présentes avec la même priorité. Les priorités inférieures attendent que les groupes supérieurs soient tous terminés.

BlocTâcheTravail (min)Repos (min)Reste de la tâche (min)
1t325065
2t72050
3t325040
4t325015
5t315100
6t125020
7t225035
8t12050
9t225010
10t210150
11t625035
12t625010
13t610150
14t42505
15t52505
16t45200
17t55200

La file impose deux règles simultanées : priorité décroissante entre groupes, FIFO à l’intérieur d’une priorité. Le quantum change la fragmentation, pas l’ordre des priorités.

Revoir les notions de cet exercice

Exercice 3 · 8 points

Réseau CaféNet : IPv4, routage et arbres de recherche

CaféNet possède des cafés dans plusieurs villes. Quatre routeurs relient le siège et deux cafés. Dans chaque café, les bornes se connectent à un switch ; les switchs n’ont pas d’adresse IP dans le modèle du sujet. Chaque interface d’un routeur possède une adresse IPv4 du réseau auquel elle appartient. Tous les masques initiaux valent 255.255.255.0 : les trois premiers octets identifient le réseau, le dernier les machines.

172.16.2.0172.16.1.0172.16.0.0172.16.3.0172.16.4.0192.168.10.0192.168.30.0192.168.20.0203.0.113.1R4R3R1R2SiègeCafé 2Café 1Internet
Figure 1 : réseau CaféNet, interfaces détaillées dans les tableaux
Lire les connexions du schéma
  • R4 relié à R3 : 172.16.2.0
  • R4 relié à R1 : 172.16.1.0
  • R1 relié à R3 : 172.16.0.0
  • R1 relié à R2 : 172.16.3.0
  • R3 relié à R2 : 172.16.4.0
  • Siège relié à R4 : 192.168.10.0
  • Café 2 relié à R3 : 192.168.30.0
  • Café 1 relié à R2 : 192.168.20.0
  • Internet relié à R1 : 203.0.113.1
Liaison de la figure 1Interface à une extrémitéInterface à l’autre
R1 - R4R1 : 172.16.1.2R4 : 172.16.1.1
R4 - R3R4 : 172.16.2.1R3 : 172.16.2.2
R1 - R3R1 : 172.16.0.2R3 : 172.16.0.1
R1 - R2R1 : 172.16.3.2R2 : 172.16.3.1
R2 - R3R2 : 172.16.4.1R3 : 172.16.4.2
R1 - InternetR1 : 203.0.113.1Internet
Réseau localRouteurMachines de la figure
Siège : 192.168.10.0/24R4 : 192.168.10.1Ordinateur .2 ; imprimante .3 ; serveur de sauvegarde .10
Café 1 : 192.168.20.0/24R2 : 192.168.20.1Bornes .10 et .11
Café 2 : 192.168.30.0/24R3 : 192.168.30.1Bornes .10 et .11

Partie A : installer une nouvelle borne

Question 1

#

Indiquer les deux seules adresses valides pour une troisième borne du café 1 parmi : (a) 192.168.20.2 ; (b) 192.168.20.157 ; (c) 192.168.20.261 ; (d) 192.168.24.10.

Comprendre la correction

Les choix a et b sont valides : ils appartiennent au réseau 192.168.20.0/24, sont disponibles et ne sont ni le réseau ni le broadcast. L’octet 261 de c dépasse 255. L’adresse d appartient au réseau 192.168.24.0/24, différent de celui du café.

Voir la question dans le sujet PDF, p. 11 (nouvel onglet)

Question 2

#

Déterminer l’adresse de diffusion du café 1. On rappelle que c’est la dernière adresse disponible dans le réseau local.

Comprendre la correction

192.168.20.255. Avec le masque /24, les huit bits de la partie machine sont tous mis à 1 : 11111111 vaut 255.

Voir la question dans le sujet PDF, p. 12 (nouvel onglet)

Question 3

#

Déterminer combien de machines il est encore possible de connecter au café 1 après l’installation de la troisième borne.

Comprendre la correction

Le /24 contient 256 adresses, dont 2 réservées (réseau et diffusion), soit 254 adresses d’hôtes. Trois bornes et l’interface du routeur en occupent 4. Il reste donc 254 - 4 = 250 adresses pour de nouvelles machines. Le switch n’en consomme aucune selon l’énoncé.

Voir la question dans le sujet PDF, p. 12 (nouvel onglet)

Question 4

#

Le café 1 n’a pas besoin de plus de 8 adresses IP, réseau et diffusion inclus. Quelle longueur maximale de masque peut-on choisir ?

Comprendre la correction

Il faut conserver au moins 3 bits pour la partie hôte, car 2³ = 8. La longueur maximale est donc 32 - 3 = 29 bits, soit /29 et le masque 255.255.255.248. Un /30 ne fournirait que 4 adresses.

Il s’agit d’un dimensionnement. Pour l’appliquer réellement, il faudrait aussi choisir un bloc /29 et renuméroter les machines si nécessaire : les anciennes adresses .1, .10 et .11 ne sont pas toutes dans le même bloc /29.

Voir la question dans le sujet PDF, p. 12 (nouvel onglet)

Partie B : RIP

RIP cherche à minimiser le nombre de sauts, un saut étant un transfert entre deux routeurs. Une destination directement connectée a ici un coût nul. La table fournie est :

Réseau destinationInterface de sortieProchain routeurSauts
192.168.20.0192.168.20.1aucun0
172.16.3.0172.16.3.1aucun0
172.16.4.0172.16.4.1aucun0
192.168.10.0172.16.3.1172.16.3.22
172.16.0.0172.16.4.1172.16.4.21
172.16.2.0172.16.4.1172.16.4.21
192.168.30.0………
172.16.1.0………

Question 5

#

Compléter les deux dernières lignes de la table de routage de R2.

Comprendre la correction
Réseau destinationInterface de sortieProchain routeurSauts
192.168.30.0172.16.4.1172.16.4.21
172.16.1.0172.16.3.1172.16.3.21

Le café 2 est directement relié à R3 : depuis R2, on envoie sur l’interface du lien R2-R3, vers l’adresse de R3 sur ce lien. Le réseau 172.16.1.0 est relié à R1, donc on l’atteint en un saut R2-R1. Les interfaces de sortie sont des adresses de R2, les prochains routeurs des adresses du voisin.

Voir la question dans le sujet PDF, p. 12 (nouvel onglet)

Question 6

#

Identifier dans la table de R2 le réseau de destination dont une ligne peut être remplie autrement tout en respectant RIP, puis donner cette autre ligne.

Indice

Comparez les chemins R2-R1-R4 et R2-R3-R4.

Comprendre la correction
Réseau destinationInterface de sortieProchain routeurSauts
192.168.10.0172.16.4.1172.16.4.22

Le siège, raccordé à R4, est à deux sauts de R2 par R1 comme par R3 : R2-R1-R4 et R2-R3-R4. RIP ne départage pas ces chemins par leur débit. La ligne initiale passait par R1 ; celle proposée passe par R3.

Voir la question dans le sujet PDF, p. 13 (nouvel onglet)

Question 7

#

Une IP non référencée doit être routée par défaut vers Internet. Compléter la ligne « autre » avec l’interface de sortie et le prochain routeur.

Comprendre la correction
Réseau destinationInterface de sortieProchain routeur
autre172.16.3.1172.16.3.2

Internet est relié à R1. R2 transmet donc à son voisin R1 par le réseau 172.16.3.0. Il n’utilise pas directement 203.0.113.1 comme prochain saut : cette interface de R1 n’appartient pas à leur lien commun.

Voir la question dans le sujet PDF, p. 13 (nouvel onglet)

Partie C : OSPF

Le coût des liaisons est défini par coût = 10⁹ / débit, avec le débit en bits par seconde.

Question 8

#

Compléter la colonne coût : Ethernet 10 Mbit/s = 10⁷ bit/s donne 100 ; Fast Ethernet 100 Mbit/s = 10⁸ bit/s donne … ; fibre 1 Gbit/s = 10⁹ bit/s donne ….

Comprendre la correction
ConnexionDébitCoût
Ethernet10⁷ bit/s100
Fast Ethernet10⁸ bit/s10
Fibre10⁹ bit/s1

Le calcul est 10⁹ / débit. Un débit dix fois plus élevé donne un coût dix fois plus faible. On additionne ces coûts sur un trajet.

Voir la question dans le sujet PDF, p. 13 (nouvel onglet)
Fibre : 1 Gbit/sEthernet : 10 Mbit/sEthernet : 10 Mbit/sFast Ethernet : 100 Mbit/sFast Ethernet : 100 Mbit/sR4R3R1R2
Figure 2 : débits des liaisons entre les quatre routeurs
Lire les connexions du schéma
  • R4 relié à R3 : Fibre : 1 Gbit/s
  • R4 relié à R1 : Ethernet : 10 Mbit/s
  • R1 relié à R3 : Ethernet : 10 Mbit/s
  • R1 relié à R2 : Fast Ethernet : 100 Mbit/s
  • R3 relié à R2 : Fast Ethernet : 100 Mbit/s

La figure 2 imprime les étiquettes Café 1 et Café 2 à l’inverse de la figure 1. Cette différence n’affecte pas la question, qui porte seulement sur les liaisons entre R1 et R4 ; leurs débits sont reproduits exactement ci-dessus.

Question 9

#

Déterminer la route de coût minimal de R1 à R4 et calculer son coût OSPF.

Comprendre la correction
Chemin simpleCoût
R1-R4100
R1-R3-R4100 + 1 = 101
R1-R2-R3-R410 + 10 + 1 = 21

Le meilleur chemin est R1-R2-R3-R4, de coût 21. Il comporte davantage de sauts que la liaison directe, mais utilise des connexions plus rapides. Avec des coûts strictement positifs, ajouter un cycle ne peut pas améliorer un chemin.

Voir la question dans le sujet PDF, p. 14 (nouvel onglet)

Partie D : chercher les destinations avec un ABR

ip_bin reçoit une IPv4 décimale et renvoie 35 caractères, les quatre octets sur huit bits séparés par trois points. Exemple :

ip_bin('192.168.10.1')
# '11000000.10101000.00001010.00000001'

Question 10

#

Donner la chaîne renvoyée par ip_bin('192.168.20.12').

Comprendre la correction
'11000000.10101000.00010100.00001100'

192 = 128 + 64 ; 168 = 128 + 32 + 8 ; 20 = 16 + 4 ; 12 = 8 + 4. Chaque octet occupe huit bits : les zéros en tête sont conservés. On obtient 32 bits et 3 points, soit 35 caractères.

Voir la question dans le sujet PDF, p. 14 (nouvel onglet)

precede(ip_1, ip_2) reçoit deux chaînes binaires de ce format et indique si la première précède strictement la seconde. On compare les caractères de gauche à droite ; le premier bit différent décide. Exemple : 192.168.10.1 précède 192.168.15.1 car le premier bit différent du troisième octet est plus petit.

def precede(ip_1, ip_2):
    for i in range(35):
        if ip_1[i] < ip_2[i]:
            return ...
        elif ip_1[i] > ip_2[i]:
            return ...
    return ...

Question 11

#

Dans quel cas precede exécutera-t-elle le dernier return de la ligne 7 ?

Comprendre la correction

Quand les deux chaînes sont identiques. Aucune comparaison n’a rencontré un caractère différent, donc la boucle a examiné les 35 positions sans retourner. Deux adresses égales ne se précèdent pas strictement.

Voir la question dans le sujet PDF, p. 15 (nouvel onglet)

Question 12

#

Compléter les lignes 4, 6 et 7 de precede.

Comprendre la correction
def precede(ip_1, ip_2):
    for i in range(35):
        if ip_1[i] < ip_2[i]:
            return True
        elif ip_1[i] > ip_2[i]:
            return False
    return False

Un premier caractère plus petit impose True, un plus grand impose False, et l’égalité complète donne False. Les points sont aux mêmes positions dans les deux chaînes : ils ne créent aucune différence parasite.

Voir la question dans le sujet PDF, p. 15 (nouvel onglet)

Chaque ligne de routage devient un nœud d’ABR. adresse_ip est la destination, interface l’interface de sortie, passerelle le prochain routeur et cout le nombre de sauts. L’arbre vide est un objet dont l’adresse est la chaîne vide. Un nœud non vide possède deux sous-arbres, éventuellement vides. Toute adresse du sous-arbre gauche précède celle du nœud, et toute adresse du sous-arbre droit la suit.

class Abr:
    def __init__(self, adresse_ip, interface, passerelle, cout):
        self.adresse_ip = adresse_ip
        self.interface = interface
        self.passerelle = passerelle
        self.cout = cout
        if adresse_ip != '':
            self.gauche = Abr('', '', '', 0)
            self.droite = Abr('', '', '', 0)

    def est_vide(self):
        return ...

Question 13

#

Citer un attribut et une méthode de la classe Abr.

Comprendre la correction

adresse_ip est un attribut qui contient une donnée de l’objet. est_vide est une méthode, c’est-à-dire une fonction associée à la classe. interface, passerelle ou cout conviennent aussi comme attributs.

Voir la question dans le sujet PDF, p. 16 (nouvel onglet)

Question 14

#

Compléter la ligne 14, le retour de est_vide.

Comprendre la correction
def est_vide(self):
    return self.adresse_ip == ''

Le test suit la représentation choisie. Un ABR vide n’est pas None, et ne possède pas nécessairement d’attributs gauche et droite.

Voir la question dans le sujet PDF, p. 16 (nouvel onglet)

Question 15

#

Justifier, à partir du cours, l’intérêt possible de représenter une table de routage par un ABR.

Comprendre la correction

À chaque comparaison, on choisit un seul sous-arbre : on ne parcourt pas toutes les destinations. La recherche coûte O(h), où h est la hauteur. Si l’arbre est équilibré, cela donne O(log n) pour n entrées, contre O(n) pour une recherche séquentielle.

Un arbre très déséquilibré peut avoir une hauteur linéaire : le gain n’est donc pas automatique. Le modèle du sujet effectue une recherche exacte sur des destinations ; le routage réel peut aussi demander une recherche de préfixe le plus long, qui est une opération différente.

Voir la question dans le sujet PDF, p. 16 (nouvel onglet)
def modifie(self, adresse_ip, interface, passerelle, cout):
    if self.est_vide():
        self.adresse_ip = adresse_ip
        self.interface = interface
        self.passerelle = passerelle
        self.cout = cout
        self.gauche = Abr('', '', '', 0)
        self.droite = Abr('', '', '', 0)
    else:
        self.adresse_ip = adresse_ip
        self.interface = interface
        self.passerelle = passerelle
        self.cout = cout

Question 16

#

Réécrire modifie en évitant la répétition des affectations communes aux deux branches.

Comprendre la correction
def modifie(self, adresse_ip, interface, passerelle, cout):
    if self.est_vide():
        self.gauche = Abr('', '', '', 0)
        self.droite = Abr('', '', '', 0)
    self.adresse_ip = adresse_ip
    self.interface = interface
    self.passerelle = passerelle
    self.cout = cout

On crée les deux sous-arbres uniquement si le nœud était vide, puis on effectue les quatre affectations communes. Tester la vacuité avant de remplacer l’adresse est essentiel : une fois l’adresse mise à jour, le nœud ne paraît plus vide et on risquerait d’oublier ses enfants.

Voir la question dans le sujet PDF, p. 16 (nouvel onglet)
def rechercher(self, adresse_ip):
    if self.est_vide() or adresse_ip == self.adresse_ip:
        return self
    elif precede(...):
        return self.gauche.rechercher(adresse_ip)
    else:
        return self.droite.rechercher(adresse_ip)

def inserer(self, adresse_ip, interface, passerelle, cout):
    destination = self.rechercher(adresse_ip)
    destination.modifie(adresse_ip, interface, passerelle, cout)

Question 17

#

Compléter la ligne 35 de rechercher. Rappel : precede attend des adresses écrites sous forme binaire.

Indice

La fonction precede reçoit des chaînes binaires, pas décimales.

Comprendre la correction
elif precede(ip_bin(adresse_ip), ip_bin(self.adresse_ip)):

On descend à gauche si l’adresse recherchée précède l’adresse du nœud courant. Les deux chaînes décimales doivent être converties avant la comparaison. Inverser les arguments ferait choisir la mauvaise branche.

Le premier test de rechercher renvoie soit le nœud trouvé, soit le premier arbre vide à la bonne position. Ainsi inserer met à jour une entrée existante ou remplit cet emplacement sans casser l’ordre de l’ABR.

Voir la question dans le sujet PDF, p. 17 (nouvel onglet)
RIP et OSPF choisissent-ils la même route ?Un atelier pour expérimenter

Changez le débit de la liaison directe R1-R4 puis comparez les trois chemins simples. RIP compte les sauts ; OSPF additionne les coûts calculés à partir des débits.

Lire le résultat de l’expérience initiale

OSPF choisit R1-R2-R3-R4

Le coût direct diminue quand son débit augmente. Une route plus longue en sauts peut alors perdre ou gagner l’avantage.

CheminSautsCoût OSPF
R1-R41100
R1-R3-R42101
R1-R2-R3-R4321

Une route est optimale par rapport à un critère précis. Le chemin le plus court en nombre de sauts n’est pas nécessairement le moins coûteux en OSPF.

Revoir les notions de cet exercice

Du sujet à la méthode

Votre prochaine séance de révision

  • Traitez les deux modèles SQL de l’exercice 1 séparément et ne propagez pas implicitement la correction d’année de la partie A dans l’extrait indépendant de la partie B.
  • Pour les files de priorité, dessinez la file après chaque retrait et réinsertion : le détail décisif est l’ordre des égalités.
  • En réseau, recopiez le chemin de routeurs avant de chercher les adresses des interfaces ; cette étape évite de confondre le routeur courant et son voisin.

Retrouver ces notions dans d’autres sujets

Toutes les annales de NSI · Le guide pour préparer le bac NSI 2027

Énoncé : sujet 25-NSIJ1ME1 (PDF). Corrigé et explications pédagogiques proposés par Sofien.