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.
| id | marque | modele | annee | num_ser | prix |
|---|---|---|---|---|---|
| 1 | Gibson | Les Paul Goldtop | 1956 | @70562 | 100000 |
| 2 | Gibson | Les Paul Goldtop | 1988 | 81738349 | 20000 |
| 3 | Gibson | Les Paul Standard | 1959 | @90663 | 250000 |
| 4 | Gibson | Les Paul Standard | 1987 | 81757532 | 25000 |
| 5 | Fender | Telecaster | 1952 | 000230 | 150000 |
| 6 | Fender | Telecaster | 1965 | 81345673 | 10000 |
| 7 | Fender | Stratocaster | 1956 | 001359 | 200000 |
| 8 | Fender | Stratocaster | 1965 | 81757532 | 15000 |
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.
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
| marque | modele |
|---|---|
| Gibson | Les Paul Goldtop |
| Fender | Stratocaster |
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.
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.
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.
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.
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.id | nom |
|---|---|
| 1 | Gibson |
| 2 | Fender |
| modele.id | nom | id_marque |
|---|---|---|
| 1 | Les Paul Goldtop | 1 |
| 2 | Les Paul Standard | 1 |
| 3 | Telecaster | 2 |
| 4 | Stratocaster | 2 |
| guitare.id | id_modele | annee | num_ser | prix |
|---|---|---|---|---|
| 1 | 1 | 1956 | @70562 | 100000 |
| 2 | 1 | 1988 | 81738349 | 20000 |
| 3 | 2 | 1959 | @90663 | 250000 |
| 4 | 2 | 1987 | 81757532 | 25000 |
| 5 | 3 | 1952 | 000230 | 150000 |
| 6 | 3 | 1965 | 81345673 | 10000 |
| 7 | 4 | 1956 | 001359 | 200000 |
| 8 | 4 | 1965 | 81757532 | 15000 |
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.
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.
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.
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.
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.
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.
| id | Marque | Modèle | Année | Prix (€) |
|---|---|---|---|---|
| 5 | Fender | Telecaster | 1952 | 150000 |
| 1 | Gibson | Les Paul Goldtop | 1956 | 100000 |
| 7 | Fender | Stratocaster | 1956 | 200000 |
| 3 | Gibson | Les Paul Standard | 1959 | 250000 |
| 6 | Fender | Telecaster | 1965 | 10000 |
| 8 | Fender | Stratocaster | 1965 | 15000 |
| 4 | Gibson | Les Paul Standard | 1987 | 25000 |
| 2 | Gibson | Les Paul Goldtop | 1988 | 20000 |
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éro | Nom | Durée | Durée restante |
|---|---|---|---|
| 1 | Répondre aux e-mails | 45 | 45 |
| 2 | Ranger ma chambre | 60 | 60 |
| 3 | Réviser la NSI | 90 | 90 |
| 4 | S’entraîner aux échecs | 30 | 30 |
| 5 | Apprendre le vocabulaire de chinois | 30 | 30 |
| 6 | Lire Fondation | 60 | 60 |
| 7 | Écrire ma lettre au Père Noël | 20 | 20 |
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.
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 - nOn 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.
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 <= 0Tester 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.
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.
| Opération File | Effet |
|---|---|
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.
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]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
passLes 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.
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.
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éro | Nom | Durée | Priorité |
|---|---|---|---|
| 3 | Réviser la NSI | 90 | 4 |
| 7 | Écrire ma lettre au Père Noël | 20 | 4 |
| 1 | Répondre aux e-mails | 45 | 3 |
| 2 | Ranger ma chambre | 60 | 3 |
| 6 | Lire Fondation | 60 | 2 |
| 4 | S’entraîner aux échecs | 30 | 1 |
| 5 | Apprendre le vocabulaire de chinois | 30 | 1 |
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
| Bloc | Intervalle (min) | Tâche |
|---|---|---|
| 1 | 0 à 25 | t3 |
| 2 | 25 à 50 | t7 |
| 3 | 50 à 75 | t3 |
| 4 | 75 à 100 | t3 |
| 5 | 100 à 125 | t3 |
| 6 | 125 à 150 | t1 |
| 7 | 150 à 175 | t2 |
| 8 | 175 à 200 | t1 |
| 9 | 200 à 225 | t2 |
| 10 | 225 à 250 | t2 |
| 11 | 250 à 275 | t6 |
| 12 | 275 à 300 | t6 |
| 13 | 300 à 325 | t6 |
| 14 | 325 à 350 | t4 |
| 15 | 350 à 375 | t5 |
| 16 | 375 à 400 | t4 |
| 17 | 400 à 425 | t5 |
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.
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 resultatChaque 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.
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.
| Bloc | Tâche | Travail (min) | Repos (min) | Reste de la tâche (min) |
|---|---|---|---|---|
| 1 | t3 | 25 | 0 | 65 |
| 2 | t7 | 20 | 5 | 0 |
| 3 | t3 | 25 | 0 | 40 |
| 4 | t3 | 25 | 0 | 15 |
| 5 | t3 | 15 | 10 | 0 |
| 6 | t1 | 25 | 0 | 20 |
| 7 | t2 | 25 | 0 | 35 |
| 8 | t1 | 20 | 5 | 0 |
| 9 | t2 | 25 | 0 | 10 |
| 10 | t2 | 10 | 15 | 0 |
| 11 | t6 | 25 | 0 | 35 |
| 12 | t6 | 25 | 0 | 10 |
| 13 | t6 | 10 | 15 | 0 |
| 14 | t4 | 25 | 0 | 5 |
| 15 | t5 | 25 | 0 | 5 |
| 16 | t4 | 5 | 20 | 0 |
| 17 | t5 | 5 | 20 | 0 |
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.
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.
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 1 | Interface à une extrémité | Interface à l’autre |
|---|---|---|
| R1 - R4 | R1 : 172.16.1.2 | R4 : 172.16.1.1 |
| R4 - R3 | R4 : 172.16.2.1 | R3 : 172.16.2.2 |
| R1 - R3 | R1 : 172.16.0.2 | R3 : 172.16.0.1 |
| R1 - R2 | R1 : 172.16.3.2 | R2 : 172.16.3.1 |
| R2 - R3 | R2 : 172.16.4.1 | R3 : 172.16.4.2 |
| R1 - Internet | R1 : 203.0.113.1 | Internet |
| Réseau local | Routeur | Machines de la figure |
|---|---|---|
| Siège : 192.168.10.0/24 | R4 : 192.168.10.1 | Ordinateur .2 ; imprimante .3 ; serveur de sauvegarde .10 |
| Café 1 : 192.168.20.0/24 | R2 : 192.168.20.1 | Bornes .10 et .11 |
| Café 2 : 192.168.30.0/24 | R3 : 192.168.30.1 | Bornes .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é.
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.
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é.
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.
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 destination | Interface de sortie | Prochain routeur | Sauts |
|---|---|---|---|
| 192.168.20.0 | 192.168.20.1 | aucun | 0 |
| 172.16.3.0 | 172.16.3.1 | aucun | 0 |
| 172.16.4.0 | 172.16.4.1 | aucun | 0 |
| 192.168.10.0 | 172.16.3.1 | 172.16.3.2 | 2 |
| 172.16.0.0 | 172.16.4.1 | 172.16.4.2 | 1 |
| 172.16.2.0 | 172.16.4.1 | 172.16.4.2 | 1 |
| 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 destination | Interface de sortie | Prochain routeur | Sauts |
|---|---|---|---|
| 192.168.30.0 | 172.16.4.1 | 172.16.4.2 | 1 |
| 172.16.1.0 | 172.16.3.1 | 172.16.3.2 | 1 |
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.
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 destination | Interface de sortie | Prochain routeur | Sauts |
|---|---|---|---|
| 192.168.10.0 | 172.16.4.1 | 172.16.4.2 | 2 |
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.
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 destination | Interface de sortie | Prochain routeur |
|---|---|---|
| autre | 172.16.3.1 | 172.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.
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
| Connexion | Débit | Coût |
|---|---|---|
| Ethernet | 10⁷ bit/s | 100 |
| Fast Ethernet | 10⁸ bit/s | 10 |
| Fibre | 10⁹ bit/s | 1 |
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.
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 simple | Coût |
|---|---|
| R1-R4 | 100 |
| R1-R3-R4 | 100 + 1 = 101 |
| R1-R2-R3-R4 | 10 + 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.
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.
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.
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 FalseUn 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.
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.
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.
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.
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 = coutQuestion 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 = coutOn 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.
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.
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.
| Chemin | Sauts | Coût OSPF |
|---|---|---|
| R1-R4 | 1 | 100 |
| R1-R3-R4 | 2 | 101 |
| R1-R2-R3-R4 | 3 | 21 |
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.
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.
