Épreuve écrite · 2026 · Jour 2

Bac NSI 2026 Métropole jour 2

Le sujet du 17 juin 2026 relie trois situations concrètes : gérer les routes d’une entreprise, produire un taquin réellement résoluble et organiser un covoiturage. Il faut lire précisément un programme, repérer ses invariants et choisir la bonne représentation. Durée : 3 h 30 sans calculatrice ; les trois exercices indépendants sont obligatoires. Les attendus de rédaction proposés ici ne sont pas un barème officiel.

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

Réseau multisite et capacité d’une table de routage

Une entreprise interconnecte trois sites avec cinq routeurs. Le site A comporte les PC A01 et A02 reliés au switch A, connecté à R1. Le site B comporte B01, B02, B03, reliés au switch B et à R2. Le site C comporte C01, C02, reliés au switch C et à R4. Voici toutes les liaisons entre les routeurs :

R1R3R2R5R4
Liaisons des routeurs R1 à R5
Lire les connexions du schéma
  • R1 relié à R3
  • R1 relié à R4
  • R2 relié à R3
  • R5 relié à R4
  • R3 relié à R4
  • R5 relié à R2

RIP minimise le nombre de sauts entre routeurs. OSPF minimise la somme des coûts des liaisons, avec la formule imposée coût = 10⁸ / débit, où le débit est en bit/s et le coût sans unité. La configuration IP, les masques, les routes, la mémoire et les tables de routage doivent être cohérents.

Une IPv4 IP/S comporte quatre octets de 0 à 255. Les S premiers bits identifient le réseau, les autres la machine. L’adresse réseau a tous ses bits machine à 0 ; l’adresse de diffusion les a tous à 1. Ces deux adresses sont réservées. PC C01 possède 172.16.2.1/16.

Question 1

#

Déterminer l’adresse du réseau local du site C.

Indice

16 bits correspondent exactement à deux octets.

Comprendre la correction

172.16.0.0. Le /16 fixe les deux premiers octets ; les seize bits restants deviennent 0 pour l’adresse réseau. Il ne faut pas conserver le 2 du troisième octet : il appartient ici à la partie machine.

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

Question 2

#

Déterminer l’adresse de diffusion du site C.

Indice

Conservez la partie réseau et mettez tous les bits machine à 1.

Comprendre la correction

172.16.255.255. Les deux octets de la partie machine sont remplis de bits à 1, soit 255 pour chacun.

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

Question 3

#

Combien de machines au maximum peut-on connecter au réseau local du site C, machines déjà présentes comprises ?

Indice

Comptez les bits variables puis retirez les deux adresses réservées.

Comprendre la correction

2¹⁶ - 2 = 65 534 adresses hôtes utilisables. On enlève le réseau et la diffusion. Le nombre demandé est une capacité totale, pas une capacité restante après décompte des machines dessinées.

Voir la question dans le sujet PDF, p. 3 (nouvel onglet)
DestinationPasse parNombre de sauts
R2À compléterÀ compléter
R3À compléterÀ compléter
R4À compléterÀ compléter
R5À compléterÀ compléter

Question 4

#

Compléter la table RIP de R1 : destination, prochain routeur (« Passe par ») et nombre de sauts.

Indice

Explorez d’abord les voisins de R1 puis leurs voisins.

Comprendre la correction
DestinationPasse parSauts
R2R32
R3R31
R4R41
R5R42

Pour chaque destination, la colonne « Passe par » contient le premier routeur après R1, pas la route entière. Les voisins directs ont un coût de 1.

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

Question 5

#

Quel chemin suit un paquet de PC A01 vers PC B01 avec RIP ?

Indice

Cherchez la route la plus courte entre R1 et R2.

Comprendre la correction

PC A01 → switch A → R1 → R3 → R2 → switch B → PC B01. La portion de routage utilise deux sauts : R1-R3 et R3-R2. Les commutateurs locaux ne s’ajoutent pas à la métrique RIP définie ici.

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

Question 6

#

R3 tombe en panne. Quelle nouvelle route RIP peut relier R1 à R2 ?

Indice

Écartez R3 et tous les trajets qui le traversent.

Comprendre la correction

R1 → R4 → R5 → R2, soit trois sauts. Une panne de routeur supprime toutes ses liaisons, et pas seulement une arête.

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

Question 7

#

Compléter les coûts OSPF pour Ethernet (10⁷ bit/s), Fast Ethernet (10⁸ bit/s) et fibre (10⁹ bit/s).

Indice

Diviser des puissances de 10 revient à soustraire leurs exposants.

Comprendre la correction
ConnexionCalculCoût
Ethernet10⁸ / 10⁷10
Fast Ethernet10⁸ / 10⁸1
Fibre10⁸ / 10⁹0,1

On applique exactement la formule donnée, y compris le coût fractionnaire de la fibre. Aucune règle d’arrondi d’un équipement réel n’est à ajouter au modèle du sujet.

Voir la question dans le sujet PDF, p. 3, 4 (nouvel onglet)
LiaisonConnexion
R1-R3Fibre
R1-R4Ethernet
R2-R3Ethernet
R5-R4Fast Ethernet
R3-R4Fast Ethernet
R5-R2Fast Ethernet

Question 8

#

Déterminer et justifier la route OSPF entre R1 et R4.

Indice

Le coût total s’additionne ; le nombre de liens seul ne décide pas.

Comprendre la correction

R1 → R3 → R4 coûte 0,1 + 1 = 1,1, contre 10 pour la liaison directe R1-R4. Le détour par R3-R2-R5-R4 coûterait 0,1 + 10 + 1 + 1 = 12,1. Le protocole choisit donc deux liens rapides plutôt qu’un seul lien lent.

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

Un routeur est une chaîne, par exemple R1, et une route une liste de routeurs, par exemple ["R1", "R3", "R2"]. On propose :

liste_routes = []
MAX_ROUTES = 5

def ajouter_route(route):
    liste_routes.append(route)

Question 9

#

Pourquoi ce code ne garantit-il pas la limite du nombre de routes ?

Indice

Cherchez où la valeur MAX_ROUTES est utilisée après son affectation.

Comprendre la correction

MAX_ROUTES reçoit 5, mais cette constante n’intervient jamais dans la fonction. Chaque appel à append ajoute une route, y compris la sixième et les suivantes. Donner un nom à une limite n’impose pas automatiquement la contrainte : il faut la tester avant la modification.

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

La classe possède un entier capacite, égal à 5 par défaut, et une liste routes. Sa méthode ajouter refuse l’insertion quand la limite est atteinte et affiche alors un message.

class Routage:
    def __init__(self, capacite=5):
        self.capacite = ...
        self.routes = []

    def ajouter(self, route):
        if ...:
            ...
        else:
            ...

Question 10

#

Compléter les lignes 3, 7, 8 et 10 de la classe Routage.

Indice

On compare la longueur actuelle à la capacité avant append.

Comprendre la correction
class Routage:
    def __init__(self, capacite=5):
        self.capacite = capacite
        self.routes = []

    def ajouter(self, route):
        if len(self.routes) < self.capacite:
            self.routes.append(route)
        else:
            print("Capacité maximale atteinte")

La capacité fournie au constructeur est conservée dans l’instance. L’inégalité doit être stricte : si la liste contient déjà cinq routes pour une capacité de cinq, aucun nouvel ajout ne doit avoir lieu. Chaque instance possède sa propre liste.

On peut vérifier la borne avec une instance de capacité1 : le premier ajout laisse une route dans la liste ; le second doit conserver cette même longueur et produire le message de refus. Avec deux instances, un ajout dans l’une ne doit pas modifier les routes de l’autre. Ces tests contrôlent la contrainte et l’indépendance de l’état, pas seulement l’affichage.

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

Pour les routes ["R1", "R3", "R2"] et ["R1", "R4", "R5", "R2"], l’affichage attendu est :

R1
R3
R2
---
R1
R4
R5
R2
---

Question 11

#

Écrire afficher, qui affiche chaque route, avec un routeur par ligne et --- après chaque route.

Indice

L’indentation du séparateur est la partie essentielle.

Comprendre la correction
def afficher(self):
    for route in self.routes:
        for routeur in route:
            print(routeur)
        print("---")

La boucle externe choisit une route ; la boucle interne affiche ses routeurs. Le séparateur est dans la boucle externe mais en dehors de la boucle interne, donc il apparaît une seule fois par route.

Voir la question dans le sujet PDF, p. 5 (nouvel onglet)
Une route courte peut-elle être la plus lente ?Un atelier pour expérimenter

Sélectionnez la métrique et une panne éventuelle de R3. Le modèle recalcule les routes accessibles de R1 à R2 et montre leurs coûts.

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

Route retenue : R1 → R3 → R2

Les chemins sont comparés avec la métrique choisie. Les liaisons des postes aux commutateurs et routeurs ne sont pas comptées ici.

RouteCoût
R1 → R3 → R22
R1 → R4 → R5 → R23
R1 → R4 → R3 → R23
R1 → R3 → R4 → R5 → R24

Une métrique de routage et une contrainte de capacité sont deux règles qu’un programme doit explicitement appliquer.

Revoir les notions de cet exercice

Exercice 2 · 6 points

Taquin : corriger le programme et conserver la résolubilité

Le taquin possède quinze tuiles numérotées dans une grille 4 × 4 et une case vide. Une tuile adjacente au vide peut y glisser. Le but est de retrouver la grille rangée. Une grille qui le permet est résoluble. La figure 1 présente les deux grilles suivantes, où « vide » est codé par 16 :

LigneGrille rangéeGrille mélangée
11, 2, 3, 414, vide, 15, 12
25, 6, 7, 85, 3, 7, 1
39, 10, 11, 122, 10, 9, 4
413, 14, 15, vide13, 11, 6, 8

À gauche les tuiles 12 et 15 peuvent bouger ; à droite 14, 15 et 3. Une grille est une liste de quatre listes contenant chacune quatre entiers ; chaque entier de 1 à 16 apparaît exactement une fois.

rangee = [[1, 2, 3, 4], [5, 6, 7, 8],
          [9, 10, 11, 12], [13, 14, 15, 16]]
melangee = [[14, 16, 15, 12], [5, 3, 7, 1],
            [2, 10, 9, 4], [13, 11, 6, 8]]

Question 1

#

Donner melangee[2][1].

Indice

Le premier indice sélectionne une ligne, le second une colonne.

Comprendre la correction

10 : l’indice 2 désigne la troisième ligne [2, 10, 9, 4] et l’indice 1 son deuxième élément. Les indices Python commencent à zéro.

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

Partie A : mélange aléatoire

La méthode envisagée crée une liste aplatie de 1 à 16, la mélange avec random.shuffle, puis la transforme en grille.

Question 2

#

Corriger valeurs = [k for k in range(16)] pour obtenir les entiers de 1 à 16.

Indice

La longueur correcte d’une liste ne garantit pas ses bonnes bornes.

Comprendre la correction
valeurs = [k for k in range(1, 17)]

La borne de départ 1 est incluse, la borne de fin 17 est exclue. La version initiale produisait 0 à 15 : elle contenait seize valeurs, mais pas les bonnes valeurs.

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

La documentation précise que random.shuffle(x) mélange la séquence en place, sans créer une autre instance.

from random import shuffle
valeurs = shuffle(valeurs)
print(valeurs)  # None

Question 3

#

Corriger valeurs = shuffle(valeurs), qui remplace la liste par None.

Indice

Séparez un effet sur un objet et la valeur renvoyée par une fonction.

Comprendre la correction
shuffle(valeurs)

shuffle modifie la liste en place et renvoie None. Il faut appeler la fonction sans affecter son résultat à valeurs. La liste mélangée reste accessible par la variable initiale.

Voir la question dans le sujet PDF, p. 7 (nouvel onglet)
def en_grille(valeurs, n):
    assert ...
    grille = [[0 for j in range(n)] for i in range(n)]
    for i in range(n):
        for j in range(n):
            grille[i][j] = valeurs[j * n + i]
    return grille

Question 4

#

Compléter l’assertion de en_grille pour vérifier le nombre d’éléments.

Indice

Le nombre total de cases est lignes multipliées par colonnes.

Comprendre la correction
assert len(valeurs) == n * n

Une grille n × n requiert exactement n² cases. Ce test ne vérifie pas à lui seul que les valeurs sont distinctes ou qu’elles vont de 1 à n² : la question porte seulement sur la taille.

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

Question 5

#

L’appel en_grille([1, 2, 3, 4], 2) donne [[1, 3], [2, 4]] au lieu de [[1, 2], [3, 4]]. Corriger la ligne 6.

Indice

La case (1, 0) d’une grille 2 × 2 doit lire l’indice 2.

Comprendre la correction
grille[i][j] = valeurs[i * n + j]

Avant la ligne i, il y a i lignes complètes de n éléments. On ajoute le décalage j dans cette ligne. L’ancienne expression inversait le rôle de la ligne et de la colonne, et produisait une transposition.

Case (i,j), n=2Indice aplati i×n+jValeur de [1,2,3,4]
(0,0)01
(0,1)12
(1,0)23
(1,1)34

La ligne suivante commence immédiatement après les n valeurs de la précédente. Pour vérifier la formule sur une taille quelconque, la dernière case (n-1,n-1) doit lire (n-1)×n+(n-1)=n²-1, dernier indice valide.

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

Partie B : grille résoluble

Une inversion est un couple d’indices i < j tel que valeurs[i] > valeurs[j]. Par exemple [6, 9, 7, 8] comporte (1, 2) et (1, 3), donc deux inversions. La distance du vide au coin inférieur droit est la somme des écarts de lignes et de colonnes. Dans la grille mélangée de la figure 1, elle vaut 3 + 2 = 5. On admet que la grille est résoluble exactement lorsque nombre d’inversions + distance est pair.

Question 6

#

La grille [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 16, 15, 14]] est-elle résoluble ? Justifier.

Indice

Le 16 est inclus dans le comptage des inversions défini par le sujet.

Comprendre la correction

Non. Dans la liste aplatie, les seules inversions sont (16, 15), (16, 14) et (15, 14), soit 3. La case vide 16 est à la ligne d’indice 3 et colonne d’indice 1 : sa distance au coin (3, 3) vaut 2. La somme 3 + 2 = 5 est impaire. On compte bien ici les inversions impliquant 16, puisque c’est la convention de l’énoncé.

Voir la question dans le sujet PDF, p. 8 (nouvel onglet)
def compte_inversions(valeurs):
    total = 0
    for i in range(len(valeurs)):
        for j in range(len(valeurs)):
            if valeurs[i] > valeurs[j]:
                total = total + 1
    return total

assert compte_inversions([1, 2, 3, 4]) == 0

Question 7

#

Proposer un test avec une liste aplatie de quatre éléments possédant deux inversions.

Indice

Déplacez 1 derrière deux valeurs plus grandes.

Comprendre la correction
assert compte_inversions([2, 3, 1, 4]) == 2

Les inversions sont (indice 0, indice 2), car 2 > 1, et (indice 1, indice 2), car 3 > 1. Aucun autre couple i < j ne convient. Utiliser les valeurs 1 à 4 permet en plus de rester dans le cadre d’un taquin 2 × 2.

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

Question 8

#

La fonction ne passe pas les tests. Proposer une correction.

Indice

La définition impose deux conditions : ordre des indices et ordre des valeurs.

Comprendre la correction
def compte_inversions(valeurs):
    total = 0
    for i in range(len(valeurs)):
        for j in range(i + 1, len(valeurs)):
            if valeurs[i] > valeurs[j]:
                total += 1
    return total

La seconde boucle impose désormais j > i. L’ancienne version comparait toutes les paires dans les deux sens ; pour quatre valeurs distinctes, elle comptait toujours six comparaisons vraies, même sur une liste triée. La complexité de la version corrigée reste quadratique.

Sur [2,3,1,4], i=0 compare2 aux valeurs placées après lui et trouve2>1 ; i=1 trouve3>1 ; les autres comparaisons n’ajoutent rien. Chaque paire d’indices est visitée une seule fois avec i

Voir la question dans le sujet PDF, p. 9 (nouvel onglet)
def distance_tuile_vide(grille, n):
    num_tuile_vide = n * n
    for i in range(n):
        for j in range(n):
            if grille[...][...] == ...:
                return ...

Question 9

#

Compléter les lignes 5 et 6 de distance_tuile_vide.

Indice

Pour n = 4, la cible est (3, 3), pas (4, 4).

Comprendre la correction
if grille[i][j] == num_tuile_vide:
    return (n - 1 - i) + (n - 1 - j)

Le coin inférieur droit possède les indices (n - 1, n - 1). Tous les indices de la grille sont inférieurs ou égaux à ces deux coordonnées, donc les écarts sont déjà positifs. On cherche n², numéro conventionnel du vide, et non 0.

Voir la question dans le sujet PDF, p. 9 (nouvel onglet)
def est_resoluble(valeurs, n):
    grille = en_grille(valeurs, n)
    inv = ...(...)
    dis = ...(...)
    return (... + ...) ... == ...

Question 10

#

Compléter est_resoluble. On rappelle que 7 % 2 == 1 et 8 % 2 == 0.

PythonDécider si un taquin 4×4 peut être résoluÉcrivez votre solution et mettez-la à l’épreuve

Complétez est_resoluble(valeurs, n) pour le taquin 4×4 de cette partie : n vaut 4, valeurs est une permutation de 1 à 16 et 16 représente le vide. Les helpers sont fournis comme dans le sujet : en_grille remplit par colonnes, compte_inversions inclut le vide, et distance_tuile_vide mesure sa distance au coin inférieur droit. Appliquez le critère de parité admis sur la somme des inversions et de cette distance. Ne généralisez pas cette formule aux dimensions impaires.

def est_resoluble(valeurs,n):
    # À vous de jouer
    pass

Les cas de test proposés :

  • La configuration finale : Aucune inversion, distance nulle : la somme est paire.
  • Deux tuiles échangées, vide immobile : Un échange de deux tuiles modifie la parité sans changer la distance du vide.
  • Un déplacement légal du vide : Une inversion et une unité de distance donnent une somme paire : tester les inversions seules serait faux.
  • Le vide se déplace entre deux colonnes : Selon le remplissage par colonnes du sujet, ces deux indices sont voisins. Le déplacement produit sept inversions et une distance de 1.
  • Une grille presque résolue mais impossible : Un déplacement légal suivi d’un échange de deux autres tuiles rend la parité impaire. La fonction doit seulement décider, sans jouer.
Indice

Chaque fonction attend une forme précise de données.

Comprendre la correction
def est_resoluble(valeurs, n):
    grille = en_grille(valeurs, n)
    inv = compte_inversions(valeurs)
    dis = distance_tuile_vide(grille, n)
    return (inv + dis) % 2 == 0

Le modulo 2 teste la parité de la somme. Les fonctions reçoivent la bonne représentation : la liste aplatie pour les inversions, la grille pour la distance. La formule admise concerne le taquin 4 × 4 de cette partie, et convient plus généralement aux dimensions paires avec le vide inclus ; il ne faut pas la présenter comme un critère général valide pour toute dimension impaire.

Une grille peut sembler presque rangée et être pourtant irrésoluble : la propriété de parité porte sur la permutation entière, pas sur une distance visuelle intuitive à la solution. Le test ne fournit pas les mouvements à jouer ; il décide seulement de l’existence d’une solution dans le modèle étudié. Calculer effectivement un chemin est une autre tâche, que l’on peut aborder comme une recherche dans le graphe des configurations.

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

Partie C : mélange réaliste

Pour garantir un mélange résoluble, on part de la grille rangée et échange plusieurs fois le vide avec une tuile voisine. Les sommets du graphe sont les indices des cases, pas leurs valeurs. Une arête relie les positions entre lesquelles un glissement est possible. La liste d’adjacence donne, pour chaque indice, ses voisins. Figure 2, grille 3 × 3 :

IndiceVoisins
01, 3
10, 2, 4
21, 5
30, 4, 6
41, 3, 5, 7
52, 4, 8
63, 7
74, 6, 8
85, 7

Question 11

#

Donner une valeur possible de graphe_4_4[9].

Indice

Dépliez les indices de 0 à 15 sur quatre lignes.

Comprendre la correction
[5, 8, 10, 13]

L’indice 9 est à la ligne 2, colonne 1. Ses voisins haut, gauche, droite et bas portent les indices 5, 8, 10, 13. L’ordre de la liste ne compte pas. Un décalage de 1 n’est valide horizontalement que si l’on reste sur la même ligne.

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

random.choice renvoie un élément choisi aléatoirement dans la liste donnée. Le programme imprimé est :

from random import choice

def melange_graphe(nb_dep, n, graphe):
    valeurs = [i for i in range(n*n)]  # ligne imprimée, erronée
    num_tuile_vide = n * n
    actuelle = num_tuile_vide - 1
    for k in range(...):
        prochaine = choice(...)
        valeurs[actuelle] = valeurs[...]
        valeurs[prochaine] = ...
        actuelle = prochaine
    return en_grille(valeurs, n)

Question 12

#

Compléter les lignes 7 à 10 de melange_graphe.

Indice

La liste passée à choice doit être celle des voisins de la position actuelle du vide.

Comprendre la correction
for k in range(nb_dep):
    prochaine = choice(graphe[actuelle])
    valeurs[actuelle] = valeurs[prochaine]
    valeurs[prochaine] = num_tuile_vide

La tuile voisine remplit l’ancien vide ; son ancienne case devient vide, puis actuelle = prochaine actualise la position. Chaque mouvement est réversible : refaire tous les mouvements en sens inverse ramène à la grille rangée.

Coquille à corriger en plus dans le programme fourni : la ligne 4 produit 0 à n² - 1 alors que la convention est 1 à n². Il faut écrire valeurs = [i for i in range(1, n*n + 1)]. Compléter uniquement les quatre lignes demandées sans rectifier cette initialisation ne donne pas un taquin valide.

Le programme construit volontairement une marche dans le graphe des positions du vide. Même si les choix sont aléatoires, inverser la suite des échanges est toujours un chemin légal vers la solution, car un glissement est réversible. Cela garantit la résolubilité mais pas que la grille soit très mélangée : des mouvements peuvent s’annuler ou ramener tôt à la solution. La fonction doit conserver exactement une occurrence de chaque nombre de1 à n².

Voir la question dans le sujet PDF, p. 10 (nouvel onglet)
L’invariant caché derrière le taquinUn atelier pour expérimenter

Comparez trois positions, puis échangez deux tuiles numérotées. Observez comment ce simple échange modifie la parité sans déplacer le vide. Le tableau liste toutes les inversions.

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

Cette grille n’est pas résoluble

Les inversions incluent la valeur 16 qui représente le vide. La formule s’applique à cette grille de dimension paire. Inversions : 16 > 15 ; 16 > 14 ; 15 > 14.

LigneColonne 1Colonne 2Colonne 3Colonne 4
11234
25678
39101112
413Vide1514

Mélanger une permutation quelconque ne garantit pas une grille gagnable. Des mouvements légaux depuis la solution, eux, conservent sa résolubilité.

Revoir les notions de cet exercice

Exercice 3 · 8 points

Covoiturage : requêtes SQL, files et point de rendez-vous

Une entreprise multisite propose une application de covoiturage : chaque salarié peut créer un trajet ou s’y inscrire. Le but est de réduire l’usage individuel de la voiture. On peut utiliser SELECT, FROM, WHERE, AND, OR, JOIN ... ON, INSERT, UPDATE, DELETE, DISTINCT et ORDER BY. Le schéma de la figure 1 est le suivant :

TableAttributsClé primaireClés étrangères
utilisateurid_utilisateur, nom, prenomid_utilisateur-
trajetid_trajet, depart, arrivee, heure, nb_places, conducteurid_trajetconducteur → utilisateur.id_utilisateur
inscriptiontrajet, passagerAucune clé soulignée sur la figure sourcetrajet → trajet.id_trajet ; passager → utilisateur.id_utilisateur

Dans la figure d’origine, les attributs clés primaires sont soulignés et les clés étrangères préfixées par #. Les identifiants de trajet et d’utilisateur sont des entiers.

Question 1

#

Pourquoi nom et prenom ne sont-ils pas retenus comme clé primaire de utilisateur ?

Indice

Une clé doit distinguer deux personnes homonymes.

Comprendre la correction

Deux salariés peuvent avoir le même nom et le même prénom. Leur combinaison ne garantit donc pas l’unicité d’une ligne. Un identifiant propre à chaque utilisateur reste distinct même en cas d’homonymie ou de changement de nom.

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

Question 2

#

Pourquoi les attributs de inscription sont-ils aussi entiers ?

Indice

Suivez chaque flèche du schéma jusqu’à l’identifiant référencé.

Comprendre la correction

inscription.trajet et inscription.passager sont des clés étrangères vers des identifiants entiers. Ils doivent appartenir aux mêmes domaines de valeurs que les attributs référencés afin de représenter ces références et de permettre leur contrôle.

Voir la question dans le sujet PDF, p. 11 (nouvel onglet)
SELECT * FROM trajet
WHERE heure > '2026-06-19 00:00:00'
  AND heure < '2026-06-20 00:00:00';

Cette requête renvoie :

id_trajetdepartarriveeheurenb_placesconducteur
1291LiverdunToul2026-06-19 07:30:00325
1292AllainNancy2026-06-19 07:45:00230
1293MesseinNancy2026-06-19 08:00:00210
1294NancyMessein2026-06-19 18:20:00210
1295NancyAllain2026-06-19 17:45:00325
1296ToulLiverdun2026-06-19 18:00:00230
trajetpassager
12914
129115
129138
129218
12954
129515
129538
129618
id_utilisateurnomprenom
4MizabYasmine
10Di MariaAlexis
15ReyMaxime
18NguemaBasil
25DanielValérie
30SanchesNathalie
38FabreClément
SELECT COUNT(*)
FROM ...
WHERE ...;

Question 3

#

Compléter la requête pour compter les passagers du trajet 1291.

Indice

La table inscription contient une ligne par inscription.

Comprendre la correction
SELECT COUNT(*)
FROM inscription
WHERE trajet = 1291;

Le résultat est 3 pour l’extrait fourni. On compte les inscriptions et non les places proposées : nb_places décrit la capacité, qui n’est pas toujours égale au nombre de passagers inscrits.

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

Question 4

#

Lister les trajets qui arrivent à Nancy le 19 juin 2026, par heure de départ croissante.

Indice

Un intervalle de dates complet évite de confondre le 19 juin avec une chaîne approximative.

Comprendre la correction
SELECT *
FROM trajet
WHERE arrivee = 'Nancy'
  AND heure >= '2026-06-19 00:00:00'
  AND heure < '2026-06-20 00:00:00'
ORDER BY heure ASC;

La borne basse inclusive contient tout le 19 juin, y compris un éventuel départ à minuit. La borne haute exclusive exclut le 20 juin. Avec les données fournies : 1292 à 07:45, puis 1293 à 08:00. Le champ heure décrit l’heure de départ ; aucune heure d’arrivée n’est fournie.

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

Question 5

#

Écrire une requête qui aurait créé le trajet 1295, supposé non modifié depuis son ajout.

Indice

Recopiez toutes les informations de la ligne 1295.

Comprendre la correction
INSERT INTO trajet
(id_trajet, depart, arrivee, heure, nb_places, conducteur)
VALUES (1295, 'Nancy', 'Allain', '2026-06-19 17:45:00', 3, 25);

Les valeurs suivent l’ordre des colonnes explicitement citées. Le conducteur 25 doit exister dans utilisateur. Il ne faut pas ajouter ici les inscriptions des passagers : elles sont dans une autre table.

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

Question 6

#

Modifier l’heure de départ du trajet 1294 à 18 h 40.

Indice

Le champ heure contient à la fois date et heure.

Comprendre la correction
UPDATE trajet
SET heure = '2026-06-19 18:40:00'
WHERE id_trajet = 1294;

On conserve la date du 19 juin et on cible le trajet par sa clé primaire. Sans WHERE, tous les trajets auraient été modifiés.

Voir la question dans le sujet PDF, p. 13 (nouvel onglet)
DELETE FROM trajet
WHERE id_trajet = 1296;
ERROR 1451 (23000) at line 95 in file: 'covoit.sql':
Cannot delete or update a parent row: a foreign key constraint fails

Question 7

#

Expliquer l’erreur rencontrée lors de la suppression du trajet 1296.

Indice

Cherchez 1296 dans la table inscription.

Comprendre la correction

La ligne (1296, 18) d’inscription référence le trajet 1296. La suppression de ce trajet laisserait cette clé étrangère sans cible. Le SGBD refuse donc l’opération pour préserver l’intégrité référentielle. Il faut supprimer les inscriptions correspondantes avant le trajet, sauf si une règle de cascade a été prévue. Ce n’est ni une erreur de syntaxe de DELETE ni une confusion avec le conducteur.

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

Question 8

#

Lister les noms et prénoms des passagers transportés au moins une fois par l’utilisatrice 25.

SQLRetrouver les passagers de la conductrice 25Écrivez votre solution et mettez-la à l’épreuve

Renvoyez sans répétition le nom et le prénom des passagers transportés au moins une fois par l’utilisatrice 25. La question ne limite pas la recherche à une seule date.

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

Les cas de test proposés :

  • Trajets et inscriptions du sujet : Les trois tables sont reprises de l’extrait officiel. Les mêmes trois passagers sont inscrits sur deux trajets conduits par Valérie Daniel.
  • Cas complémentaire : passager et conducteur sont deux rôles : Jeu complémentaire pédagogique, distinct des données officielles. La conductrice 25 est elle-même passagère d’un autre conducteur. Elle ne doit pas apparaître pour cette raison ; le trajet plus ancien reste dans le périmètre.
Indice

Trois tables relient l’identité du passager à l’identité du conducteur.

Comprendre la correction
SELECT DISTINCT u.nom, u.prenom
FROM utilisateur AS u
JOIN inscription AS i ON i.passager = u.id_utilisateur
JOIN trajet AS t ON t.id_trajet = i.trajet
WHERE t.conducteur = 25;

On part des passagers, rejoint leurs inscriptions puis les trajets concernés. DISTINCT élimine les doublons de projection, car un passager peut voyager plusieurs fois avec la conductrice. Dans l’extrait, on retrouve Yasmine Mizab, Maxime Rey et Clément Fabre. La requête ne limite pas les trajets à une journée, car la question dit « au moins une fois » sans restriction de date.

La jointure sur inscription.passager choisit bien les passagers : relier utilisateur directement à trajet.conducteur retournerait la conductrice au lieu des personnes transportées. La deuxième jointure associe chaque inscription à son trajet. Le filtre conducteur=25 garde alors les voyages pertinents, même si le passager y figure plusieurs fois.

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

Partie B : choisir le rendez-vous

Alice, Maxime, Valérie et Clément habitent aux sommets A, M, V et C. Ils envisagent les points P1, P2, P3. Chaque arête représente un trajet de distance et de durée approximativement comparables ; on mesure donc les distances en nombres d’arêtes.

CP1AP2P3VM
Domiciles et points de rendez-vous du covoiturage
Lire les connexions du schéma
  • C relié à P1
  • C relié à P2
  • P1 relié à A
  • A relié à P2
  • A relié à P3
  • P2 relié à M
  • P3 relié à M
  • M relié à V

Question 9

#

Écrire le dictionnaire associant chaque sommet de la figure 2 à ses voisins.

Indice

Vérifiez chaque liaison dans les deux sens.

Comprendre la correction
graphe = {
    "C": [
        "P1",
        "P2"
    ],
    "P1": [
        "C",
        "A"
    ],
    "A": [
        "P1",
        "P2",
        "P3"
    ],
    "P2": [
        "C",
        "A",
        "M"
    ],
    "P3": [
        "A",
        "M"
    ],
    "M": [
        "P2",
        "P3",
        "V"
    ],
    "V": [
        "M"
    ]
}

Le graphe n’est pas orienté : chaque arête doit être présente dans les deux listes correspondantes. Les positions sur le dessin ne créent pas de liaison implicite. Par exemple V ne possède que M pour voisin ; P2 et P3 ne sont pas directement reliés.

Voir la question dans le sujet PDF, p. 14 (nouvel onglet)
def dico_distance(graphe, depart):
    """Renvoie les distances de depart aux sommets atteignables."""
    file = File()
    file.inserer(depart)
    dico = {depart: 0}
    while not file.est_vide():
        sommet = file.extraire()
        for voisin in graphe[sommet]:
            if voisin not in dico:
                dico[voisin] = dico[sommet] + 1
                file.inserer(voisin)
    return dico
Méthode de FileContrat
__init__()Crée une file vide
consulter()Renvoie le premier élément sans le retirer ; IndexError si vide
est_vide()True si vide, False sinon
extraire()Renvoie et retire le premier élément ; IndexError si vide
inserer(element)Ajoute l’élément en file

Question 10

#

Rappeler le principe d’une file.

Indice

Pensez à l’ordre d’une queue d’attente.

Comprendre la correction

Une file suit la règle premier entré, premier sorti, ou FIFO. Une insertion se fait à une extrémité et une extraction à l’autre. Ainsi, un sommet découvert avant un autre est exploré avant lui. Une pile suivrait au contraire la règle dernier entré, premier sorti.

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

Question 11

#

Donner un ordre possible d’insertion des sommets dans la file en partant de P1.

Indice

Écrivez la file après chaque extraction et insertion ; n’insérez pas un sommet déjà dans dico.

Comprendre la correction

Avec l’ordre des voisins du dictionnaire proposé : P1, C, A, P2, P3, M, V. P1 découvre C et A ; C découvre P2 ; A découvre P3 (P2 est déjà marqué) ; P2 découvre M ; M découvre V. Chaque sommet est inscrit dans dico dès son insertion, ce qui évite de l’insérer plusieurs fois. D’autres ordres sont possibles si l’ordre des voisins change.

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

Question 12

#

Quel type de parcours est utilisé par dico_distance ?

Indice

Le choix file ou pile détermine l’ordre d’exploration.

Comprendre la correction

Un parcours en largeur. La file fait explorer les sommets par distance croissante ; chaque voisin inédit reçoit la distance de son prédécesseur plus un. Sur ce graphe non pondéré, c’est sa distance minimale au départ.

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

On définit l’excentricité d’un sommet comme sa plus grande distance à un autre sommet du graphe.

def excentricite(graphe, sommet):
    """Renvoie l’excentricité de sommet dans graphe (int)."""
    dico = dico_distance(graphe, sommet)
    ...
    ...
    ...
    ...
    ...

Question 13

#

Compléter excentricite sans utiliser max.

Indice

Conservez la meilleure valeur vue jusqu’ici et mettez-la à jour seulement lorsqu’elle augmente.

Comprendre la correction
def excentricite(graphe, sommet):
    dico = dico_distance(graphe, sommet)
    plus_grande = 0
    for s in dico:
        if dico[s] > plus_grande:
            plus_grande = dico[s]
    return plus_grande

L’excentricité est la plus grande distance du sommet aux autres sommets. Le dictionnaire contient des distances non négatives ; initialiser à zéro convient, y compris pour un graphe réduit à un sommet. Le graphe du sujet est connexe. Sur un graphe non connexe, ce programme donnerait seulement le maximum dans la composante atteignable, ce qu’il faudrait distinguer d’une excentricité globale infinie ou non définie selon la convention.

L’invariant de la boucle est : plus_grande est la plus grande distance parmi les entrées déjà traitées. Initialiser à0 est correct puisque les distances ne sont jamais négatives et que le sommet de départ se trouve dans dico à distance0. En fin de parcours, toutes les distances atteignables ont été comparées. Il ne faut pas comparer les noms des sommets s entre eux : ce sont les valeurs dico[s] qui mesurent l’éloignement.

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

Question 14

#

Choisir le meilleur point de rendez-vous, avec un indicateur justifié.

Indice

Comparez la distance du domicile le plus éloigné pour chaque point candidat.

Comprendre la correction
PointDistance à Aà Mà Và CMaximumSomme
P1134149
P2112125
P3112337

P2 minimise le plus grand trajet : personne ne parcourt plus de deux arêtes. Ses distances à P1 et P3 valent aussi 2, donc son excentricité globale est 2, contre 4 pour P1 et 3 pour P3. P2 minimise également la somme des distances des quatre domiciles (5), mais ce second indicateur est distinct : il est ici cohérent avec la minimisation du trajet le plus défavorisé.

Voir la question dans le sujet PDF, p. 16 (nouvel onglet)
Choisir un rendez-vous équitableUn atelier pour expérimenter

Choisissez P1, P2 ou P3 comme départ. Le parcours calcule toutes les distances ; comparez le trajet maximal et la distance totale des quatre covoitureurs.

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

Rendez-vous P1

La file explore les distances par couches. Pour l’équité, on minimise le plus long trajet des quatre domiciles ; pour l’effort collectif, on regarde la somme.

SommetDistanceNature
P10Rendez-vous possible
C1Domicile
A1Domicile
P22Rendez-vous possible
P32Rendez-vous possible
M3Domicile
V4Domicile

Un « meilleur » point dépend d’un critère annoncé. Ici P2 minimise à la fois le pire trajet et la distance collective.

Revoir les notions de cet exercice

Du sujet à la méthode

Votre prochaine séance de révision

  • Faites apparaître la correspondance entre les cases et leurs indices : beaucoup d’erreurs du taquin viennent d’un mélange indice/valeur.
  • Pour une requête SQL, écrivez d’abord les jointures nécessaires puis ajoutez les filtres.
  • Lorsque le sujet demande un indicateur, donnez sa valeur pour chaque candidat et expliquez pourquoi on le minimise.

Retrouver ces notions dans d’autres sujets

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

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