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
Arbres de codage et compression de Shannon-Fano
Le codage de Shannon-Fano, mis au point par Robert Fano à partir d’une idée de Claude Shannon, compresse des données sans perte. Dans un arbre de codage binaire, chaque feuille porte un symbole ; les bits du chemin depuis la racine forment son code. Pour décoder, on descend en suivant les bits jusqu’à une feuille, puis on repart de la racine pour le symbole suivant.
Partie A : utiliser un arbre
Dans la figure 1, c est codé 1101 et d 11000. Les branches de gauche portent ici 1, celles de droite 0.
Lire les connexions du schéma
- • relié à • : 1
- • relié à • : 1
- • relié à • : 1
- • relié à i : 1
- • relié à u : 0
- • relié à • : 0
- • relié à c : 1
- • relié à • : 0
- • relié à o : 1
- • relié à d : 0
- • relié à • : 0
- • relié à • : 1
- • relié à , : 1
- • relié à p : 0
- • relié à • : 0
- • relié à n : 1
- • relié à j : 0
- • relié à • : 0
- • relié à • : 1
- • relié à s : 1
- • relié à SP : 0
- • relié à e : 0
Question 1
#Écrire le mot binaire utilisé pour encoder l’espace, représenté par _ dans l’arbre.
Comprendre la correction
010. Depuis la racine : droite (0), gauche (1), droite (0). L’espace n’est pas un séparateur implicite : c’est une feuille à part entière.
Question 2
#Déterminer le texte codé par 0001110101111110011001.
Comprendre la correction
| Bits lus jusqu’à une feuille | Symbole |
|---|---|
| 00 | e |
| 011 | s |
| 1010 | p |
| 1111 | i |
| 11001 | o |
| 1001 | n |
Le texte est espion. On ne découpe pas les bits en blocs de taille fixe : chaque feuille rencontrée indique la fin du code courant. Le dernier bit est consommé exactement à la feuille n, sans reste.
Question 3
#Citer le type de parcours qui permettrait d’obtenir les symboles classés par taille d’encodage croissante.
Comprendre la correction
Un parcours en largeur visite les niveaux dans l’ordre des profondeurs. On ne retient que les feuilles rencontrées : leur profondeur est précisément la longueur de leur code. Un parcours en profondeur pourrait atteindre une longue branche avant une feuille plus proche de la racine.
Partie B : construire l’arbre
Le texte est je pense, donc je suis. Étape 1 : classer les symboles par effectifs croissants. Étape 2 : couper cette liste en deux groupes contigus dont les effectifs totaux sont aussi proches que possible. Étape 3 : premier groupe à gauche, étiquette 1, second groupe à droite, étiquette 0. Étape 4 : recommencer dans chaque groupe jusqu’à n’avoir qu’un symbole.
| Symbole | i | u | c | o | d | , | p | n | j | s | _ | e |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Occurrences | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 2 | 2 | 3 | 4 | 4 |
Lire les connexions du schéma
- i u c o d , p n j s _ e : 22 relié à i u c o d , p n j : 11 : 1
- i u c o d , p n j s _ e : 22 relié à s _ e : 11 : 0
Question 4
#Justifier par le calcul que l’étape 2 mène à la séparation de la figure 2.
Comprendre la correction
Le premier groupe contient i, u, c, o, d, virgule, p, n, j : 7 × 1 + 2 + 2 = 11 occurrences. Le second contient s, espace et e : 3 + 4 + 4 = 11. Les deux sommes sont égales ; l’écart nul est le plus petit possible.
La figure 3 détaille les groupes de l’arbre obtenu :
| Préfixe | Groupe et effectifs |
|---|---|
| racine | i1 u1 c1 o1 d1 ,1 p1 n2 j2 s3 _4 e4 |
| 1 | i1 u1 c1 o1 d1 ,1 p1 n2 j2 |
| 0 | s3 _4 e4 |
| 11 | i1 u1 c1 o1 d1 |
| 10 | ,1 p1 n2 j2 |
| 01 | s3 _4 |
| 111 | i1 u1 |
| 110 | c1 o1 d1 |
| 101 | ,1 p1 |
| 100 | n2 j2 |
| 1100 | o1 d1 |
Lire les connexions du schéma
- • relié à • : 1
- • relié à • : 1
- • relié à • : 1
- • relié à i : 1
- • relié à u : 0
- • relié à • : 0
- • relié à c : 1
- • relié à • : 0
- • relié à o : 1
- • relié à d : 0
- • relié à • : 0
- • relié à • : 1
- • relié à , : 1
- • relié à p : 0
- • relié à • : 0
- • relié à n : 1
- • relié à j : 0
- • relié à • : 0
- • relié à • : 1
- • relié à s : 1
- • relié à SP : 0
- • relié à e : 0
Question 5
#Donner la hauteur de l’arbre de la figure 3 et préciser ce qu’elle représente ici. Une feuille seule est de hauteur 0.
Comprendre la correction
La hauteur vaut 5 : il y a cinq arêtes sur le plus long chemin racine-feuille, pour o ou d. C’est la longueur maximale d’un code de symbole. Il faut compter les arêtes, pas les six nœuds de ce chemin.
Question 6
#Justifier, en comparant ASCII et Shannon-Fano, que ce second codage utilise environ deux fois moins d’octets pour je pense, donc je suis. Le sujet compte un octet par symbole ASCII.
Indice
Additionnez occurrence×longueur de code pour chaque symbole.
Comprendre la correction
| Longueur du code | Symboles et occurrences | Contribution |
|---|---|---|
| 2 | e : 4 | 8 |
| 3 | s : 3 ; espace : 4 | 21 |
| 4 | i, u, c, virgule, p : 1 chacun ; n, j : 2 chacun | 36 |
| 5 | o, d : 1 chacun | 10 |
Le texte contient 22 caractères, donc 22 octets ou 176 bits avec la convention ASCII du sujet. Shannon-Fano utilise 8 + 21 + 36 + 10 = 75 bits, soit 9,375 octets de données binaires, arrondis à 10 octets pour les stocker dans des octets complets.
Le rapport est 22 / 10 = 2,2 en comptant cet arrondi : l’ordre de grandeur « environ deux fois moins » est justifié. Le calcul ne comprend pas le coût de transmission de l’arbre ou de la table de codage.
Question 7
#Dessiner un arbre de codage permettant d’encoder « chiffrer » avec Shannon-Fano.
Comprendre la correction
| Groupe | Premier sous-groupe, bit 1 | Second sous-groupe, bit 0 |
|---|---|---|
| c1 h1 i1 e1 f2 r2 | c1 h1 i1 e1 : 4 | f2 r2 : 4 |
| c1 h1 i1 e1 | c1 h1 : 2 | i1 e1 : 2 |
| c1 h1 | c1 | h1 |
| i1 e1 | i1 | e1 |
| f2 r2 | f2 | r2 |
Lire les connexions du schéma
- • relié à • : 1
- • relié à • : 1
- • relié à c : 1
- • relié à h : 0
- • relié à • : 0
- • relié à i : 1
- • relié à e : 0
- • relié à • : 0
- • relié à f : 1
- • relié à r : 0
Les quatre lettres de fréquence 1 peuvent être ordonnées autrement au départ. Le code reste valide si chaque partage respecte cet ordre et rapproche les poids. Ici, les lettres fréquentes f et r ont des codes plus courts de deux bits.
Partie C : programmer l’encodage
def creer_dico_occ(texte):
"""Renvoie le nombre d’occurrences de chaque symbole."""
dico = {}
for symbole in texte:
if symbole in dico:
dico[symbole] = ...
else:
dico[symbole] = ...
return dicoQuestion 8
#Compléter les lignes 8 et 10 de creer_dico_occ.
Comprendre la correction
def creer_dico_occ(texte):
dico = {}
for symbole in texte:
if symbole in dico:
dico[symbole] = dico[symbole] + 1
else:
dico[symbole] = 1
return dicoSi le symbole est connu, on augmente son compteur ; sinon son premier passage donne le compteur 1. La boucle examine chaque caractère, y compris les espaces et la virgule. Une chaîne vide produit naturellement un dictionnaire vide.
La fonction fournie creer_tab_trie transforme le dictionnaire en une liste de couples classés par effectifs croissants. Pour le texte étudié, elle renvoie :
[('i', 1), ('u', 1), ('c', 1), ('o', 1), ('d', 1), (',', 1),
('p', 1), ('n', 2), ('j', 2), ('s', 3), (' ', 4), ('e', 4)]Question 9
#Écrire somme_occ(tab), qui prend un tableau de tuples (symbole, nb_occ) et renvoie la somme des nombres d’occurrences.
Comprendre la correction
def somme_occ(tab):
total = 0
for symbole, nb_occ in tab:
total = total + nb_occ
return totalOn additionne le second élément de chaque tuple. Le symbole n’intervient pas dans ce calcul. Pour les douze couples du tableau donné, on obtient 22 ; pour une liste vide, 0.
La fonction de séparation est fournie par le sujet :
def separe(tab):
moitie = somme_occ(tab) // 2
somme = 0
i = 0
while moitie > somme:
somme = somme + tab[i][1]
i = i + 1
tab1 = [tab[k] for k in range(0, i)]
tab2 = [tab[k] for k in range(i, len(tab))]
return tab1, tab2def shannon(symbole, tab):
if len(tab) == 1:
return ""
else:
t1, t2 = separe(tab)
if symbole in [elt[0] for elt in t1]:
return "1" + ...
else:
return "0" + ...Question 10
#Compléter les lignes 9 et 11 de la fonction récursive shannon(symbole, tab), qui renvoie l’écriture binaire associée au symbole présent dans le tableau trié.
PythonRetrouver le chemin binaire d’un symboleÉcrivez votre solution et mettez-la à l’épreuve
Complétez shannon(symbole, tab). tab est une liste non vide de couples (symbole, fréquence), triée par fréquence croissante, et le symbole cherché y figure. separe est fourni : le premier groupe porte le bit 1, le second le bit 0. Une feuille ajoute une chaîne vide. Pour respecter le code imprimé, les tests utilisent des fréquences dont tous les partages sont non vides. La séparation officielle peut échouer à réduire d’autres tableaux : cette limite est étudiée dans la question suivante.
def shannon(symbole, tab):
# À vous de jouer
passLes cas de test proposés :
- Un groupe réduit à une feuille : Aucune arête ne reste à parcourir. Le code demandé renvoie une chaîne vide, pas le caractère ni le bit 0.
- Respecter les étiquettes des branches : Le sujet attribue 1 au premier groupe et 0 au second, même si d’autres conventions existent.
- Deux niveaux de récursion : Le bit choisi à la racine précède celui choisi dans le sous-groupe.
- Une feuille plus proche de la racine : La longueur des codes n’est pas fixe. c est seul dès le premier partage.
- Des fréquences différentes, tableau inchangé : Le tableau sert à déterminer les groupes ; la recherche du code ne doit ni le vider ni le réordonner.
Indice 1
L’appel récursif conserve le symbole et réduit le tableau au groupe choisi.
Indice 2
Le code d’un chemin est un bit suivi du code du reste du chemin.
Comprendre la correction
def shannon(symbole, tab):
if len(tab) == 1:
return ""
else:
t1, t2 = separe(tab)
if symbole in [elt[0] for elt in t1]:
return "1" + shannon(symbole, t1)
else:
return "0" + shannon(symbole, t2)Le test détermine dans quel groupe se trouve la feuille cherchée. On ajoute le bit de la branche avant le suffixe calculé récursivement sur ce groupe. Une seule branche est explorée. Lorsque le groupe contient une seule feuille, il ne reste aucune arête à parcourir, d’où la chaîne vide.
Exemple pour e : le premier partage l’envoie dans le groupe droit, préfixe 0 ; le partage suivant à droite encore, préfixe 00 ; le groupe contient alors seulement e.
Question 11
#Décrire ce qui garantit la terminaison de la fonction récursive shannon.
Indice
La longueur ne décroît que si chaque sous-tableau est strictement plus court.
Comprendre la correction
Pour les partages non vides utilisés dans l’exemple, la longueur du tableau est un entier strictement décroissant à chaque appel récursif. Elle atteint 1, le cas d’arrêt. C’est le variant attendu dans le raisonnement.
Limite du code fourni : sa fonction separe ne garantit pas deux groupes non vides pour toute fréquence possible. Avec [('b', 1), ('a', 5)], la moitié vaut 3 ; la boucle cumule 1 puis 6 et renvoie le tableau entier et une liste vide. Le prochain appel reprend alors exactement le même tableau. On ne peut donc pas affirmer une terminaison générale sans une hypothèse supplémentaire ou une correction.
Voici une variante qui cherche la meilleure coupure parmi les seules coupures intérieures ; elle assure à la fois deux groupes non vides et l’écart minimal :
def separe_robuste(tab):
assert len(tab) >= 2
total = somme_occ(tab)
cumul = 0
meilleur_ecart = total + 1
coupure = 1
for i in range(1, len(tab)):
cumul = cumul + tab[i - 1][1]
ecart = abs(total - 2 * cumul)
if ecart < meilleur_ecart:
meilleur_ecart = ecart
coupure = i
return tab[:coupure], tab[coupure:]La quantité abs(total - 2 * cumul) est la différence absolue entre les deux sommes. En limitant i entre 1 et len(tab) - 1, aucune branche ne reçoit le tableau entier. Cette amélioration est distincte du code officiel à compléter.
Question 12
#Écrire encode_shannon(texte), qui renvoie le mot binaire encodé. On pourra utiliser creer_dico_occ, creer_tab_trie, separe et shannon.
Comprendre la correction
def encode_shannon(texte):
dico = creer_dico_occ(texte)
tab = creer_tab_trie(dico)
resultat = ""
for symbole in texte:
resultat = resultat + shannon(symbole, tab)
return resultatOn calcule les fréquences et le tableau une seule fois pour tout le texte. Chaque caractère utilise ensuite ce même arbre ; recalculer un arbre pour chaque lettre ferait perdre le code commun nécessaire au décodage.
Cette version correspond à l’exercice et fonctionne sur son exemple. Le départage des coupures ex aequo du code fourni diffère de la figure 3 : il donne par exemple j = 100 et p = 10110, tout en conservant 75 bits pour le texte. Pour décoder, on doit utiliser la table réellement produite, pas mélanger ces deux arbres. Pour une utilisation générale, il faut employer une séparation toujours non vide comme celle expliquée en Q11. Un texte composé d’un seul symbole réclame aussi une convention supplémentaire : avec le cas de base fourni, son code est vide. On peut choisir le code 0 et conserver la longueur du message dans le format. Le sujet ne demande pas de définir ce format de fichier.
Coupez les fréquences, puis suivez un symboleUn atelier pour expérimenter
Choisissez un message et une lettre. L’atelier construit des groupes non vides aux sommes les plus proches, puis montre tous les partages conduisant à cette lettre. Le cas dominant révèle pourquoi il faut protéger la séparation.
Lire le résultat de l’expérience initiale
i se code 1111
La branche gauche vaut 1. L’atelier exclut les coupures vides ; contrairement au code separe imprimé, il termine aussi pour un symbole dominant.
| Groupe avant partage | Poids total | Bit choisi | Groupe conservé |
|---|---|---|---|
| i u c o d , p n j s SP e | 22 | 1 | i u c o d , p n j |
| i u c o d , p n j | 11 | 1 | i u c o d |
| i u c o d | 5 | 1 | i u |
| i u | 2 | 1 | i |
Le variant de récursion est la taille du groupe. Il décroît uniquement si chaque partage conserve deux groupes non vides : une preuve dépend réellement de cette propriété.
Exercice 2 · 6 points
Ludothèque : emprunts SQL et podium avec ex aequo
Une ludothèque municipale gère les jeux, ses adhérents, les emprunts et les avis. Elle ne possède qu’un exemplaire de chaque jeu ; deux jeux ne peuvent donc pas avoir le même nom. La figure 1 indique les clés primaires soulignées et les clés étrangères par un croisillon.
Lire les connexions du schéma
- avis vers jeu : nomJeu
- avis vers adherent : idAdherent
- emprunt vers jeu : nomJeu
- emprunt vers adherent : idAdherent
| Relation | Attributs (clé primaire indiquée) | Clés étrangères |
|---|---|---|
| jeu | nomJeu (PK), editeur, anneeSortie, ageMinimum, categorie | Aucune |
| avis | idAvis (PK), nomJeu, idAdherent, commentaire | nomJeu → jeu.nomJeu ; idAdherent → adherent.idAdherent |
| emprunt | idEmprunt (PK), nomJeu, idAdherent, dateEmprunt, dateRendu | nomJeu → jeu.nomJeu ; idAdherent → adherent.idAdherent |
| adherent | idAdherent (PK), nom, prenom, dateNaissance, adresse | Aucune |
On peut utiliser SELECT, FROM, WHERE, AND, OR, JOIN ... ON, UPDATE, INSERT, DELETE, DISTINCT, ORDER BY et COUNT. SELECT COUNT(nomJeu) FROM jeu donne le nombre de jeux.
Question 1
#Expliquer pourquoi nom ne peut pas être la clé primaire de adherent.
Comprendre la correction
Plusieurs adhérents peuvent partager un nom de famille. La valeur de nom ne suffit donc pas à identifier une ligne de façon unique. idAdherent, choisi comme clé dans le schéma, distingue les homonymes sans supposer quoi que ce soit sur leurs noms.
SELECT nomJeu, editeur
FROM jeu
ORDER BY nomJeu;Question 2
#Décrire le résultat de la requête suivante.
Comprendre la correction
Elle renvoie le nom et l’éditeur de chaque jeu, classés par ordre croissant du nom du jeu. Elle ne regroupe pas les jeux par éditeur et ne modifie pas la table.
Question 3
#Écrire une requête pour connaître le nom de tous les jeux en cours d’emprunt. Pour un jeu non rendu, dateRendu vaut NULL.
Comprendre la correction
SELECT nomJeu
FROM emprunt
WHERE dateRendu IS NULL;NULL se teste avec IS NULL, pas avec = NULL : il représente l’absence de valeur. Le nom du jeu est déjà dans la table d’emprunts, donc aucune jointure n’est nécessaire.
Question 4
#Écrire une requête affichant le nom et le prénom de tous les adhérents ayant emprunté « Catan ».
SQLRetrouver les emprunteurs de CatanÉcrivez votre solution et mettez-la à l’épreuve
Renvoyez le nom et le prénom des adhérents ayant emprunté Catan. Une personne qui l’a emprunté plusieurs fois ne doit apparaître qu’une fois dans cette projection.
SELECT ...
FROM ...
WHERE ...;Les cas de test proposés :
- Cas pédagogique autour de la visite citée : Le sujet cite Claire VOYANT, son emprunt 1538 de Catan et le retour du 3 juin 2025. Les identifiants d’adhérent, dates de départ, autres lignes et attributs absents sont des compléments pédagogiques.
- Cas complémentaire : plusieurs personnes et plusieurs emprunts : Jeu complémentaire pédagogique, distinct des données officielles. Les emprunts passés et présents comptent tous ; seul le nom du jeu et l’identité de la personne importent.
Comprendre la correction
SELECT DISTINCT adherent.nom, adherent.prenom
FROM adherent JOIN emprunt
ON adherent.idAdherent = emprunt.idAdherent
WHERE emprunt.nomJeu = 'Catan';La jointure relie chaque emprunt à l’adhérent correspondant. DISTINCT élimine les répétitions d’un même couple nom-prénom lorsqu’une personne a emprunté plusieurs fois Catan. Le sujet ne demande pas de limiter aux emprunts actuels.
Deux personnes ayant exactement le même nom et prénom deviennent indiscernables dans cette projection : pour les distinguer, on devrait aussi afficher leur identifiant, mais la question demande seulement nom et prénom.
Question 5
#Claire VOYANT a rendu Catan le 3 juin 2025. L’identifiant de cet emprunt est 1538. Écrire la requête de mise à jour ; les dates sont au format AAAA-MM-JJ.
Indice
Le schéma et le texte n’écrivent pas l’identifiant exactement de la même façon.
Comprendre la correction
UPDATE emprunt
SET dateRendu = '2025-06-03'
WHERE idEmprunt = 1538;Le schéma écrit idEmprunt ; le texte de la question imprime id_emprunt. La requête utilise le nom effectif de la colonne de la figure. L’identifiant permet de viser cet emprunt précis, même si Claire a emprunté plusieurs fois le même jeu.
Question 6
#Écrire une requête donnant le nom et la catégorie des jeux sortis à partir de 2010 et dont l’âge minimum est strictement inférieur à 10 ans.
Comprendre la correction
SELECT nomJeu, categorie
FROM jeu
WHERE anneeSortie >= 2010 AND ageMinimum < 10;« À partir de 2010 » inclut 2010, alors que « strictement inférieur à 10 » exclut 10. Les deux conditions doivent être vraies simultanément, d’où AND.
La figure 2 ajoute evenement(nom, dateEvenement, heure), où nom est clé primaire, et participation(idParticipation, ...), où idParticipation est clé primaire. Les quatre relations initiales restent identiques. Les références manquantes de participation sont à concevoir.
Lire les connexions du schéma
Question 7
#Proposer les clés étrangères de participation et préciser les attributs référencés.
Comprendre la correction
| Nouvel attribut de participation | Référence |
|---|---|
| idAdherent | adherent.idAdherent |
| nomEvenement | evenement.nom |
Chaque participation relie un adhérent à un événement. idParticipation identifie la ligne, mais ne suffit pas à retrouver les deux entités concernées. Les noms des deux nouvelles colonnes sont proposés : le schéma les laisse à compléter.
Le programme fourni récupère la liste des jeux empruntés, avec une occurrence par emprunt :
import sqlite3
# Connexion à la base de données
connection = sqlite3.connect("ludotheque.db")
curseur = connection.cursor()
# Exécution de la requête
curseur.execute("SELECT nomJeu FROM emprunt")
# Récupération des résultats
jeux = curseur.fetchall()
liste = []
for jeu in jeux:
liste.append(jeu[0])
# Fermeture de la connexion
curseur.close()
connection.close()Question 8
#Écrire un script créant dict_emprunts, qui associe à chaque jeu emprunté son nombre d’emprunts à partir de liste.
Comprendre la correction
dict_emprunts = {}
for jeu in liste:
if jeu in dict_emprunts:
dict_emprunts[jeu] = dict_emprunts[jeu] + 1
else:
dict_emprunts[jeu] = 1La liste contient une occurrence par emprunt, pas seulement un exemplaire de chaque nom. On crée la clé au premier emprunt, puis on augmente le compteur aux passages suivants. Une liste vide donne un dictionnaire vide ; aucun jeu absent de la liste n’est inventé avec un compteur nul.
dict_emprunts = {
"Terraforming Mars": 25,
"Codenames": 22,
"Agricola": 18,
"Puerto Rico": 18,
"Caylus": 18,
"Dominion": 22,
"Dixit": 12
}Le sujet donne le podium suivant :
[["Agricola", "Puerto Rico", "Caylus"],
["Dominion", "Codenames"], ["Terraforming Mars"]]Question 9
#Proposer un script générant un podium sous forme de trois listes. Garder tous les ex aequo aux trois meilleurs nombres d’emprunts, dans l’ordre troisième place, deuxième place, première place.
Indice 1
Il faut classer les nombres d’emprunts distincts, pas prendre seulement les trois premiers jeux.
Indice 2
Pour chaque niveau retenu, récupérez tous les jeux qui ont cet effectif.
Comprendre la correction
effectifs = []
for nombre in dict_emprunts.values():
if nombre not in effectifs:
effectifs.append(nombre)
effectifs.sort(reverse=True)
meilleurs = effectifs[:3]
podium = [[], [], []]
for rang in range(len(meilleurs)):
for jeu, nombre in dict_emprunts.items():
if nombre == meilleurs[rang]:
podium[2 - rang].append(jeu)On classe d’abord les effectifs distincts, puis on récupère tous les jeux associés aux trois plus grands. Cela évite qu’un ex aequo à la première place occupe artificiellement la deuxième place. L’indice 2 - rang place le maximum à droite, comme dans l’exemple.
Avec les données fournies, les niveaux sont 25, 22 et 18. Le résultat est [['Agricola', 'Puerto Rico', 'Caylus'], ['Codenames', 'Dominion'], ['Terraforming Mars']]. L’ordre à l’intérieur d’une place n’est pas imposé ; la deuxième liste peut donc inverser Codenames et Dominion. Le script garde des listes vides si moins de trois effectifs distincts existent.
Un podium qui ne sacrifie aucun ex aequoUn atelier pour expérimenter
Faites varier les emprunts de Dixit et de Codenames. Observez les trois niveaux distincts retenus et les jeux qui se partagent chaque marche.
Lire le résultat de l’expérience initiale
6 jeux sur trois marches
Une marche correspond à un nombre d’emprunts distinct. Plusieurs jeux partagent la même marche ; ils ne consomment pas des rangs supplémentaires.
| Place | Emprunts | Jeux ex aequo |
|---|---|---|
| 1 | 25 | Terraforming Mars |
| 2 | 22 | Codenames, Dominion |
| 3 | 18 | Agricola, Puerto Rico, Caylus |
Un classement avec ex aequo doit définir ce qu’est une place. Ici, les trois plus grands effectifs distincts donnent trois groupes, potentiellement plus de trois jeux.
Exercice 3 · 8 points
Masque jetable, HTTPS et segmentation IPv4
Partie A : masque jetable
Le sujet décrit un masque jetable : une clé au moins aussi longue que le message, choisie de manière totalement aléatoire et utilisée une seule fois. Dans ce modèle, les messages et clés ne contiennent que les lettres majuscules non accentuées A à Z. À chaque lettre on associe son rang de 0 à 25 ; le chiffrement ajoute les rangs du message et de la clé modulo 26.
| Lettre | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Rang | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
La figure 1, adaptée de l’article « Masque jetable » de Wikipédia cité dans le sujet, donne HELLO + WMCKL = DQNVZ :
| Étape | H / W | E / M | L / C | L / K | O / L |
|---|---|---|---|---|---|
| Message | 7 | 4 | 11 | 11 | 14 |
| Masque | 22 | 12 | 2 | 10 | 11 |
| Somme | 29 | 16 | 13 | 21 | 25 |
| Modulo 26 | 3 | 16 | 13 | 21 | 25 |
| Chiffré | D | Q | N | V | Z |
Question 1
#Chiffrer LIBRE avec la clé EYQMT par la méthode du masque jetable.
Comprendre la correction
| Position | Message | Clé | Somme | Modulo 26 | Chiffré |
|---|---|---|---|---|---|
| 1 | L : 11 | E : 4 | 15 | 15 | P |
| 2 | I : 8 | Y : 24 | 32 | 6 | G |
| 3 | B : 1 | Q : 16 | 17 | 17 | R |
| 4 | R : 17 | M : 12 | 29 | 3 | D |
| 5 | E : 4 | T : 19 | 23 | 23 | X |
Le message obtenu est PGRDX. Les sommes 32 et 29 dépassent 25 ; on leur retire une fois 26. Une seule soustraction suffit puisque la somme de deux rangs est au maximum 50.
La variable globale accessible à toutes les fonctions est :
alphabet = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z']Question 2
#Écrire indice(L, element), qui renvoie l’indice de element dans L. Les éléments sont uniques et l’élément cherché est présent. Exemple : indice(alphabet, 'K') vaut 10.
Comprendre la correction
def indice(L, element):
i = 0
while L[i] != element:
i = i + 1
return iOn part de l’indice 0 et on avance jusqu’à l’égalité. La précondition garantit que la recherche rencontre l’élément avant la fin de la liste. Sans cette précondition, il faudrait tester la borne et décider quoi renvoyer si l’élément est absent.
Question 3
#Écrire lettres_vers_indices, qui reçoit une chaîne et renvoie, dans l’ordre, la liste des indices de ses lettres. HELLO doit donner [7, 4, 11, 11, 14].
Comprendre la correction
def lettres_vers_indices(texte):
resultat = []
for lettre in texte:
resultat.append(indice(alphabet, lettre))
return resultatOn parcourt les lettres dans l’ordre du message et on ajoute chaque rang en fin de liste. Des lettres répétées doivent donner des rangs répétés : on ne construit pas un ensemble.
On dispose de indices_vers_lettres, qui transforme une liste d’entiers entre 0 et 25 en chaîne. Par exemple, [3, 16, 13, 21, 25] donne DQNVZ.
def chiffrement(msg, cle):
assert len(cle) >= len(msg), 'impossible'
indices_msg = lettres_vers_indices(msg)
indices_cle = lettres_vers_indices(cle)
n = len(msg)
indices_msg_chiffre = []
for k in range(n):
ind = ...
if ind >= 26:
ind = ...
indices_msg_chiffre.append(ind)
msg_chiffre = indices_vers_lettres(...)
return msg_chiffreQuestion 4
#Compléter les lignes 7 à 13 de chiffrement(msg, cle).
Comprendre la correction
def chiffrement(msg, cle):
assert len(cle) >= len(msg), 'impossible'
indices_msg = lettres_vers_indices(msg)
indices_cle = lettres_vers_indices(cle)
n = len(msg)
indices_msg_chiffre = []
for k in range(n):
ind = indices_msg[k] + indices_cle[k]
if ind >= 26:
ind = ind - 26
indices_msg_chiffre.append(ind)
msg_chiffre = indices_vers_lettres(indices_msg_chiffre)
return msg_chiffrePour chaque position du message, on additionne les deux indices de cette même position. Si la somme atteint 26, on la ramène dans l’alphabet. On reconvertit la liste entière seulement après la boucle.
Une clé plus longue est autorisée ; seuls ses premiers len(msg) caractères sont utilisés. La chaîne vide produit une chaîne vide, et aucun accès d’indice n’est effectué.
Question 5
#Indiquer et justifier ce qu’on observe avec chiffrement('RESEAU', 'GFTZ').
Comprendre la correction
Une AssertionError portant le message impossible est levée dès la ligne 2 : la clé contient 4 caractères, alors que le message en contient 6. L’exécution s’arrête avant le chiffrement ; aucun message chiffré n’est renvoyé.
Cette réponse suppose l’exécution habituelle de Python, où les assertions sont actives. Une validation de production devrait utiliser un contrôle explicite si elle doit subsister en mode optimisé.
Question 6
#Déchiffrer GMEDH avec la clé FVEIT.
Comprendre la correction
| Chiffré | Clé | Différence | Modulo 26 | Clair |
|---|---|---|---|---|
| G : 6 | F : 5 | 1 | 1 | B |
| M : 12 | V : 21 | -9 | 17 | R |
| E : 4 | E : 4 | 0 | 0 | A |
| D : 3 | I : 8 | -5 | 21 | V |
| H : 7 | T : 19 | -12 | 14 | O |
Le message en clair est BRAVO. Une différence négative est augmentée de 26 pour revenir dans l’intervalle des rangs.
Question 7
#Expliquer comment déchiffrer un message quand on connaît la clé.
Comprendre la correction
On associe leurs rangs aux lettres du texte chiffré et de la clé, puis on calcule position par position rang_chiffre - rang_cle modulo 26. On remplace chaque résultat par sa lettre. La soustraction inverse l’addition du chiffrement : (m + k - k) modulo 26 = m.
Question 8
#Adapter les lignes 6 à 13 pour obtenir dechiffrement. Les paramètres et les lignes 2 à 5 restent inchangés ; le résultat intermédiaire s’appelle indices_msg_dechiffre.
Indice
Soustraire inverse l’addition ; un résultat négatif doit revenir dans 0 à 25.
Comprendre la correction
def dechiffrement(msg, cle):
assert len(cle) >= len(msg), 'impossible'
indices_msg = lettres_vers_indices(msg)
indices_cle = lettres_vers_indices(cle)
n = len(msg)
indices_msg_dechiffre = []
for k in range(n):
ind = indices_msg[k] - indices_cle[k]
if ind < 0:
ind = ind + 26
indices_msg_dechiffre.append(ind)
msg_dechiffre = indices_vers_lettres(indices_msg_dechiffre)
return msg_dechiffreLa structure reste la même. Deux changements algorithmiques suffisent : soustraire au lieu d’additionner, puis ajouter 26 si le résultat est négatif. Tous les noms de variables désignant le résultat sont adaptés pour ne pas lire par erreur une ancienne liste.
Partie B : sécurisation des communications
Question 9
#Expliquer la différence entre chiffrement symétrique et asymétrique.
Comprendre la correction
Un chiffrement symétrique utilise un secret partagé entre les interlocuteurs, généralement la même clé pour chiffrer et déchiffrer. Un chiffrement asymétrique utilise une paire liée mathématiquement : une clé publique peut être diffusée, l’autre reste privée. Pour envoyer confidentiellement à Bob, on chiffre avec sa clé publique et il déchiffre avec sa clé privée.
Cette distinction porte sur les clés et leur distribution. À elle seule, elle ne dit pas que l’identité d’un expéditeur a été prouvée.
Question 10
#Bob transmet sa clé publique à Alice, qui chiffre son message avec cette clé puis lui envoie le résultat. Comment Bob le déchiffre-t-il ?
Comprendre la correction
Bob utilise sa clé privée, associée à la clé publique employée par Alice. Il ne doit pas transmettre cette clé privée à Alice : elle est précisément le secret qui réserve le déchiffrement à Bob.
Question 11
#Expliquer comment une tierce personne pourrait se faire passer pour Alice sans que Bob s’en aperçoive.
Comprendre la correction
La clé publique de Bob est publique : un tiers peut donc chiffrer son propre message avec elle et prétendre être Alice. Le fait que Bob puisse déchiffrer le message prouve qu’il lui était destiné, pas qui l’a écrit.
Il faut ajouter un mécanisme d’authentification de l’expéditeur, par exemple une signature vérifiée avec une clé publique d’Alice dont l’identité a été établie. La confidentialité et l’authenticité répondent à deux questions distinctes.
Question 12
#Expliquer brièvement le fonctionnement de HTTPS.
Comprendre la correction
HTTPS est HTTP transporté dans une connexion TLS. Le client vérifie le certificat du serveur, notamment le nom de domaine et la chaîne de confiance, puis un échange cryptographique établit des clés de session. Les données HTTP circulent ensuite avec chiffrement symétrique authentifié, qui protège leur confidentialité et permet de détecter les modifications.
Les mécanismes asymétriques servent notamment à l’authentification et à l’établissement des clés ; le trafic applicatif n’est pas chiffré lettre par lettre avec une clé publique. Les détails exacts de l’échange dépendent de la version et des paramètres TLS.
Question 13
#Expliquer pourquoi on utilise HTTPS pour sécuriser les communications sur Internet plutôt qu’un seul chiffrement asymétrique.
Comprendre la correction
HTTPS/TLS assemble plusieurs fonctions nécessaires : authentifier le serveur, établir les clés, protéger la confidentialité et l’intégrité des échanges, tout en utilisant un chiffrement symétrique efficace pour les données. Un simple chiffrement avec une clé publique n’authentifie pas à lui seul l’expéditeur ni le propriétaire réel de cette clé.
La formulation « intégralement » du sujet doit être nuancée : HTTPS ne garantit pas que le site est honnête et n’authentifie pas automatiquement Alice auprès de Bob. L’identité d’un utilisateur exige encore un mécanisme adapté, comme une connexion à un compte ou un certificat client.
Partie C : réseaux
Bob et Marc travaillent sur 192.168.110.0/24, masque 255.255.255.0. Les trois premiers octets forment le réseau ; le dernier désigne l’hôte. Bob reçoit l’identifiant hôte 115 et Marc 153. Le bloc contient 256 adresses IPv4 au total.
--- 192.168.100.115 ping statistics ---
4 packets transmitted, 0 received, 100% packet loss, time 3060msQuestion 14
#Marc lance ping 192.168.100.115 pour joindre Bob et obtient 4 paquets transmis, 0 reçu, 100 % de perte. Expliquer l’affichage et corriger son erreur.
Comprendre la correction
Aucune réponse n’a été reçue pour les quatre requêtes envoyées à l’adresse visée. Marc s’est trompé de réseau : il faut lancer ping 192.168.110.115, puisque le troisième octet du réseau de l’entreprise est 110, et non 100.
Une absence de réponse ne prouve pas à elle seule que la machine est éteinte : elle peut aussi venir d’un filtrage ou d’une erreur de routage. Ici, l’erreur d’adresse est directement visible dans les données de l’énoncé.
L’administratrice sépare le réseau en sous-réseaux reliés par des routeurs. Elle remplace le masque par la valeur binaire indiquée.
Question 15
#Donner la représentation décimale du nouveau masque 11111111.11111111.11111111.11100000.
Comprendre la correction
255.255.255.224. Les trois premiers octets valent 255 ; le dernier vaut 128 + 64 + 32 = 224. Le masque contient 27 bits à 1, soit un préfixe /27.
Une adresse réseau s’obtient par ET bit à bit entre masque et adresse. Pour Bob, le dernier octet est 115 :
11100000 (224, masque)
& 01110011 (115, Bob)
= 01100000 (96, réseau)Son sous-réseau est donc 192.168.110.96/27.
Question 16
#Indiquer le nombre total d’adresses IPv4 du sous-réseau 192.168.110.96 où se trouve Bob.
Comprendre la correction
Le /27 laisse 32 - 27 = 5 bits pour les hôtes : il contient donc 2⁵ = 32 adresses au total, de .96 à .127.
Parmi elles, .96 est l’adresse réseau et .127 celle de diffusion : 30 sont attribuables à des interfaces hôtes. Le sujet emploie « total d’adresses pouvant être attribuées » après avoir compté 256 adresses pour un /24 ; on explicite donc les deux nombres pour éviter de confondre capacité totale et hôtes utilisables.
Question 17
#Zoé reçoit 192.168.110.134. Donner la représentation binaire de 134.
Comprendre la correction
10000110, car 134 = 128 + 4 + 2. Les huit positions de l’octet sont conservées.
Question 18
#Zoé lance n°1 ping 192.168.110.115 et n°2 ping 192.168.110.153. Indiquer et justifier laquelle a produit 4 paquets transmis, 4 reçus, 0 % de perte.
Indice 1
Comparez les adresses réseau obtenues par ET avec 224.
Indice 2
Être dans deux sous-réseaux distincts impose un routage, pas nécessairement un échec.
Comprendre la correction
| Poste | Dernier octet | ET avec 224 | Sous-réseau |
|---|---|---|---|
| Bob | 115 | 96 | 192.168.110.96/27 |
| Marc | 153 | 128 | 192.168.110.128/27 |
| Zoé | 134 | 128 | 192.168.110.128/27 |
La commande n°2 vise Marc, dans le même sous-réseau que Zoé : c’est la réponse attendue si l’on raisonne sur la communication locale directe.
Limite de l’énoncé : il dit aussi que les sous-réseaux sont reliés par des routeurs. Avec un routage et des règles de filtrage permettant la communication, la commande n°1 peut également réussir. Les seules adresses ne permettent donc pas de conclure qu’elle a échoué. Le succès n°2 est cohérent avec une liaison locale ; son exclusivité demanderait des informations supplémentaires sur le routage ou le filtrage.
Chiffrez, puis retrouvez le messageUn atelier pour expérimenter
Choisissez une paire du sujet et passez du chiffrement au déchiffrement. Chaque ligne montre l’opération sur les rangs, le passage modulo 26 et la lettre obtenue.
Lire le résultat de l’expérience initiale
PGRDX
Pour être un masque jetable sûr, cette clé devrait être aléatoire, secrète et jamais réutilisée. Les clés fixes du sujet sont seulement des exemples de calcul.
| Position | Entrée | Clé | Résultat brut | Modulo 26 | Lettre |
|---|---|---|---|---|---|
| 1 | L (11) | E (4) | 15 | 15 | P |
| 2 | I (8) | Y (24) | 32 | 6 | G |
| 3 | B (1) | Q (16) | 17 | 17 | R |
| 4 | R (17) | M (12) | 29 | 3 | D |
| 5 | E (4) | T (19) | 23 | 23 | X |
Le calcul modulo 26 est réversible avec la clé. La sécurité du masque jetable dépend surtout de la génération, du secret et de l’usage unique de cette clé.
Du sujet à la méthode
Votre prochaine séance de révision
- Dessinez les bits sur chaque branche avant de coder : ce sujet emploie 1 à gauche, contrairement à beaucoup d’exemples usuels.
- Pour un podium, séparez les niveaux d’effectifs des jeux qui appartiennent à chaque niveau.
- Justifiez les limites de vos conclusions : une preuve de terminaison exige une vraie décroissance ; une adresse dans un autre sous-réseau ne prouve pas un ping impossible.
Retrouver ces notions dans d’autres sujets
Toutes les annales de NSI · Le guide pour préparer le bac NSI 2027
Énoncé : sujet 25-NSIJ2ME1 (PDF). Corrigé et explications pédagogiques proposés par Sofien.
