Épreuve écrite · 2026 · Jour 1

Bac NSI 2026 Antilles-Guyane jour 1

Le sujet du 16 juin 2026 associe bases relationnelles, optimisation d’un algorithme récursif et chiffrement pédagogique. Son fil conducteur est la conservation d’une information essentielle : identité d’une commande, ordre d’une sous-séquence et unicité d’une décomposition dans un sac. Les trois exercices se traitent en 3 h 30, sans calculatrice.

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

Pizzayolo : commandes, intégrité et composition des pizzas

Le restaurant Pizzayolo souhaite moderniser ses réservations de pizzas. Un prestataire propose un SGBD relationnel pour remplacer un tableur où chaque nouvelle commande serait une ligne.

Question 1

#

Donner deux avantages d’un SGBD par rapport à ce simple tableur.

Indice

Pensez à deux personnes enregistrant des commandes au même moment.

Comprendre la correction

Un SGBD peut faire respecter l’intégrité des données (unicité d’un code de commande, existence du client et de la pizza) et gérer des accès simultanés avec des transactions, pour éviter des modifications incohérentes. Il permet aussi des droits d’accès et des requêtes reliant les tables sans recopier partout les coordonnées. Deux avantages expliqués suffisent ; le SGBD ne supprime pas le besoin d’un schéma bien conçu.

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

Schéma proposé, clés primaires indiquées :

RelationAttributsClé primaire
clientid entier, nom texte, prenom texteid
pizzanum entier, couleur texte, prix flottantnum
commandecode entier, id_client entier, num_pizza entier, date texte, livraison entier, paiement entiercode
client.idnomprenom
103EspositoGiulia
106PanziniRaffaele
pizza.numcouleurprix
1Rossa11.5
2Bianca11.5
3Nera12.5
4Bianca15.5
codeid_clientnum_pizzadatelivraisonpaiement
4236210322025-01-0211
4236310332025-01-0211
4236410612025-01-0310
4236510312025-01-0301

Chaque pizza commandée correspond à une ligne distincte. Un client peut donc avoir plusieurs codes de commande le même jour. livraison vaut 1 si la pizza est livrée et 0 sinon ; paiement vaut 1 si elle est réglée et 0 sinon.

Question 2

#

Peut-on avoir deux pizzas avec le même numéro et le même prix, mais une couleur différente ? Justifier.

Indice

La clé primaire num suffit à interdire le doublon.

Comprendre la correction

Non dans cette relation. num est la clé primaire de pizza : deux lignes ne peuvent partager ce numéro, quels que soient leurs prix ou couleurs. Une même couleur et un même prix peuvent au contraire apparaître sur des numéros distincts. La contrainte porte sur la clé, pas sur la totalité des autres attributs.

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

Question 3

#

Donner une clé étrangère, sa relation et la relation/attribut référencé.

Indice

Une valeur étrangère sert à relier les lignes de deux tables.

Comprendre la correction

commande.id_client référence client.id. Autre réponse valable : commande.num_pizza référence pizza.num. Une commande doit désigner un client et une pizza existants ; la valeur étrangère peut apparaître dans plusieurs commandes, contrairement à la clé primaire de la table cible.

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

On peut employer SELECT, FROM, WHERE, AND, OR, JOIN ... ON, INSERT, UPDATE, DELETE, DISTINCT et ORDER BY. COUNT compte les valeurs non nulles, AVG calcule leur moyenne, MIN et MAX les extrêmes, SUM leur somme. Par exemple MAX(prix) donne le prix le plus élevé, et SUM(livraison) compte les pizzas livrées puisque les valeurs sont 0 ou 1.

Question 4

#

Quel résultat donne SELECT prix FROM pizza WHERE couleur = 'Bianca' sur les extraits ?

Indice

Appliquez le filtre sur couleur puis ne conservez que la colonne prix.

Comprendre la correction
prix
11.5
15.5

Les pizzas 2 et 4 vérifient la condition. Le résultat contient leurs prix, pas leurs numéros. Aucun ordre n’est garanti en l’absence d’ORDER BY ; ces deux lignes forment le résultat attendu.

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

Question 5

#

Obtenir les couleurs des pizzas dont le prix est strictement supérieur à 15 €.

Indice

« Strictement supérieur » se traduit par >, pas >=.

Comprendre la correction
SELECT couleur
FROM pizza
WHERE prix > 15;

Le filtre strict exclut une éventuelle pizza à exactement 15 €. Dans l’extrait, seule la pizza 4 convient, donc le résultat est Bianca. DISTINCT serait possible pour une liste de couleurs sans répétition, mais le sujet ne l’impose pas ici.

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

Question 6

#

Obtenir les prénoms des clients qui n’ont pas payé une pizza pourtant livrée.

SQLRepérer les pizzas livrées mais non payéesÉcrivez votre solution et mettez-la à l’épreuve

Renvoyez les prénoms des clients ayant au moins une commande avec livraison=1 et paiement=0. Les deux conditions doivent concerner la même commande.

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

Les cas de test proposés :

  • Commandes de Pizzayolo dans le sujet : Ces lignes reproduisent les trois tables de l’extrait officiel.
  • Cas complémentaire : ne pas mélanger deux commandes : Jeu complémentaire pédagogique, distinct des données officielles. Alex a une livraison payée et une autre commande non payée mais non livrée : aucune ligne ne satisfait les deux critères. Béa doit apparaître une fois malgré deux impayés livrés.
Indice

Reliez client et commande puis imposez les deux états simultanément.

Comprendre la correction
SELECT DISTINCT c.prenom
FROM client AS c
JOIN commande AS co ON co.id_client = c.id
WHERE co.livraison = 1 AND co.paiement = 0;

Les deux conditions portent sur la même commande : une pizza effectivement livrée et non réglée. La jointure retrouve ensuite le prénom du client. DISTINCT évite de répéter le même prénom dans la projection si plusieurs commandes répondent au filtre. L’extrait renvoie Raffaele. On ne doit pas utiliser OR : cela retiendrait aussi des commandes payées ou encore non livrées.

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

Question 7

#

Calculer le prix moyen des pizzas commandées par un client de nom Esposito.

Indice

Il faut les trois tables : identité du client, choix de pizza et prix.

Comprendre la correction
SELECT AVG(p.prix)
FROM client AS c
JOIN commande AS co ON co.id_client = c.id
JOIN pizza AS p ON p.num = co.num_pizza
WHERE c.nom = 'Esposito';

Chaque commande fournit un prix à la moyenne. Dans l’extrait : (11,5 + 12,5 + 11,5) / 3 = 35,5 / 3, soit environ 11,83 €. Ne pas écrire AVG(DISTINCT prix) : les commandes répétées doivent compter. La question filtre un nom, pas un identifiant unique ; si plusieurs clients s’appellent Esposito, leurs commandes sont toutes concernées. Le schéma ne stocke pas le prix au moment de la commande : on calcule avec les prix actuels de pizza, ce qui est la seule information disponible.

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

Question 8

#

La commande 42365 vient d’être livrée. Mettre à jour la base.

Indice

Le code est l’identifiant de la commande à modifier.

Comprendre la correction
UPDATE commande
SET livraison = 1
WHERE code = 42365;

On conserve la date, le client et l’état du paiement déjà égal à 1. WHERE cible la commande, pas toutes les commandes du même client.

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

Question 9

#

Le 3 janvier 2025, le nouveau client Emanuele Girasole demande la livraison de la pizza 2. Insérer les données dans le bon ordre, avec id 107 et code 42366 disponibles.

Indice

L’insertion d’une commande dépend de l’existence du client.

Comprendre la correction
INSERT INTO client (id, nom, prenom)
VALUES (107, 'Girasole', 'Emanuele');

INSERT INTO commande
(code, id_client, num_pizza, date, livraison, paiement)
VALUES (42366, 107, 2, '2025-01-03', 0, 0);

Le client est créé avant sa commande pour que la clé étrangère 107 ait une cible. La demande n’est pas une livraison déjà réalisée : livraison vaut 0. Aucun règlement n’étant indiqué, on l’enregistre ici en attente avec paiement = 0 ; si le paiement était effectué lors de la réservation, seule cette valeur deviendrait 1. Ces hypothèses doivent être explicites, pas confondues avec les événements futurs.

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

Question 10

#

Quel problème pose DELETE FROM pizza WHERE num = 1 ?

Indice

Retirer de la vente et effacer l’existence passée sont deux opérations différentes.

Comprendre la correction

Les commandes 42364 et 42365 référencent la pizza 1. Avec les clés étrangères activées et sans suppression en cascade, le SGBD refuse sa suppression ; sinon, l’historique contiendrait des références orphelines et perdrait le prix/couleur de la pizza. Une pizza retirée de la vente peut rester dans le catalogue historique avec un indicateur disponible = 0, plutôt que disparaître. Supprimer toutes ses anciennes commandes n’est pas une bonne façon de préserver l’historique.

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

Question 11

#

Ajouter ingredient et composition pour représenter plusieurs ingrédients par pizza et un ingrédient partagé entre plusieurs pizzas ; préciser les clés.

Indice

Une ligne de composition décrit une association précise entre une pizza et un ingrédient.

Comprendre la correction
RelationAttributsClé primaireClés étrangères
ingredientid_ingredient entier, nom texteid_ingredient-
compositionnum_pizza entier, id_ingredient entier(num_pizza, id_ingredient)num_pizza → pizza.num ; id_ingredient → ingredient.id_ingredient

La relation composition résout l’association plusieurs-à-plusieurs. Sa clé composée empêche d’associer deux fois le même ingrédient à la même pizza, tout en autorisant cet ingrédient sur d’autres pizzas. Un attribut quantite pourrait être ajouté si nécessaire, mais il n’est pas demandé. Stocker tous les ingrédients dans une chaîne séparée par des virgules rendrait les recherches et les contraintes beaucoup moins fiables.

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

Question 12

#

Quels problèmes pose un accès extérieur pour consulter l’historique, et quelles précautions prendre ?

Indice

Distinguez la preuve de l’identité et le droit d’accéder à une commande donnée.

Comprendre la correction

Le principal enjeu est que chaque client ne voie que ses propres commandes. Il faut authentifier le compte, puis vérifier ses autorisations sur le serveur pour chaque requête ; cacher un bouton ou recevoir un id_client dans l’URL ne suffit pas. L’application doit communiquer en HTTPS, utiliser des requêtes paramétrées contre les injections SQL et un compte de base aux droits limités. La base n’a pas besoin d’être directement exposée à Internet : un serveur applicatif contrôle les requêtes. Des sauvegardes, mises à jour et journaux d’accès contribuent à protéger la disponibilité et à détecter les incidents.

Voir la question dans le sujet PDF, p. 4 (nouvel onglet)
Livrée, payée : quelles commandes faut-il relancer ?Un atelier pour expérimenter

Choisissez les états livraison et paiement. Le modèle filtre les quatre commandes puis reconstitue le prénom et le prix par jointure.

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

1 commande(s) sélectionnée(s)

Les deux critères sont appliqués à la même ligne avec AND. La jointure ajoute ensuite les informations des tables client et pizza.

CodePrénomPizzaPrixLivréePayée
42364Raffaele111.510

Un filtre porte sur des lignes précises ; une jointure récupère leurs informations associées sans perdre le lien entre client et commande.

Revoir les notions de cet exercice

Exercice 2 · 6 points

Plus longue sous-séquence commune : de l’exhaustif au dynamique

Une LCS (Longest Common Subsequence) est une plus longue sous-liste commune dont les éléments gardent leur ordre, sans devoir être contigus. Pour L₁ = [9, 3, 7, 5, 8] et L₂ = [9, 7, 8, 3, 7, 3], des sous-listes communes sont [], [9], [3], [9, 7], [9, 3, 7] et [9, 7, 8]. Les deux dernières ont une longueur maximale de 3. Le mot « sous-liste » désigne donc ici une sous-séquence, pas une tranche contiguë.

En Python, + concatène deux listes : [1, 2] + [3, 4] donne [1, 2, 3, 4] et [] + [1] donne [1].

Partie A : recherche exhaustive

Algorithme sous_listes(L, accumulateur), avec accumulateur initial [[]] : si L est vide, renvoyer accumulateur. Sinon retirer le dernier élément e ; créer une liste temporaire ; y ajouter chaque sous-liste de l’accumulateur précédée de e ; rappeler sous_listes avec L raccourcie et la concaténation de l’accumulateur et de la liste temporaire.

Question 1

#

Compléter les paramètres des appels de sous_listes([9, 3, 7], [[]]).

Indice

À partir de [[], [7]], préfixez successivement chaque sous-liste par 3.

Comprendre la correction
Appellisteaccumulateur
Initial[9, 3, 7][[]]
Premier[9, 3][[], [7]]
Deuxième[9][[], [7], [3], [3, 7]]
Dernier[][[], [7], [3], [3, 7], [9], [9, 7], [9, 3], [9, 3, 7]]

À chaque étape, on conserve les sous-listes sans le nouvel élément, puis on ajoute celles qui le contiennent en tête. Comme les éléments sont retirés depuis la fin de liste, le placement en tête préserve l’ordre original. Le nombre de sous-listes double : 1, 2, 4, 8. Il ne s’agit pas de permuter les valeurs.

Voir la question dans le sujet PDF, p. 5, 6 (nouvel onglet)
def sous_listes(liste, accumulateur=[[]]):
    if len(liste) == 0:
        return accumulateur
    element = liste.pop()
    tmp = []
    for sous_liste in accumulateur:
        tmp.append(...)
    return sous_listes(...)

Question 2

#

Compléter les lignes 7 et 8 de sous_listes.

Indice

L’accumulateur doit contenir les choix « sans e » et « avec e ».

Comprendre la correction
tmp.append([element] + sous_liste)
return sous_listes(liste, accumulateur + tmp)

[element] transforme l’entier en liste d’un élément avant la concaténation. La concaténation crée de nouvelles listes ; on n’insère pas l’élément dans les sous-listes existantes, qui doivent rester dans l’accumulateur sans cet élément. liste.pop(), en revanche, modifie bien la liste d’entrée. Après l’appel complet, elle est vide. Pour préserver une liste utilisée ensuite, appeler sous_listes(liste.copy()). Cette copie n’est pas indispensable au résultat de l’algorithme fourni, mais évite un effet de bord surprenant.

Voir la question dans le sujet PDF, p. 6 (nouvel onglet)
def est_extraite(sous_liste, liste):
    index_ss_liste, index_liste = 0, 0
    while index_ss_liste < len(sous_liste) and index_liste < len(liste):
        if sous_liste[index_ss_liste] == liste[index_liste]:
            index_ss_liste += 1
        index_liste += 1
    return index_ss_liste == len(sous_liste)
def lcs_force_brute(liste_1, liste_2):
    tmp = sous_listes(liste_1)
    resultat, maxi = [], 0
    for element in tmp:
        if est_extraite(element, liste_2) and len(element) > maxi:
            resultat = element
            maxi = len(element)
    return resultat

Question 3

#

Pourquoi lcs_force_brute peut-elle devenir très lente quand les listes grandissent ? Justifier.

Indice

Combien de décisions conserver/exclure prenez-vous pour m éléments ?

Comprendre la correction

Pour une première liste de longueur m, chaque élément peut être conservé ou exclu, donc la fonction génère 2ᵐ choix de sous-séquences. Si des valeurs se répètent, certaines listes obtenues sont identiques, mais le programme les génère et les teste tout de même. Pour chaque candidat, est_extraite parcourt la seconde liste avec des indices croissants, en O(n) dans le pire cas. On obtient donc un coût de vérification pouvant atteindre O(n·2ᵐ), auquel s’ajoutent les constructions et copies de listes, de l’ordre de O(m·2ᵐ).

Passer de m à m + 1 double le nombre de candidats ; à m = 30, il dépasse le milliard. Le problème est aussi spatial : tmp conserve toutes les sous-listes simultanément. Le test est_extraite fonctionne pourtant efficacement pour un seul candidat : son indice dans la sous-liste avance seulement lorsqu’une valeur correspond, tandis que l’indice dans la liste cible avance toujours. C’est l’énumération exhaustive qui domine.

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

Partie B : décomposer le problème

La fonction lcs(L₁, m′, L₂, n′) renvoie une LCS des m′ premiers éléments de L₁ et des n′ premiers éléments de L₂. Exemple avec 0,0 : []. Avec les préfixes de longueurs 3 et 2 des listes ci-dessus : une LCS de [9,3,7] et [9,7] est [9,7].

Question 4

#

Que renvoie lcs([9, 3, 7, 5, 8], 1, [9, 7, 8, 3, 7, 3], 1) ?

Indice

Ne confondez pas la longueur d’un préfixe et le dernier indice inclus.

Comprendre la correction
[9]

Les paramètres 1 désignent les nombres d’éléments des deux préfixes : chacun vaut [9]. Ils ne désignent pas ici l’indice du dernier élément. La plus longue sous-séquence commune est donc [9], pas [9, 3].

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

Les listes sont non vides, m et n sont leurs longueurs, et L₁[m-1] = L₂[n-1]. Exemple : m=5, n=3, L₁=[9,3,7,5,8], L₂=[9,7,8]. Si leurs fins sont différentes, on choisit une plus longue liste entre lcs(L₁,m-1,L₂,n) et lcs(L₁,m,L₂,n-1). Avec L₂=[9,7,8,3], le sujet compare notamment [9,7] et [9,7,8], et garde cette dernière.

Question 5

#

Si les derniers éléments de L₁ et L₂ sont égaux, exprimer une LCS à partir de lcs(L₁, m-1, L₂, n-1).

Indice

L’élément commun final doit être placé après la solution des deux préfixes.

Comprendre la correction
lcs(L_1, m - 1, L_2, n - 1) + [L_1[m - 1]]

Le dernier élément commun peut prolonger une LCS des préfixes privés de leurs derniers éléments. Il doit être ajouté à la fin pour préserver l’ordre. Pour [9,3,7,5,8] et [9,7,8], une LCS des préfixes est [9,7] ; on ajoute 8 et obtient [9,7,8].

L’idée de la récurrence est structurelle : si les derniers éléments diffèrent, une solution commune ne peut pas les utiliser tous les deux comme dernier élément. Il faut alors comparer les solutions obtenues en retirant la fin de l’une ou de l’autre liste, puis garder une plus longue. En cas d’égalité de longueur, plusieurs LCS peuvent être correctes.

Voir la question dans le sujet PDF, p. 7 (nouvel onglet)
def lcs_top_down(liste_x, liste_y):
    def aux(liste_x, index_x, liste_y, index_y):
        if index_x < 0 or index_y < 0:
            return []
        if liste_x[index_x] == liste_y[index_y]:
            return aux(liste_x, index_x-1, liste_y, index_y-1) + [liste_x[index_x]]
        resultat_1 = aux(liste_x, index_x, liste_y, index_y-1)
        resultat_2 = aux(liste_x, index_x-1, liste_y, index_y)
        if len(resultat_1) >= len(resultat_2):
            return resultat_1
        return resultat_2
    return aux(liste_x, len(liste_x)-1, liste_y, len(liste_y)-1)

Question 6

#

Justifier que aux dans lcs_top_down est récursive.

Indice

Repérez les trois lignes qui rappellent aux.

Comprendre la correction

La fonction aux s’appelle elle-même dans son propre corps, avec un ou deux indices diminués. Les appels de la branche d’égalité diminuent les deux indices ; ceux de la branche d’inégalité en diminuent un. Quand un indice devient négatif, le préfixe correspondant est vide et le cas de base renvoie []. Le nom top_down ne prouve pas à lui seul la récursivité : ce sont les auto-appels qui la caractérisent.

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

Question 7

#

Quel risque pose ce choix pour de très grandes listes ?

Indice

La profondeur maximale et le nombre total d’appels sont deux mesures différentes.

Comprendre la correction

Les appels non terminés s’empilent. Une branche peut avoir une profondeur proportionnelle à m + n ; pour de grandes listes, elle peut dépasser la limite de récursion et provoquer RecursionError. Les cadres d’appels consomment aussi de la mémoire.

Un second problème s’ajoute : le programme fourni n’a aucune mémoïsation. Plusieurs branches recalculent les mêmes couples d’indices, ce qui peut rendre le temps exponentiel. Le qualifier « top_down » n’en fait donc pas automatiquement un algorithme dynamique efficace. Les copies de listes lors des concaténations ajoutent encore du travail. Il faut distinguer la profondeur de pile et le nombre total de sous-appels : diminuer l’un ne résout pas nécessairement l’autre.

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

Question 8

#

Proposer une solution au problème précédent.

Indice

Pour éviter la pile, calculez d’abord les sous-problèmes les plus petits dans un tableau.

Comprendre la correction

Employer une programmation dynamique itérative : une table D[i][j] contient la longueur d’une LCS des i premiers éléments de L₁ et des j premiers éléments de L₂. Les bords D[0][j] et D[i][0] valent 0. Si les dernières valeurs correspondent, D[i][j] = 1 + D[i-1][j-1] ; sinon D[i][j] = max(D[i-1][j], D[i][j-1]). On remplit la table ligne par ligne sans appels récursifs.

Pour restituer une sous-séquence, on remonte depuis D[m][n] : une égalité des valeurs ajoute cet élément et fait reculer en diagonale ; sinon on va vers une case voisine de même meilleure longueur. Les éléments sont collectés à rebours puis inversés. On remplit (m+1)(n+1) cases, en O(mn) temps et mémoire pour cette version. Une mémoïsation récursive éviterait les recalculs mais conserverait la profondeur de pile ; elle ne répond pas à elle seule au risque de RecursionError. Augmenter la limite de récursion ne change pas la nature du problème.

Voir la question dans le sujet PDF, p. 8 (nouvel onglet)
Construire une LCS dans la table de préfixesUn atelier pour expérimenter

Choisissez deux préfixes des listes du sujet. La table montre leurs longueurs de LCS et le modèle reconstitue une solution. Essayez de prédire si ajouter le prochain élément augmente réellement la longueur.

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

Une LCS : [9, 7, 8]

Chaque case utilise la diagonale en cas d’égalité, sinon le maximum du haut et de la gauche. Des égalités peuvent conduire à plusieurs sous-séquences optimales.

L1 / L2vide978373
vide0000000
90111111
30111222
70122233
50122233
80123333

La table ne stocke pas toutes les sous-séquences ; elle partage les résultats des sous-problèmes de préfixes.

Revoir les notions de cet exercice

Exercice 3 · 8 points

De Bob à Alice : routage, échange de clés et sac supercroissant

Bob désigne le navigateur d’un utilisateur, Alice le serveur web du domaine alice.fr. Lors de la première saisie de http://alice.fr, Bob interroge un serveur DNS pour obtenir l’adresse IP d’Alice avant de lui envoyer sa requête. Bob est relié à R1, Alice à R5 et le DNS à R8. Les coûts des liaisons inter-routeurs sont ceux du schéma :

111010.0110.110.10.01R1R2R3R4R5R6R7R8
Réseau pondéré entre Bob, Alice et le DNS
Lire les connexions du schéma
  • R1 relié à R2 : 1
  • R2 relié à R3 : 1
  • R2 relié à R4 : 10
  • R4 relié à R5 : 1
  • R4 relié à R6 : 0.01
  • R3 relié à R6 : 1
  • R3 relié à R7 : 0.1
  • R3 relié à R8 : 1
  • R6 relié à R7 : 0.1
  • R7 relié à R8 : 0.01

Question 1

#

Avec RIP, donner uniquement les routeurs traversés pour aller de Bob au DNS.

Indice

Le DNS est raccordé à R8 ; cherchez le moins de routeurs depuis R1.

Comprendre la correction

R1 → R2 → R3 → R8. Ce chemin traverse quatre routeurs et trois liaisons entre eux. Toute route contournant R3 demanderait davantage de routeurs. Les coûts numériques des arêtes ne sont pas la métrique de RIP.

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

Question 2

#

Bob connaît l’IP d’Alice. Donner la route OSPF vers Alice, uniquement avec les routeurs.

Indice

Comparez les deux façons de passer de R3 à R6.

Comprendre la correction

R1 → R2 → R3 → R7 → R6 → R4 → R5. La liaison R2-R4 de coût 10 est évitée ; R3-R6 de coût 1 est aussi moins intéressante que R3-R7-R6 de coût 0,2. Le choix optimal ne minimise donc pas le nombre d’arêtes.

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

Question 3

#

Donner le coût du chemin précédent.

Indice

Écrivez la somme dans le même ordre que les routeurs du trajet.

Comprendre la correction

1 + 1 + 0,1 + 0,1 + 0,01 + 1 = 3,21. On additionne une fois chaque liaison empruntée. Les connexions terminales Bob-R1 et R5-Alice n’ont pas de coût indiqué dans la figure : le total demandé est celui des liaisons pondérées.

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

Question 4

#

Que se passe-t-il si R3 tombe en panne ? Sans écrire les tables de routage.

Indice

Retirez R3 et toutes ses liaisons, puis vérifiez si les autres routeurs restent connectés.

Comprendre la correction

Les routeurs détectent l’indisponibilité des liens vers R3 et mettent à jour leurs informations de routage. Après convergence, les communications peuvent continuer, car le réseau restant relie encore les destinations. Pour Alice, Bob peut suivre R1-R2-R4-R5 ; pour le DNS, R1-R2-R4-R6-R7-R8. Les nouvelles routes peuvent être plus coûteuses et une interruption temporaire peut se produire pendant la convergence.

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

Question 5

#

Citer un protocole permettant de chiffrer les échanges entre navigateur et serveur web.

Indice

Le S de HTTPS correspond à la sécurisation du transport.

Comprendre la correction

HTTPS, c’est-à-dire HTTP transporté dans une connexion TLS. Le chiffrement protège les données en transit ; TLS ajoute aussi des mécanismes d’intégrité et d’authentification du serveur. L’adresse initiale http://alice.fr ne fournit pas à elle seule cette protection.

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

Bob génère une clé symétrique qu’il doit transmettre à Alice pour qu’ils disposent du même secret.

Question 6

#

Quel rôle joue la même clé symétrique possédée par Alice et Bob ?

Indice

Symétrique signifie que le secret est partagé entre les participants.

Comprendre la correction

Elle permet de chiffrer et déchiffrer les messages échangés. L’émetteur utilise le secret pour produire le chiffré ; le destinataire possède le même secret pour retrouver le clair. La clé doit rester inconnue des tiers. Elle ne prouve pas à elle seule lequel des deux détenteurs a produit un message, puisque tous deux la connaissent.

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

Alice génère un couple clé publique / clé privée. Bob doit envoyer sa clé symétrique sans qu’un tiers puisse l’intercepter et l’utiliser.

Question 7

#

Comment employer les clés publique et privée d’Alice pour transmettre cette clé symétrique ? Ne parler ni de signature ni de certificat dans cette réponse.

Indice

La clé publique du destinataire protège le secret qui lui est destiné.

Comprendre la correction

Alice transmet sa clé publique à Bob et garde sa clé privée secrète. Bob chiffre la clé symétrique avec la clé publique d’Alice, puis lui envoie le résultat. Alice le déchiffre avec sa clé privée. La clé publique peut circuler ; elle ne permet pas à un observateur de déchiffrer ce message. La question raisonne dans le modèle d’échange de clés décrit, sans traiter ici la vérification de l’identité du propriétaire de la clé publique.

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

Le sujet décrit la signature ainsi : Bob envoie sa clé publique à Alice, calcule un condensé de sa clé symétrique, signe ce condensé avec sa clé privée, puis chiffre l’ensemble clé symétrique + signature avec la clé publique d’Alice avant l’envoi.

Question 8

#

Pourquoi Alice seule peut-elle accéder à la clé symétrique et vérifier que Bob l’a envoyée ?

Indice

Le chiffrement extérieur protège la confidentialité ; la signature vérifie origine et intégrité.

Comprendre la correction

Le paquet complet est chiffré avec la clé publique d’Alice : sa clé privée permet de l’ouvrir. Alice récupère la clé symétrique et la signature, vérifie cette signature avec la clé publique de Bob, puis compare le condensé vérifié à un nouveau condensé calculé sur la clé reçue. Une modification de la clé provoque un désaccord ; une signature valide établit qu’elle a été produite avec la clé privée correspondant à celle de Bob.

Cette conclusion exige que la clé publique attribuée à Bob soit authentiquement la sienne. Un attaquant qui substituerait sa propre clé pourrait signer lui-même un message : l’envoi d’une clé publique sur un réseau hostile ne prouve pas son identité. La formulation du sujet « chiffrer avec la clé privée » est une image pédagogique de la signature ; les algorithmes de signature réels ne se résument pas tous à inverser un algorithme de chiffrement.

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

Partie B : module outils

Question 9

#

Dans le module outils, écrire somme(tab), somme des entiers d’une liste. Exemple : somme([1,3,5,7,9]) vaut 25.

Indice

L’accumulateur doit être initialisé avant la boucle et renvoyé après elle.

Comprendre la correction
def somme(tab):
    total = 0
    for valeur in tab:
        total += valeur
    return total

L’accumulateur vaut la somme des éléments déjà parcourus. Il démarre à 0, élément neutre de l’addition, ce qui rend aussi la réponse correcte pour une liste vide. La liste d’entrée n’est pas modifiée. Le programme utilise une addition par valeur et parcourt la liste une seule fois.

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

Question 10

#

Écrire permuter(tab, i, j), qui échange en place les éléments i et j. Exemple : [1,3,5,7,9] avec i=1,j=3 devient [1,7,5,3,9].

Indice

Il faut conserver une des deux anciennes valeurs avant la première écriture.

Comprendre la correction
def permuter(tab, i, j):
    temporaire = tab[i]
    tab[i] = tab[j]
    tab[j] = temporaire

La variable temporaire sauvegarde la première valeur avant de l’écraser. Sans elle, deux affectations successives naïves copieraient la même valeur aux deux endroits. L’affectation simultanée tab[i], tab[j] = tab[j], tab[i] convient également en Python. Le tableau est modifié sur place ; aucun return n’est nécessaire. Si i = j, l’échange laisse la liste inchangée.

Voir la question dans le sujet PDF, p. 11 (nouvel onglet)
def inverser(tab):
    for i in range(len(tab) // 2):
        permuter(...)

Question 11

#

Compléter la ligne de inverser qui inverse tab en place.

Indice

Le premier élément échange avec le dernier, d’indice n - 1.

Comprendre la correction
permuter(tab, i, len(tab) - 1 - i)

Les indices symétriques sont i et n - 1 - i. La boucle ne parcourt que n // 2 positions afin de ne pas échanger une deuxième fois les mêmes paires. Pour n impair, la case centrale ne bouge pas, ce qui est correct. Pour n = 0 ou 1, aucun échange n’est nécessaire.

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

Un sac contient huit entiers : premier entier aléatoire entre 1 et 5 inclus ; à chaque ajout, calculer la somme S des entiers déjà présents, choisir entre S+1 et 2S inclus, puis ajouter. Une fois huit éléments obtenus, inverser la liste pour mettre le plus grand en premier. randint(a,b) inclut ses deux bornes.

from random import randint
def generer_sac():
    sac = ...
    for i in range(7):
        s = ...
        nouvel_entier = ...
        sac.append(nouvel_entier)
    ...
    return sac

Question 12

#

Compléter generer_sac selon la méthode décrite.

Indice

La boucle complète le premier élément jusqu’à une longueur totale de huit.

Comprendre la correction
from random import randint

def generer_sac():
    sac = [randint(1, 5)]
    for i in range(7):
        s = somme(sac)
        nouvel_entier = randint(s + 1, 2 * s)
        sac.append(nouvel_entier)
    inverser(sac)
    return sac

Un premier élément est créé, puis sept autres pour un total de huit. À chaque étape, la somme est recalculée sur le sac déjà constitué. Le nouvel entier est strictement supérieur à la somme de tous les précédents : c’est la propriété supercroissante. Après inversion, chaque élément dépasse la somme de ceux qui le suivent. Cette propriété donnera une décision de déchiffrement sans ambiguïté. Écrire randint(s, 2*s) perdrait l’inégalité stricte, et la taille ne doit pas être neuf.

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

Partie C : chiffrer un octet avec un sac

Un octet est représenté par huit bits en liste. Cle_symetrique possède un seul attribut sac, fourni par outils.generer_sac. Pour chiffrer, multiplier chaque bit par le poids de même indice puis sommer les produits. Avec la clé [568,205,71,32,10,6,2,1], l’octet [1,0,1,1,1,1,0,1] donne 568 + 71 + 32 + 10 + 6 + 1 = 688.

import outils
class Cle_symetrique:
    def __init__(self):
        ... = ...

Question 13

#

Compléter le constructeur de Cle_symetrique.

Indice

L’attribut de l’objet doit recevoir le résultat de l’appel de fonction.

Comprendre la correction
self.sac = outils.generer_sac()

L’appel fabrique une nouvelle clé pour l’instance. outils.generer_sac sans parenthèses conserverait la fonction elle-même au lieu de la liste. Le préfixe outils est nécessaire puisque l’import est import outils.

Voir la question dans le sujet PDF, p. 12, 13 (nouvel onglet)
def chiffrer(self, octet):
    """octet : liste de huit 0 ou 1 ; renvoie un entier."""
    tab_produits = []
    for i in range(...):
        produit = ... * ...
        tab_produits.append(produit)
    s = ...
    return s

Question 14

#

Compléter les lignes 8 à 11 de chiffrer.

Indice

Construisez d’abord la liste des huit contributions puis utilisez le module outils pour les sommer.

Comprendre la correction
for i in range(8):
    produit = octet[i] * self.sac[i]
    tab_produits.append(produit)
s = outils.somme(tab_produits)

Le bit 0 sélectionne un produit nul et le bit 1 sélectionne le poids entier. Les indices des bits et du sac doivent rester alignés. On additionne les produits, pas les bits seuls ni tous les éléments du sac. Sur l’exemple, le bit de poids 205 vaut 0 : ce nombre ne contribue donc pas au total. La précondition impose huit bits ; une validation d’entrée serait une amélioration supplémentaire.

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

Déchiffrement : pour chaque indice i de 0 à 7, si l’entier restant est supérieur ou égal à sac[i], écrire le bit 1 et soustraire sac[i] ; sinon écrire le bit 0.

Question 15

#

Justifier que 688 se déchiffre en [1,0,1,1,1,1,0,1].

Indice

À chaque ligne, comparez le reste au poids puis soustrayez uniquement si le bit vaut 1.

Comprendre la correction
PoidsReste avantBitReste après
5686881120
2051200120
71120149
3249117
101717
6711
2101
1110

On examine les poids du plus grand au plus petit. Si le reste atteint le poids, le bit doit valoir 1 : les poids plus petits, tous réunis, ne pourraient atteindre ce poids. Sinon ce bit vaut nécessairement 0. Le reste final nul confirme que tous les termes sélectionnés reconstituent 688. Ce raisonnement utilise la supercroissance, pas simplement l’ordre décroissant.

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

Question 16

#

Écrire dechiffrer, qui reçoit l’entier chiffré et renvoie les huit bits.

PythonRetrouver huit bits avec une clé supercroissanteÉcrivez votre solution et mettez-la à l’épreuve

Complétez la méthode dechiffrer de Cle_symetrique. self.sac contient huit poids en ordre décroissant ; chaque poids dépasse la somme de tous les suivants. entier est garanti issu du chiffrement d’un octet par cette clé. Renvoyez les huit bits dans l’ordre du sac. Pour rendre les tests reproductibles, generer_sac fournit une clé fixe valide, créée pour cet entraînement. La clé ne doit pas être modifiée.

class Cle_symetrique:
    def __init__(self):
        self.sac = generer_sac()

    def dechiffrer(self, entier):
        # Construisez les huit bits
        pass

Les cas de test proposés :

  • L’octet nul : Il faut renvoyer huit décisions, même si aucune soustraction n’est effectuée.
  • Le plus grand poids exactement : L’égalité signifie que ce poids est présent. Une comparaison stricte le refuserait.
  • Un bit de poids faible : L’ordre de sortie reste celui du sac décroissant, pas l’ordre des poids sélectionnés.
  • Un octet mélangé : 397 = 310 + 70 + 16 + 1. Après chaque choix, le reste doit diminuer.
  • Tous les poids et clé inchangée : La somme des huit poids vaut 592. Déchiffrer ne doit pas consommer la liste de la clé.
Indice

La variable reste doit diminuer sans modifier la clé elle-même.

Comprendre la correction
def dechiffrer(self, entier):
    octet = []
    reste = entier
    for poids in self.sac:
        if reste >= poids:
            octet.append(1)
            reste -= poids
        else:
            octet.append(0)
    return octet

L’invariant est : entier initial = somme des poids déjà sélectionnés + reste. La boucle garde huit décisions dans l’ordre du sac. Pour un entier effectivement obtenu par chiffrer avec cette clé, le reste final est zéro et l’octet est unique. Tous les entiers entre 0 et la somme du sac ne sont pas forcément représentables ; pour accepter des données arbitraires, on pourrait vérifier reste == 0 et refuser sinon.

Ce mécanisme est un modèle pédagogique, pas une recommandation de cryptographie actuelle : avec seulement 256 octets possibles et une structure aussi visible, il ne fournit pas la sécurité des protocoles réels. L’intérêt de l’exercice est de comprendre pourquoi l’algorithme glouton est correct pour cette suite de poids particulière.

Voir la question dans le sujet PDF, p. 13 (nouvel onglet)
Chiffrer puis vérifier chaque décision du déchiffrementUn atelier pour expérimenter

Choisissez la valeur d’un octet de 0 à 255. Le modèle calcule ses huit bits, le total chiffré et tous les restes de la décomposition gloutonne avec la clé du sujet.

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

10111101 devient 688

Le sac est décroissant et chaque poids dépasse la somme de tous les suivants : la décision gloutonne ne peut pas être compensée par de plus petits poids.

PoidsReste avantBit choisiReste après
5686881120
2051200120
71120149
3249117
101717
6711
2101
1110

Un algorithme glouton n’est correct que sous une propriété structurelle : ici, aucun ensemble de petits poids ne remplace un poids plus grand.

Revoir les notions de cet exercice

Du sujet à la méthode

Votre prochaine séance de révision

  • Dans les requêtes, vérifiez quelle table apporte chaque colonne avant de choisir les jointures.
  • Pour la récursivité, distinguez le problème de profondeur du problème de recalcul.
  • Une décision gloutonne demande une justification : expliquez ici pourquoi les poids restants ne peuvent compenser un poids omis.

Retrouver ces notions dans d’autres sujets

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

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