Baccalauréat — Métropole J1 — 2025 — NSI
Bac NSI 2025 — Métropole — Sujet 1
Sujet
Sujet officiel du baccalauréat général, épreuve d'enseignement de spécialité numérique et sciences informatiques, session 2025, Métropole, jour 1 (épreuve du mardi 17 juin 2025). Durée 3 heures 30, calculatrice non autorisée. Le sujet comporte 3 exercices indépendants, à traiter tous les trois. Barème : exercice 1 (6 points, 10 questions), exercice 2 (6 points, 10 questions), exercice 3 (8 points, 17 questions) — soit 37 questions au total.
Exercice 1 (6 points) — Bases de données relationnelles et requêtes SQL
Dans cet exercice, on pourra utiliser les clauses du langage SQL pour construire des requêtes d'interrogation à l'aide de SELECT, FROM, WHERE (avec les opérateurs logiques AND, OR), JOIN ... ON ; des requêtes d'insertion et de mise à jour à l'aide de UPDATE, INSERT, DELETE ; et affiner les recherches à l'aide de DISTINCT, ORDER BY.
Dans un schéma relationnel, la clé primaire d'une relation est définie par son attribut souligné, et les attributs précédés de # sont des clés étrangères.
Le guitariste Slash possède une incroyable collection de guitares. Maud est une grande fan de Slash. Elle décide de faire un inventaire de la collection sous la forme d'une base de données relationnelle.
Partie A
Maud utilise la relation suivante :
inventaire (id, marque, modele, annee, num_ser, prix)
(id est la clé primaire). num_ser représente le numéro de série d'une guitare : il est unique pour chaque guitare d'une même marque. Le prix est en euro.
Extrait de la table inventaire :
| 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 |
1. Expliquer pourquoi l'attribut num_ser ne peut pas être une clé primaire de la relation inventaire.
2. Donner, sous forme de tableau, le résultat de la requête suivante appliquée à l'extrait de table précédent.
SELECT marque, modele
FROM inventaire
WHERE annee = 19563. Écrire une requête SQL permettant d'obtenir toutes les années du modèle Les Paul Standard dans la collection.
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.
5. Maud a fait une erreur de saisie pour la guitare d'identifiant id=1. L'année est en réalité 1957. Écrire une requête SQL permettant de corriger cette erreur de saisie.
Partie B
Maud change de représentation pour l'inventaire de la collection. Elle utilise désormais trois relations :
marque (id, nom)
modele (id, nom, #id_marque)
guitare (id, #id_modele, annee, num_ser, prix)
Dans modele, #id_marque référence la clé primaire id de marque. Dans guitare, #id_modele référence la clé primaire id de modele.
Extraits des trois tables :
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 |
6. Expliquer brièvement, en justifiant, dans quel ordre les trois tables doivent être créées.
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 de la collection.
Slash a fait cadeau d'une de ses guitares à un ami : Maud doit la retirer de sa base de données.
8. Écrire une requête SQL permettant de retirer de la collection la guitare d'identifiant id=3.
Slash a aussi acheté une guitare d'une marque qu'il n'avait pas encore dans sa collection. Maud décide de la rajouter.
9. Écrire l'ensemble des requêtes SQL permettant d'ajouter la guitare suivante : marque BC Rich, modèle Mockingbird, année 1992, numéro de série 92R, prix 5000. On suppose que l'on peut attribuer la valeur 3 pour l'id de la marque BC Rich, la valeur 5 pour l'id du modèle Mockingbird, et la valeur 9 pour l'id de cette guitare.
Maud souhaite connaître la valeur totale des modèles Stratocaster de la collection, à l'aide de la fonction SUM (SELECT SUM(nom_colonne) FROM tab calcule la somme des valeurs de la colonne nom_colonne de la table tab).
10. Écrire une requête SQL permettant de calculer la valeur totale des modèles Stratocaster de la collection de Slash.
Exercice 2 (6 points) — Algorithmique, structures de données et gestion de processus
On cherche à créer une application de type liste de tâches à faire pour aider Alice à planifier sa journée. Une tâche saisie par Alice est représentée par un objet Tache, muni de quatre attributs : numero (saisi par Alice), nom (saisi par Alice), duree (entier en minutes, saisi par Alice) et duree_restante (entier en minutes, initialisé avec la durée totale). Avancer de n minutes (n entier positif) dans une tâche diminue de n sa durée restante ; une tâche est terminée si sa durée restante est négative ou nulle.
Lors de la planification (aucune tâche commencée), Alice liste :
| Numéro | Nom | Durée |
|---|---|---|
| 1 | Répondre aux e-mails | 45 |
| 2 | Ranger ma chambre | 60 |
| 3 | Réviser la NSI | 90 |
| 4 | S'entraîner aux échecs | 30 |
| 5 | Apprendre le vocabulaire de chinois | 30 |
| 6 | Lire Fondation | 60 |
| 7 | Écrire ma lettre au Père Noël | 20 |
(la colonne « Durée restante » est identique à « Durée », aucune tâche n'ayant commencé).
On dispose de la classe Tache ci-dessous :
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)+'>'1. Donner le code Python qui permet d'instancier deux variables tache1 et tache2 représentant : tâche numéro 1, « Répondre aux e-mails », durée estimée 45 minutes ; tâche numéro 2, « Ranger ma chambre », durée estimée 60 minutes. On suppose dans la suite que tache1, ..., tache7 représentent ainsi les sept tâches d'Alice. La méthode __repr__ permet d'avoir print(tache1) qui affiche <t1>.
2. Recopier et compléter le code de la méthode avancer de la classe Tache, qui avance la tâche self de n minutes.
def avancer(self, n):
...3. Recopier et compléter le code de la méthode est_terminee, qui renvoie True si la tâche est terminée, False sinon.
def est_terminee(self):
...On associe à chaque tâche une priorité (entier ≥ 1 ; plus le nombre est grand, plus la tâche est prioritaire). Les tâches sont stockées dans une file dont les éléments sont des tuples (tache, priorite), respectant deux conditions : Condition 1, les éléments sont rangés par ordre décroissant de priorité (l'élément le plus prioritaire en tête de file) ; Condition 2, parmi des éléments de même priorité, ils sont rangés dans l'ordre où ils ont été insérés (le premier inséré reste devant).
Exemple, la file de tâches f :
[début] (<t3>, 4) (<t1>, 3) (<t2>, 3) (<t4>, 1) (<t5>, 1) [fin]signifie : la tâche de priorité maximale est <t3> ; viennent ensuite <t1> puis <t2> (toutes deux de priorité 3, <t1> insérée avant <t2>) ; aucune tâche de priorité 2 ; les moins prioritaires sont <t4> puis <t5> (priorité 1, <t4> insérée avant <t5>).
4. Représenter l'état de la file f ci-dessus lorsqu'on lui ajoute successivement la tâche numéro 6 avec la priorité 2, puis la tâche numéro 7 avec la priorité 4, en respectant les conditions 1 et 2.
On suppose déjà définies pour la classe File : File() (crée une file vide) ; enfiler(self, e) (ajoute e en fin de file) ; defiler(self) (renvoie et supprime le premier élément) ; examiner(self) (renvoie sans supprimer le premier élément) ; est_vide(self) (renvoie True si la file est vide).
5. En repartant de la file f [début] (<t3>, 4) (<t1>, 3) (<t2>, 3) (<t4>, 1) (<t5>, 1) [fin], donner la valeur de f.defiler()[0], et représenter le contenu de f après l'exécution.
6. En repartant de la même file f initiale, donner la valeur de f.examiner()[1], et représenter le contenu de f après l'exécution.
On souhaite écrire ajouter_file_prio(f, t, p), qui ajoute le tuple (t, p) à la bonne position dans la file f : on remplit une file auxiliaire f_aux en défilant les éléments en début de f tant que la priorité du premier élément est supérieure ou égale à p, puis on enfile (t, p) dans f_aux, puis on défile tous les éléments restants de f dans f_aux, et enfin on enfile dans f tous les éléments de f_aux.
7. Recopier et compléter le code de ajouter_file_prio.
def ajouter_file_prio(f, t, p):
f_aux = File()
while ...:
...
...enfiler(...)
while not ...:
...
while not ...:
...8. Donner le coût d'exécution temporel dans le pire des cas de ajouter_file_prio, en fonction du nombre m d'éléments de la file f.
L'application propose ensuite un planning « Pomodoro » : la tâche en tête de file est défilée, avancée de 25 minutes ; si elle n'est pas terminée, elle est remise dans la file avec la fonction ajouter_file_prio et sa priorité initiale ; si elle se termine, Alice se repose le reste des 25 minutes ; on répète tant que la file n'est pas vide.
On reprend les sept tâches d'Alice, ajoutées à la file de priorité dans cet ordre :
| 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 |
9. Indiquer, pour chaque bloc de 25 minutes, la tâche qui avance, jusqu'à la fin de toutes les tâches.
10. Écrire le code d'une fonction planning qui prend en paramètre une file de priorité f (éléments (tache, prio)) et renvoie la liste des tâches, dans l'ordre où elles sont effectuées par tranche de 25 minutes (méthode Pomodoro). Par exemple :
file = File()
for t, p in [(tache1, 3), (tache2, 3), (tache3, 4)]:
ajouter_file_prio(file, t, p)
print(planning(file))doit afficher [<t3>, <t3>, <t3>, <t3>, <t1>, <t2>, <t1>, <t2>, <t2>] (avec tache3 de durée 90, tache1 de durée 45, tache2 de durée 60).
Exercice 3 (8 points) — Architecture matérielle (réseau), arbres binaires de recherche et programmation Python
L'entreprise CaféNet possède plusieurs cafés répartis dans différentes villes. Le réseau de la chaîne de cafés est représenté par un schéma (figure 1, décrit ci-dessous car il ne peut pas être reproduit tel quel). Le schéma comporte 4 routeurs (numérotés 1 à 4), le réseau du siège social, le réseau du café 1 et le réseau du café 2. Dans les réseaux du café 1 et du café 2, des bornes de commande sont connectées à des switchs (boîtiers de connexion qui n'ont pas eux-mêmes d'adresse IP). Les 4 routeurs possèdent chacun au moins 3 interfaces réseau, chaque interface ayant sa propre adresse IPv4 sur le réseau auquel elle est reliée. Les masques de sous-réseau sont tous 255.255.255.0 : les trois premiers octets d'une adresse IP codent l'adresse réseau, le dernier octet code l'adresse de la machine à l'intérieur du sous-réseau.
Description du schéma (figure 1, topologie et adressage ; figure 2, type de chaque liaison) :
- Réseau du siège social, sous-réseau
192.168.10.0/24, relié via un switch à une interface du routeur 4 (192.168.10.1). Machines : un ordinateur (192.168.10.2), une imprimante (192.168.10.3), un serveur de sauvegarde (192.168.10.10). - Réseau du café 1, sous-réseau
192.168.20.0/24, relié via un switch à une interface du routeur 2 (192.168.20.1). Deux bornes de commande :192.168.20.10et192.168.20.11. - Réseau du café 2, sous-réseau
192.168.30.0/24, relié via un switch à une interface du routeur 3 (192.168.30.1). Deux bornes de commande :192.168.30.10et192.168.30.11. - Liaison routeur 4 — routeur 3 (fibre optique) : sous-réseau
172.16.2.0/24(interface172.16.2.1sur le routeur 4,172.16.2.2sur le routeur 3). - Liaison routeur 4 — routeur 1 (Ethernet) : sous-réseau
172.16.1.0/24(interface172.16.1.1sur le routeur 4,172.16.1.2sur le routeur 1). - Liaison routeur 1 — routeur 3 (Ethernet) : sous-réseau
172.16.0.0/24(interface172.16.0.2sur le routeur 1,172.16.0.1sur le routeur 3). - Liaison routeur 1 — routeur 2 (Fast Ethernet) : sous-réseau
172.16.3.0/24(interface172.16.3.2sur le routeur 1,172.16.3.1sur le routeur 2). - Liaison routeur 3 — routeur 2 (Fast Ethernet) : sous-réseau
172.16.4.0/24(interface172.16.4.2sur le routeur 3,172.16.4.1sur le routeur 2). - Le routeur 1 possède en outre une interface d'adresse publique
203.0.113.1reliée à Internet.
Partie A
Le gérant veut faire installer une troisième borne de commande dans le café 1.
1. Indiquer les deux seules adresses IP valides pour cette nouvelle borne, parmi les quatre adresses IP proposées : (a) 192.168.20.2 — (b) 192.168.20.157 — (c) 192.168.20.261 — (d) 192.168.24.10.
L'adresse de diffusion (ou de broadcast) est la dernière adresse disponible à l'intérieur d'un réseau local.
2. Déterminer l'adresse de diffusion du réseau du café 1.
3. Déterminer combien de machines informatiques il est encore possible de connecter au réseau du café 1 après l'installation de la troisième borne de commande.
Le réseau local du café 1 n'a pas besoin de plus de 8 adresses IP différentes (ce décompte inclut les adresses réservées : adresse de diffusion et adresse réseau). Il est rappelé que la longueur du masque de sous-réseau est actuellement de 24 bits (3 octets).
4. Expliquer quelle est la longueur maximale du masque de sous-réseau que l'on pourrait choisir pour le réseau local du café 1.
Partie B
RIP (Routing Information Protocol) est un protocole de routage utilisé dans les réseaux IP. Un « saut » correspond au transfert des données d'un routeur à un autre : RIP utilise le nombre de sauts comme critère principal du coût d'un chemin — le chemin le plus optimal est celui qui traverse le moins de routeurs.
La table de routage du routeur 2 est :
| Réseau destination | Interface de sortie | Prochain routeur | Nombre de 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 | … | … | … |
5. Recopier et compléter les deux dernières lignes de la table de routage du routeur 2.
La table de routage du routeur 2 contient un réseau de destination pour lequel deux routes différentes sont possibles : la ligne correspondante aurait donc pu être remplie différemment tout en respectant le protocole RIP.
6. Identifier ce réseau de destination et indiquer comment cette ligne de la table de routage pourrait être modifiée.
Une adresse IP qui n'est pas référencée dans la table de routage doit être routée par défaut vers Internet.
7. Recopier et compléter la ligne à ajouter à la table de routage du routeur 2 :
| Réseau destination | Interface de sortie | Prochain routeur |
|---|---|---|
| autre | … | … |
Partie C
OSPF est également un protocole d'échange de données entre les routeurs, qui prend en compte le coût des routes. Le coût est lié au débit des liaisons par la formule coût = 10^9 / débit (débit exprimé en bit.s⁻¹).
8. Recopier et compléter la dernière colonne du tableau des coûts :
| Type de connexion | Débit en bit.s⁻¹ | coût |
|---|---|---|
| Ethernet | 100 | |
| Fast Ethernet | … | |
| Fibre optique | … |
9. En utilisant les types de liaison décrits plus haut (fibre optique entre les routeurs 4 et 3, Ethernet entre les routeurs 4 et 1, Ethernet entre les routeurs 1 et 3, Fast Ethernet entre les routeurs 1 et 2, Fast Ethernet entre les routeurs 3 et 2), déterminer la route dont le coût est minimal pour aller du routeur 1 jusqu'au routeur 4, et calculer son coût au sens du protocole OSPF.
Partie D
Le but de cette partie est de classer les adresses IP des différents réseaux afin de faciliter leur recherche.
La fonction ip_bin prend en argument une chaîne de caractères décrivant une adresse IP en notation décimale, et renvoie une chaîne de caractères, de longueur 35 (32 bits et les 3 points), décrivant l'adresse IP en notation binaire. Exemple : ip_bin('192.168.10.1') renvoie '11000000.10101000.00001010.00000001'.
10. Donner la chaîne de caractères renvoyée par ip_bin('192.168.20.12').
La fonction precede prend en paramètres deux adresses IP en notation binaire, sous forme de chaînes identiques à celles renvoyées par ip_bin, et renvoie un booléen valant True si la première adresse précède la seconde. Exemple : avec a = '11000000.10101000.00001010.00000001' et b = '11000000.10101000.00001111.00000001', precede(a, b) renvoie True.
L'algorithme compare bit à bit les deux chaînes binaires, en lisant de gauche à droite ; dans l'exemple ci-dessus, tous les caractères sont identiques jusqu'au sixième caractère du troisième octet, où le bit de a est inférieur à celui de b, d'où la conclusion. Si la première adresse ne précède pas la seconde, la fonction doit renvoyer False.
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 ...11. Expliquer dans quel cas la fonction precede exécutera la dernière instruction return (celle de la ligne 7, hors de la boucle).
12. Recopier et compléter les lignes 4, 6 et 7 du code de la fonction precede.
Les tables de routage de chaque routeur sont implémentées sous la forme d'un arbre binaire de recherche, avec la classe Abr :
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 ...Dans cette représentation : adresse_ip désigne l'adresse IP de la destination ; interface désigne l'interface réseau ; passerelle désigne l'adresse IP du prochain routeur ; cout désigne le nombre de sauts pour atteindre la destination. Par convention, l'arbre binaire vide est une instance de Abr pour laquelle adresse_ip est une chaîne vide. Un arbre binaire de recherche non vide possède nécessairement un sous-arbre gauche et un sous-arbre droite, éventuellement vides, qui sont eux-mêmes des arbres binaires de recherche : si le sous-arbre gauche n'est pas vide, l'adresse IP du sous-arbre gauche précède celle de l'instance parent ; si le sous-arbre droit n'est pas vide, l'adresse IP de l'instance parent précède celle du sous-arbre droit.
13. Citer un attribut et citer une méthode de la classe Abr.
14. Recopier et compléter la ligne return ... du code de la méthode est_vide.
15. Justifier, en mobilisant des connaissances de cours, l'intérêt qu'il peut y avoir à représenter la table de routage par un arbre binaire de recherche.
La méthode modifie (incluse dans la classe Abr) est donnée ci-dessous :
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 = coutLes quatre affectations du bloc if (self.adresse_ip = ... à self.cout = ...) sont exactement les mêmes que celles du bloc else.
16. Réécrire le code de la fonction modifie en évitant cette répétition.
La classe Abr est complétée afin de permettre l'ajout de nouvelles lignes à la table de routage, tout en conservant les propriétés d'un arbre binaire de recherche :
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)On rappelle que la fonction precede prend en arguments des adresses IP écrites sous forme binaire.
17. Recopier et compléter la ligne elif precede(...): du code de la fonction rechercher.
Corrigé
Créez un compte gratuit : votre première correction est offerte.