Tous les sujets du Bac 2025 · Toutes les annales du Bac de Numérique et Sciences Informatiques

Sujet du Bac Numérique et Sciences Informatiques 2025 · Métropole 2 remplacement

Sujet du Bac de Numérique et Sciences Informatiques 2025, Métropole 2 remplacement : 6 exercices corrigés. Thèmes : Programmation orientée objet et Graphes.

Thèmes du sujet : Programmation orientée objet : classes, attributs, méthodes · Graphes : modélisation, implémentations et parcours · Bases de données relationnelles, SGBD et langage SQL · Binaire, entiers et opérateurs booléens · Architectures matérielles et systèmes d'exploitation · Sécurisation des communications : chiffrement et HTTPS

Exercice 1 · Sécurisation des communications : chiffrement et HTTPS

Exercice 1 (6 points)

Cet exercice porte sur la programmation Python et la cryptographie.

Le chiffrement Playfair, popularisé par Lord Playfair et utilisé par l'armée britannique durant les guerres du XXème siècle, est basé sur le chiffrement de paires de lettres (appelées digrammes).

Partie A: la clef de chiffrement

Ce chiffrement utilise un tableau de 5×55\times 5 lettres contenant un mot clef. On remplit le tableau avec les lettres du mot clef (sans doublons), puis on le complète avec les lettres restantes de l'alphabet (sans la lettre W) dans leur ordre alphabétique. Une lettre n'apparaît qu'une seule fois dans le tableau.

Par exemple, si on choisit comme clef le mot PLAYFAIR, le carré de chiffrement obtenu est le suivant:

On commence par les lettres de la clef (cases blanches) sans les doublons (ici le A) puis on complète le tableau (cases grisées) avec les lettres restantes de l'alphabet, dans l'ordre alphabétique.

1.

Donner le carré de chiffrement si la clef est EPREUVEDENSI. Remplir chaque case du tableau 5×55\times 5:

On donne ci-dessous le code Python de la fonction creer_liste_clef qui prend en paramètre la clef de chiffrement et renvoie une liste contenant 25 lettres ordonnées de la façon suivante: d'abord les lettres de la clef choisie (sans doublon) puis les lettres de l'alphabet restantes (classées par ordre alphabétique).

def creer_liste_clef(clef):

"""

hypothèse: la clef ne contient pas la lettre W

"""

deja_utilises = []

# alphabet sans la lettre W:

alphabet = 'ABCDEFGHIJKLMNOPQRSTUVXYZ'

for i in range(len(clef)):

if not (clef[i] in deja_utilises):

deja_utilises.append(clef[i])

for lettre in alphabet:

if not lettre in deja_utilises:

deja_utilises.append(lettre)

return deja_utilises 2.

On donne ci-dessous le code incomplet de la fonction creer_carre qui prend en paramètre la liste créée par la fonction creer_liste_clef et renvoie le carré de chiffrement.

def creer_carre(liste_clef):

carre = [[0 for i in range(5)] for j in range(5)]

for i in range(25):

carre[...][...] = liste_clef[i]

return carre 3.

Partie B: chiffrer un message

Le chiffrement d'un message se fait en deux étapes: 1ère étape: on découpe le message en digrammes (paires de lettres);

2ème étape: on chiffre chacun des digrammes avec le carré de chiffrement.

Pour découper le message en digrammes, on prend les lettres deux par deux en tenant compte de deux cas particuliers: si les deux lettres sont identiques, on ajoute un 'X' après la première lettre et on poursuit le découpage deux à deux à partir de la deuxième lettre;

s'il ne reste qu'une seule lettre, on forme une dernière paire en lui ajoutant la lettre un 'X'.

Par exemple: le découpage de 'BACCALAUREAT' donnera 'BA', 'CX', 'CA', 'LA', 'UR', 'EA', 'TX'.

Le chiffrement d'un message se fait ensuite en chiffrant chaque digramme (paire de lettres), de la manière suivante: si les lettres du digramme se trouvent sur la même ligne du carré de chiffrement, il faut les remplacer par celles se trouvant immédiatement à leur droite (en bouclant sur la gauche si le bord est atteint);

si les lettres apparaissent sur la même colonne du carré de chiffrement, les remplacer par celles qui sont juste en dessous (en bouclant par le haut si le bas de la table est atteint);

sinon, remplacer les lettres par celles se trouvant sur la même ligne du carré de chiffrement, mais dans le coin opposé du rectangle défini par la paire originale.

Par exemple, si le message est 'VIVELANSI', les digrammes sont: 'VI', 'VE', 'LA', 'NS', 'IX' et leurs codages avec la clef PLAYFAIR sont:

digramme · VI · VE · LA · NS · IX

chiffré · TB · TH · AY · OM · CT

On donne ci-dessous le code incomplet de la fonction couper_en_digrammes qui prend en paramètre une chaine de caractères et renvoie la liste des digrammes la constituant:

def couper_en_digrammes(message):

digrammes = []

i = 0

while i < len(message) - 1:

if message[i] == message[i+1]:

digrammes.append(message[i] + 'X')

i = i + 1

else:

...

i = i + 2

if i == len(message) - 1: #il reste une lettre isolée

digrammes.append(message[i] + 'X')

return digrammes 4.

5.

6.

7.

Donner le code Python de la fonction ligne_colonne qui prend en paramètres une lettre et le carré de chiffrement créé par la fonction creer_carre, et qui renvoie les coordonnées de la lettre dans le carré de chiffrement.

ligne_colonne('A', carre)

>>> (0, 2)

ligne_colonne('N', carre)

>>> (3, 1) def ligne_colonne(lettre, carre):

8.

Donner le code Python de la fonction sur_la_meme_ligne qui prend en paramètres un digramme et le carré de chiffrement créé par la fonction creer_carre, et qui renvoie True si les deux lettres du digramme sont sur la même ligne, ou False sinon.

sur_la_meme_ligne('RT', carre)

>>> False

sur_la_meme_ligne('PL', carre)

>>> True def sur_la_meme_ligne(digramme, carre):

On dispose pour la suite de la fonction sur_la_meme_colonne, similaire à sur_la_meme_ligne mais en colonne.

Voici le code incomplet de la fonction chiffrer_digramme:

def chiffrer_digramme(digramme, carre):

lettre1 = digramme[0]

lettre2 = digramme[1]

i1, j1 = ligne_colonne(lettre1, carre)

i2, j2 = ligne_colonne(lettre2, carre)

if sur_la_meme_ligne(digramme, carre):

digramme_chiffre = carre[i1][(j1 + 1)%5] + carre[i2][(j2 + 1)%5]

elif sur_la_meme_colonne(digramme, carre):

digramme_chiffre =...

else:

digramme_chiffre =...

return digramme_chiffre 9.

10.

Écrire le code python de la fonction chiffrer_playfair qui prend en paramètres deux chaînes de caractères message et clef correspondant au message à chiffrer et au mot-clef choisi, et qui renvoie le message chiffré, en utilisant les fonctions déjà écrites précédemment.

chiffrer_playfair('VIVELANSI', 'PLAYFAIR')

>>> 'TBTHAYOMCT' def chiffrer_playfair(message, clef):

Exercice 2 - Partie A · Architectures matérielles et systèmes d'exploitation

Exercice 2 (6 points)

Cet exercice porte sur les systèmes d'exploitation ainsi que sur programmation et la programmation orientée objet.

Partie A

Partie A

1.

Écrire, dans un système Linux, la commande masquée en noir qui a permis d'obtenir le résultat de la Figure 1.

Dans la Figure 1, les permissions des fichiers apparaissent au début de chaque ligne et sont composées de 10 caractères. En lisant de la gauche vers la droite:

le premier caractère indique la nature du fichier, - pour un fichier classique, d pour un répertoire et l pour un lien;

ensuite, on trouve trois groupes de 3 caractères chacun, indiquant si le fichier (ou répertoire) est autorisé en lecture r, écriture w ou exécution x. Si la permission n'est pas accordée, le caractère est remplacé par -. Les 3 groupes correspondent, dans cet ordre, aux droits du propriétaire (user), du groupe (group) puis du reste des utilisateurs (other).

2.

Décrire les permissions que les trois types d'utilisateurs (user, group ou other) ont sur le fichier verify.sh d'après la Figure 1.

3.

D'après la Figure 1, écrire la commande que l'utilisateur, propriétaire du fichier concat.pls, doit saisir pour supprimer ce fichier.

On s'intéresse à la façon de modifier les permissions d'un fichier avec la commande chmod. Pour cela on saisit dans un terminal la commande man chmod. La Figure 2 ci-après reproduit l'affichage obtenu.

4.

Avec les éléments de la Figure 1 et à la lecture de la documentation Figure 2, expliquer quel sera l'effet de l'instruction chmod g-wx todo.txt.

Exercice 2 - Partie B · Binaire, entiers et opérateurs booléens

Exercice 2 (6 points)

Cet exercice porte sur les systèmes d'exploitation ainsi que sur programmation et la programmation orientée objet.

Partie A

Partie B

Dans la suite de la documentation de la commande chmod, on peut lire l'extrait suivant.

l'indication de mode, qui peut être une représentation symbolique des changements à effectuer, ou un nombre octal représentant le motif binaire des nouveaux bits de comportement.

Cela signifie que l'on peut indiquer les permissions soit sous la forme d'une chaîne de 9 caractères composée de -, r, w et x, appelée représentation symbolique, soit sous la forme d'une valeur numérique abc composée de trois chiffres a, b et c compris entre 0 et 7 appelée représentation octale.

Le but de cette partie est d'écrire des fonctions Python permettant de passer d'une représentation symbolique à une représentation octale des permissions.

On considère une chaîne de trois caractères dont:

le premier caractère est '-' ou 'r';

le second caractère est '-' ou 'w';

et le dernier est '-' ou 'x'.

On souhaite déterminer le motif binaire de 3 bits correspondant à cette chaîne. Pour cela, on applique les règles suivantes:

si le premier caractère est 'r', le premier bit (celui le plus à gauche), vaut 1, sinon il vaut 0;

si le second caractère est 'w', le second bit vaut 1, sinon il vaut 0;

Si le dernier caractère est 'x', le dernier bit vaut 1, sinon il vaut 0.

5.

Déterminer l'écriture en base 10 du motif binaire de 3 bits correspondant à la chaîne 'rw-'.

La représentation octale associée à une représentation symbolique est obtenue en appliquant ce qui précède trois fois:

les trois caractères correspondant à user donnent le chiffre de gauche;

les trois suivants (correspondant à group) donnent le chiffre du milieu;

les trois derniers (correspondant à other) donnent le chiffre de droite.

Par exemple, la représentation octale associée à la représentation symbolique rwxr-xr-- est 754.

6.

Écrire le code d'une fonction Python bin_to_oct qui prend en paramètre une chaîne de 3 caractères composée uniquement des caractères '0' et '1' et renvoie la représentation octale correspondant à ce motif binaire (le bit de poids fort étant encore à gauche).

Exemples:

>>> bin_to_oct('101') → 5

>>> bin_to_oct('011') → 3

def bin_to_oct(chaine):

7.

On considère la fonction Python mystere qui prend en paramètre une chaîne de 9 caractères et qui renvoie une chaîne de caractères.

def mystere(chaine):

resultat = ''

for c in chaine:

if c == '-':

resultat = resultat + '0'

else:

resultat = resultat + '1'

return resultat

Déterminer ce que renvoie mystere('rw-r-xr--').

8.

On considère dans cette question qu'on dispose d'une fonction Python symb_to_bin qui prend en paramètre une chaîne de 9 caractères correspondant à l'écriture symbolique d'une permission et qui renvoie une chaîne de caractères correspondant au motif binaire de cette permission.

Recopier et compléter le code de la fonction Python symb_to_oct donné ci-dessous, qui prend en paramètre une représentation symbolique sous la forme d'une chaîne de caractères, et qui renvoie la représentation octale correspondante sous la forme d'une chaîne de caractères. Il est possible de répondre en une ou plusieurs lignes de code.

def symb_to_oct(chaine):

repr_bin = symb_to_bin(chaine)

# les 3 premiers bits correspondent au propriétaire

user = repr_bin[0] + repr_bin[1] + repr_bin[2]

# les 3 suivants au groupe

group = repr_bin[3] + repr_bin[4] + repr_bin[5]

# et les 3 derniers aux autres

other = repr_bin[6] + repr_bin[7] + repr_bin[8]

...

Exercice 2 - Partie C · Programmation orientée objet : classes, attributs, méthodes

Exercice 2 (6 points)

Cet exercice porte sur les systèmes d'exploitation ainsi que sur programmation et la programmation orientée objet.

Partie A

Partie C

Dans cette partie, on souhaite écrire un gestionnaire de fichiers Linux. Pour cela, on modélise les fichiers en utilisant la programmation orientée objet. On dispose de la classe Fichier ci-dessous.

class Fichier:

def __init__(self, nom, poids, proprio, groupe, permission):

self.nom = nom

self.poids = poids # taille en octets

self.proprietaire = proprio # nom du propriétaire

self.groupe = groupe # nom du groupe

self.permission = permission # représentation symbolique 9.

Instancier l'objet mon_fichier de la classe Fichier représentant un fichier nommé concat.pls, dont le propriétaire est root, le groupe est www-data, le poids 1257 octets et dont les permissions sont données par la représentation symbolique 'rw-r--r--'.

10.

Écrire une méthode chown de la classe Fichier qui prend en paramètre une chaîne de caractères qui correspond au nom du nouveau propriétaire et qui modifie le propriétaire du fichier.

11.

Écrire une fonction get_executable qui prend en paramètres une liste d'objets de la classe Fichier ainsi qu'un nom de propriétaire et qui renvoie la liste des fichiers qui appartiennent à ce propriétaire et qui sont exécutables par ce propriétaire.

Exercice 3 - Bases de données · Bases de données relationnelles, SGBD et langage SQL

Cet exercice porte principalement sur les bases de données, les graphes et la programmation de base en Python.

Un supermarché utilise une base de données qui contient des informations sur les produits, les fournisseurs, les commandes passées et leurs détails. Le modèle relationnel de cette base est donné par le schéma ci-dessous:

Dans ce schéma, les clés primaires sont soulignées et les clés étrangères sont précédées du symbole #. Le type de chaque attribut est indiqué entre parenthèses.

On considère l'extrait de la base de données ci-dessous:

Table Commandes

id_commande · date_commande · total_commande

1 · 03/06/2025 · 176.00

2 · 08/12/2024 · 1150.00

3 · 21/04/2025 · 155.00

L'énoncé de cet exercice utilise tout ou une partie des mots clefs du langage SQL suivants: SELECT, DISTINCT, FROM, WHERE, JOIN... ON, UPDATE... SET, DELETE, INSERT INTO... VALUES.

Avant de mettre en vente un nouveau produit, il faut le créer dans la base.

1.

Écrire une requête SQL permettant d'ajouter le produit croissant référencé dans la base sous le numéro 10. Il est vendu au prix unitaire de 0,90 €. Le magasin se fournit, pour ce produit, auprès de Boulangerie du Coin.

Le fournisseur Livres en Folie a changé d'entrepôt. Il se trouve maintenant au 78 Rue des Jeux à Elbeuf (France).

2.

Écrire la requête SQL permettant de mettre à jour la base de données.

3.

Décrire le résultat obtenu avec la requête SQL ci-dessous:

SELECT nom

FROM Produits

WHERE categorie = 'Alimentaire';

4.

Écrire une requête SQL permettant d'afficher les détails des commandes passées ayant un total de commande supérieur ou égal à 1000 €.

5.

Écrire une requête SQL permettant d'afficher le nom des fournisseurs basés en Espagne ou en Italie.

6.

Écrire une requête SQL qui permet d'afficher le nom de tous les fournisseurs qui ont vendu des produits alimentaires.

7.

Écrire une requête SQL permettant d'afficher le numéro et la date des commandes ainsi que le nom des fournisseurs où des produits de catégories Vêtement ont été commandés.

Exercice 3 - Graphes · Graphes : modélisation, implémentations et parcours

Cet exercice porte principalement sur les bases de données, les graphes et la programmation de base en Python.

Un supermarché utilise une base de données qui contient des informations sur les produits, les fournisseurs, les commandes passées et leurs détails. Le modèle relationnel de cette base est donné par le schéma ci-dessous:

Dans ce schéma, les clés primaires sont soulignées et les clés étrangères sont précédées du symbole #. Le type de chaque attribut est indiqué entre parenthèses.

On considère l'extrait de la base de données ci-dessous:

Table Commandes

id_commande · date_commande · total_commande

1 · 03/06/2025 · 176.00

2 · 08/12/2024 · 1150.00

3 · 21/04/2025 · 155.00

8.

Déterminer le plus court chemin (en termes de distance) entre Toulouse et Calais.

9.

Écrire la liste des sommets dans l'ordre d'un parcours en profondeur à partir de Calais (les sommets sont toujours pris dans l'ordre alphabétique s'il y a un choix à faire).

10.

Écrire la liste des sommets dans l'ordre d'un parcours en largeur à partir de Calais (les sommets sont toujours pris dans l'ordre alphabétique s'il y a un choix à faire).

Pour implémenter ce graphe, on utilise le dictionnaire en Python ci-dessous:

graphe = {'Paris': {'Strasbourg': 490, 'Lyon': 465,

'Nantes': 385, 'Calais': 300},

'Strasbourg': {'Paris': 490, 'Lyon': 470,

'Calais': 600},

'Lyon': {'Paris': 465, 'Strasbourg': 470,

'Toulouse': 405, 'Nantes': 600},

'Nantes': {'Paris': 385, 'Lyon': 600,

'Bordeaux': 340, 'Calais': 550},

'Calais': {'Paris': 300, 'Strasbourg': 600,

'Nantes': 550},

'Toulouse': {'Lyon': 405, 'Bordeaux': 250,

'Marseille': 400},

'Marseille': {'Toulouse': 400},

'Bordeaux': {'Nantes': 340, 'Toulouse': 250}

}

Dans cette implémentation, il manque la route entre Lyon et Marseille.

11.

Écrire les instructions permettant d'ajouter dans le dictionnaire graphe la route entre les villes de Lyon et Marseille sachant que la distance les séparant est de 315 km.

12.

Écrire une fonction distance qui prend en paramètres le dictionnaire graphe et deux villes (de type str) et qui renvoie la distance entre ces deux villes si elles sont adjacentes, ou None sinon.

def distance(graphe, ville1, ville2):

Pour déterminer le chemin entre deux villes quelconques (s'il en existe un) dans le dictionnaire graphe, on utilise la fonction trouver_chemin(graphe, ville_depart, ville_destination) qui renvoie la liste des villes parcourues. On suppose que cette fonction est codée en Python.

13.

Écrire une fonction distance_totale qui prend en paramètre le dictionnaire graphe et deux villes (de type str) et qui renvoie la distance entre ces deux villes s'il existe un chemin entre elles sinon elle retourne None.

def distance_totale(graphe, ville_depart, ville_destination):

On décide désormais de prendre en compte le temps nécessaire pour parcourir les distances entre les villes.

Ainsi les arêtes du graphe sont pondérées à l'aide d'une liste contenant la distance (en km arrondie à l'unité) et la durée (en heure arrondie à deux décimales) nécessaire pour effectuer le trajet entre les deux villes.

Exemple: >>> graphe['Paris']

{'Strasbourg': [490, 6.37], 'Lyon': [465, 5.58], 'Nantes': [385, 3.47], 'Calais': [300, 3.3]}

14.

À partir de l'exemple précédent, déterminer la valeur de graphe['Paris']['Nantes'][1].

La fonction ratio_duree_distance ci-dessous prend en paramètre le dictionnaire graphe. Elle permet de calculer le ratio durée/distance pour toutes les arêtes du graphe et de l'ajouter à la pondération de chaque arête:

1 def ratio_duree_distance(graphe):

2 for ville, connexions in...:

3 for destination, valeurs in...:

4 distance, duree =...

5 ratio =...

6 graphe[ville][destination].append(...)

7 return graphe

15.

Recopier et compléter la fonction ratio_duree_distance.

def ratio_duree_distance(graphe):

Un élève souhaite utiliser ChatGPT pour trouver un algorithme qui détermine le chemin à privilégier entre deux villes. Il fournit son script Python contenant le dictionnaire graphe (et son descriptif) et les fonctions précédentes. Il écrit le prompt suivant:

« Écris un algorithme, en langage naturel, pour trouver un chemin entre deux villes en minimisant le ratio durée/distance où à chaque étape, on choisira l'arête avec le ratio le plus faible. »

16.

Déterminer le nom que l'on donne à un algorithme qui construit une solution étape par étape, comme celui demandé par l'élève.

17.

Déterminer le chemin trouvé grâce à cet algorithme entre Toulouse et Calais.

Tu peux aussi réviser par thème

Entraîne-toi sur un thème avec les exercices des sujets de toutes les années.

Voir les 18 thèmes

Autres sujets de 2025

Métropole France 1 (2025) · Métropole France 1 remplacement (2025) · Métropole France 2 (2025)