Épreuve écrite · 2025 · Remplacement jour1 - 9 septembre 2025

Bac NSI 2025 Métropole remplacement jour 1

Le sujet de remplacement du 9 septembre 2025 propose une comparaison précise des routages RIP et OSPF, puis une véritable optimisation algorithmique : trouver le plus grand carré constructible sans réexaminer sans cesse les mêmes cases. Enfin, SQL et recherche textuelle servent une étude de papillons. Le corrigé montre ce que chaque donnée permet de déduire et les coûts cachés derrière les boucles.

Les 36 questions appartiennent à trois exercices de 6,6 et 8 points. Durée :3 h30 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

Choisir une interface de routage à partir d’une IPv4

Une IPv4 comporte quatre octets,32 bits, notés a.b.c.d. En CIDR /n, les n premiers bits identifient le réseau ; les autres identifient l’hôte. Les bits hôtes tous nuls donnent l’adresse réseau, tous à 1 la diffusion. Le masque met les n bits réseau à 1 et les autres à 0.

Une machine communique directement avec une destination de son réseau ; sinon elle transmet à sa passerelle. Dans 192.168.1.0/24, la passerelle est R1, interface G0, adresse 192.168.1.254.

50.50.50.8/3050.50.50.4/3050.50.50.0/3050.50.50.16/3050.50.50.12/3050.50.50.20/3050.50.50.24/30R1R2R3R4R5R6
Figure 1 : réseau et interfaces, chaque liaison détaillée dans le tableau
Lire les connexions du schéma
  • R1 relié à R2 : 50.50.50.8/30
  • R1 relié à R4 : 50.50.50.4/30
  • R1 relié à R5 : 50.50.50.0/30
  • R2 relié à R3 : 50.50.50.16/30
  • R2 relié à R4 : 50.50.50.12/30
  • R4 relié à R5 : 50.50.50.20/30
  • R4 relié à R6 : 50.50.50.24/30
LiaisonRéseauInterface du premier routeurInterface du second
R1-R250.50.50.8/30G3G3
R1-R450.50.50.4/30G2G0
R1-R550.50.50.0/30G1G2
R2-R350.50.50.16/30G1G1
R2-R450.50.50.12/30G2G1
R4-R550.50.50.20/30G3G1
R4-R650.50.50.24/30G2G1
Réseau localPasserelle, interface
192.168.1.0/24R1,G0
192.168.2.0/24R2,G0
192.168.3.0/24R3,G0
10.0.0.0/8R5,G0
172.16.0.0/16R6,G0

Question 1

#

Donner le routeur et l’interface constituant la passerelle de 192.168.2.0/24.

Comprendre la correction

Le routeur R2, interface G0. C’est son interface directement reliée au réseau local, pas une interface vers un autre routeur.

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

Question 2

#

La passerelle doit recevoir la dernière adresse hôte disponible. Quelle adresse lui attribuer dans 172.16.0.0/16 ?

Comprendre la correction

172.16.255.254. La dernière adresse du bloc,172.16.255.255, est réservée à la diffusion. La précédente est donc le dernier hôte utilisable, conformément à la politique donnée.

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

Question 3

#

Lister les quatre adresses de 50.50.50.4/30 et attribuer celle de l’interface G0 de R4, sachant que R1 G2 utilise 50.50.50.5.

Comprendre la correction
AdresseRôle
50.50.50.4Réseau
50.50.50.5Hôte : R1,G2
50.50.50.6Hôte : R4,G0
50.50.50.7Diffusion

Un /30 laisse 2 bits hôtes, soit 4 adresses. Les deux adresses utilisables sont.5 et.6 ; la première est déjà prise.

Voir la question dans le sujet PDF, p. 3 (nouvel onglet)
Réseau destinationInterface donnéeMétrique donnée
192.168.1.0/24G00
192.168.2.0/24……
192.168.3.0/24……
172.16.0.0/16G22
10.0.0.0/8G11
50.50.50.0/30G10
50.50.50.4/30G20
50.50.50.8/30G30
50.50.50.12/30G21
50.50.50.16/30G31
50.50.50.20/30……
50.50.50.24/30……

Question 4

#

Compléter la table RIP de R1. En cas d’égalité, choisir le numéro d’interface le plus faible.

Indice

Pour un réseau de liaison, atteindre l’une des extrémités suffit.

Comprendre la correction
Réseau destinationInterfaceMétrique
192.168.1.0/24G00
192.168.2.0/24G31
192.168.3.0/24G32
172.16.0.0/16G22
10.0.0.0/8G11
50.50.50.0/30G10
50.50.50.4/30G20
50.50.50.8/30G30
50.50.50.12/30G21
50.50.50.16/30G31
50.50.50.20/30G11
50.50.50.24/30G21

Les lignes manquantes sont 192.168.2.0/24 : G3,1 ;192.168.3.0/24 : G3,2 ;50.50.50.20/30 : G1,1 ;50.50.50.24/30 : G2,1. Pour.20/30, on peut atteindre un routeur attaché au réseau par R5 viaG 1 ou R4 viaG 2. Les métriques étant égales, G1 est retenue.

Un réseau directement raccordé coûte 0 ; atteindre un réseau derrière un autre routeur ajoute un saut. Il n’est pas nécessaire de traverser les deux extrémités d’un réseau de liaison pour l’atteindre.

Voir la question dans le sujet PDF, p. 3, 4 (nouvel onglet)
t_routage = [((192,168,1,0,24), 'G0', 0),
             ...,
             ...,
             ((172,16,0,0,16), 'G2', 2),
             ((10,0,0,0,8), 'G1', 1),
             ((50,50,50,0,30), 'G1', 0),
             ((50,50,50,4,30), 'G2', 0),
             ((50,50,50,8,30), 'G3', 0),
             ((50,50,50,12,30), 'G2', 1),
             ((50,50,50,16,30), 'G3', 1),
             ...,
             ...]

Question 5

#

Compléter les lignes 2,3,11 et 12 du tableau Python t_routage, qui représente cette table par triplets.

Comprendre la correction
# Ligne2
((192, 168, 2, 0, 24), 'G3', 1),
# Ligne3
((192, 168, 3, 0, 24), 'G3', 2),
# Ligne11
((50, 50, 50, 20, 30), 'G1', 1),
# Ligne12
((50, 50, 50, 24, 30), 'G2', 1)

Chaque entrée contient un quintuplet réseau, la chaîne d’interface et la métrique entière. Le masque /n est le cinquième élément du quintuplet, pas un sixième champ du triplet.

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

Le réseau est obtenu par ET bit à bit. et_bit_a_bit(10,252) renvoie 8. La fonction mask_for_size(n) renvoie le masque comme quadruplet d’entiers. Exemples : /24 →(255,255,255,0), /26 →(255,255,255,192).

192.168.1.10 : 11000000.10101000.00000001.00001010
Masque /24   : 11111111.11111111.11111111.00000000
Résultat ET  : 11000000.10101000.00000001.00000000

Question 7

#

Déterminer le réseau de 50.50.50.6/30 en écrivant le ET bit à bit. Convertir 50 et 6 en binaire.

Comprendre la correction
Adresse : 00110010.00110010.00110010.00000110
Masque  : 11111111.11111111.11111111.11111100
ET      : 00110010.00110010.00110010.00000100

50 vaut 32+16+2, et 6 vaut 4+2. Le résultat est 50.50.50.4/30 : seuls les deux derniers bits sont effacés.

Voir la question dans le sujet PDF, p. 5 (nouvel onglet)
def is_in_network(address, network):
    network_mask = mask_for_size(...)
    for i in range(4):
        if et_bit_a_bit(network_mask[i], address[i]) != ...:
            return ...
    return ...

Exemples : (192,168,1,1) appartient à(192,168,1,0,24), mais pas à(192,168,2,0,24).

Question 8

#

Compléter is_in_network(address, network), où address est un quadruplet et network un quintuplet incluant le préfixe.

Indice

Le cinquième élément du tuple réseau contient le préfixe CIDR.

Comprendre la correction
def is_in_network(address, network):
    network_mask = mask_for_size(network[4])
    for i in range(4):
        if et_bit_a_bit(network_mask[i], address[i]) != network[i]:
            return False
    return True

Le masque provient du cinquième élément, d’indice 4. On compare chacun des octets masqués au réseau canonique donné. Une seule différence suffit pour retourner faux ; vrai vient seulement après les quatre comparaisons. Les tuples network du sujet sont déjà de véritables adresses réseau.

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

Question 9

#

Écrire choose_interface(t_routage, destination_ip), qui renvoie l’interface correspondant au réseau de destination, ouNone si aucun réseau ne correspond.

Comprendre la correction
def choose_interface(t_routage, destination_ip):
    for network, interface, metrique in t_routage:
        if is_in_network(destination_ip, network):
            return interface
    return None

On décompose chaque triplet et on réutilise le test d’appartenance. La métrique a déjà servi à construire la table ; il n’est pas nécessaire de refaire ici un calcul de chemin. Dans les réseaux non chevauchants du sujet, le premier match est le seul.

Avec une table générale comportant des préfixes imbriqués, le routage réel choisirait le plus long préfixe correspondant. Ce raffinement dépasse les données proposées et ne doit pas être confondu avec le simple parcours demandé.

Voir la question dans le sujet PDF, p. 6 (nouvel onglet)
LiaisonTypeDébit
R1-R2Gigabit Ethernet1 Gb/s
R1-R4Fast Ethernet100 Mb/s
R1-R5Gigabit Ethernet1 Gb/s
R2-R3Gigabit Ethernet1 Gb/s
R2-R4Fibre10 Gb/s
R4-R5Gigabit Ethernet1 Gb/s
R4-R6Fibre10 Gb/s

Question 10

#

Calculer les coûts des trois types de liaisons avec coût = 10¹⁰ / débit, débit en bits/s.

Comprendre la correction
TypeDébitCoût
Gigabit Ethernet1 Gbit/s =10⁹bit/s10
Fast Ethernet100 Mbit/s =10⁸bit/s100
Fibre10 Gbit/s =10¹⁰bit/s1

On convertit d’abord tous les débits dans la même unité. Un débit plus grand donne un coût plus faible ; ces coûts s’additionnent le long d’une route.

Voir la question dans le sujet PDF, p. 6 (nouvel onglet)
RéseauInterface donnéeMétrique donnée
192.168.1.0/24G00
192.168.2.0/24……
192.168.3.0/24G320
172.16.0.0/16……
10.0.0.0/8……
50.50.50.0/30G10
50.50.50.4/30G20
50.50.50.8/30G30
50.50.50.12/30G310
50.50.50.16/30G310
50.50.50.20/30……
50.50.50.24/30G311

Question 11

#

Compléter les lignes manquantes de la table OSPF de R1. Les réseaux directement connectés ont métrique 0 ; les autres prennent le coût minimal pour les atteindre.

Comprendre la correction
RéseauInterfaceMétrique
192.168.1.0/24G00
192.168.2.0/24G310
192.168.3.0/24G320
172.16.0.0/16G312
10.0.0.0/8G110
50.50.50.0/30G10
50.50.50.4/30G20
50.50.50.8/30G30
50.50.50.12/30G310
50.50.50.16/30G310
50.50.50.20/30G110
50.50.50.24/30G311

Les lignes demandées sont :192.168.2.0/24 →G3,10 ;172.16.0.0/16 →G3,12 ;10.0.0.0/8 →G1,10 ;50.50.50.20/30 →G1,10. Pour rejoindre R6, la route R1-R2-R4-R6 vaut 10+1+1=12, bien moins que R1-R4-R6 à 101.

Le réseau.20/30 est accessible dès R5, atteint par G1à coût 10. Il ne faut pas ajouter le coût pour traverser ensuite ce lien vers R4.

Voir la question dans le sujet PDF, p. 7 (nouvel onglet)
Une adresse, un réseau, une interfaceUn atelier pour expérimenter

Choisissez une destination et comparez la sortie de R1 sous RIP ou OSPF. Le réseau de destination est recherché par le masque ; l’interface dépend de la table construite.

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

172.16.0.10 : sortie G3

Le test de masque trouve le réseau. Le protocole a déterminé en amont quelle interface utiliser et avec quelle métrique.

RéseauRIP : interface / métriqueOSPF : interface / métrique
172.16.0.0/16G2 / 2G3 / 12

L’appartenance à un réseau et le choix de la meilleure route sont deux étapes distinctes : le masque traite la première, la table de routage mémorise la seconde.

Revoir les notions de cet exercice

Exercice 2 · 6 points

Construire le plus grand carré par programmation dynamique

Une carte de stratégie est un carré n×n, n strictement positif, contenant au moins une case constructible. Une liste de listes code 1 pour une case constructible,0 sinon. Une base est un carré ne contenant que des 1 ; on cherche une base de côté maximal.

carte_A

Ligne / colonne01234
000111
111011
201110
301110
401110

La plus grande base de carte_A a un côté de 3 cases et commence en ligne 2, colonne 1 : sa cellule de départ est carte_A[2][1].

Partie A : essayer tous les carrés

carte_B

Ligne / colonne0123
01111
10111
20111
31011

Question 1

#

Donner la taille de la plus grande base de carte_B et les coordonnées de son coin supérieur gauche.

Comprendre la correction

La taille est 3, à partir de (0,1). Les lignes 0 à 2 et colonnes 1 à 3 forment neuf 1. Un carré de côté 4 reprendrait toute la carte et contiendrait des 0 ; aucun autre carré de côté 3 ne convient.

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

Question 2

#

Pour un carré constructible de côté taille, donner la somme des valeurs de toutes ses cellules.

Comprendre la correction

taille², soit taille * taille. Le carré possède exactement ce nombre de cellules et chacune vaut 1. Comme la carte ne contient que 0 et 1, atteindre cette somme est aussi suffisant pour prouver que toutes les cellules valent 1.

Voir la question dans le sujet PDF, p. 9 (nouvel onglet)
def est_constructible(carte, i_coin, j_coin, taille):
    s = 0
    for i in range(i_coin, i_coin + taille):
        for j in range(..., ...):
            s = ...
    ...

Question 3

#

Compléter est_constructible(carte, i_coin, j_coin, taille). Les paramètres garantissent que le carré reste dans la carte.

Comprendre la correction
def est_constructible(carte, i_coin, j_coin, taille):
    s = 0
    for i in range(i_coin, i_coin + taille):
        for j in range(j_coin, j_coin + taille):
            s = s + carte[i][j]
    return s == taille * taille

La boucle extérieure parcourt les lignes du carré, l’intérieure ses colonnes. Les bornes droites sont exclues et donnent exactement taille positions chacune. La comparaison finale avec l’aire transforme la somme en booléen.

La fonction n’a pas à vérifier les limites de carte : cette condition est une précondition fournie. Elle examine toujours taille² cellules dans cette version, même si un 0 apparaît tôt.

Voir la question dans le sujet PDF, p. 9 (nouvel onglet)
def plus_grande_base_exhaustive(carte):
    n = len(carte)
    for taille in range(n, 0, -1):
        i = 0
        while i + taille <= n:
            j = ...
            while ...:
                if est_constructible(...):
                    return (taille, i, j)
                j = ...
            i = i + 1

Question 4

#

Compléter la recherche exhaustive qui teste les tailles de n à 1 et renvoie (taille,i,j) dès qu’un carré constructible est trouvé.

Indice

Chaque taille doit tester tous les coins qui gardent le carré dans la carte.

Comprendre la correction
def plus_grande_base_exhaustive(carte):
    n = len(carte)
    for taille in range(n, 0, -1):
        i = 0
        while i + taille <= n:
            j = 0
            while j + taille <= n:
                if est_constructible(carte, i, j, taille):
                    return (taille, i, j)
                j = j + 1
            i = i + 1

Pour chaque taille, on réinitialise i et, pour chaque ligne, j. Les tests i + taille <= n et j + taille <= n garantissent que le carré entier reste inclus. Tester les tailles décroissantes justifie le retour immédiat : aucun carré plus grand n’a convenu.

Le sujet garantit au moins un 1, donc on trouvera au plus tard un carré de taille 1. Sans cette hypothèse, on devrait définir un résultat pour une carte entièrement non constructible.

Voir la question dans le sujet PDF, p. 9, 10 (nouvel onglet)
0 millions2 millions4 millions6 millions8 millions050100150200250300Largeur de la carte
Figure1reconstruite : nombre de carrés à examiner dans le pire cas selon la largeur.

Question 5

#

Pourquoi l’approche exhaustive est-elle inapplicable aux grandes cartes ?

Comprendre la correction

Il y a (n - t + 1)² positions pour un carré de taille t. Le nombre total de carrés atteint 1² + 2² + ... + n² = n(n+1)(2n+1)/6, donc croît comme n³. À 300 cases de côté, cela représente déjà 9 045 050 carrés possibles.

Et chaque test de côté t additionne t² cellules : le coût de cette version exhaustive peut croître comme n⁵. La figure compte les carrés, pas toutes les lectures de cellules. Augmenter simplement la vitesse de la machine ne supprime pas ces répétitions.

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

Partie B : mémoriser les sous-problèmes

On construit une table auxiliaire de même taille : aux[i][j] est le côté maximal d’une base issue de(i, j). Pour carte_A :

aux_A

Ligne / colonne01234
000121
111011
203210
302210
401110

La valeur 3 en(2,1) repère sa meilleure base. Il suffit ensuite de chercher le maximum de aux.

Question 6

#

Déterminer aux_B, où chaque cellule contient le côté du plus grand carré constructible qui commence à cet endroit.

Comprendre la correction

aux_B

Ligne / colonne0123
01321
10221
20121
31011

Le 3 en(0,1) reprend la base maximale. Le 2 en(2,2) représente le carré du coin inférieur droit. Une cellule à 0 dans la carte donne 0 dans aux ; une case du bord ne peut commencer qu’un carré de taille 0 ou 1.

Voir la question dans le sujet PDF, p. 11 (nouvel onglet)
carte_C = [
    [..., ..., ..., 0, ..., ...],
    [1, ..., ..., ..., ..., ...],
    [..., ..., ..., ..., ..., ...],
    [..., ..., ..., 1, ..., ...],
    [1, ..., ..., ..., ..., ...],
    [..., ..., ..., ..., ..., ...],
]
aux_C = [
    [..., ..., ..., a, 2, ...],
    [b, 0, ..., 1, 1, ...],
    [3, 2, ..., ..., ..., ...],
    [..., ..., ..., c, 2, ...],
    [d, 2, ..., 2, 2, ...],
    [1, 1, ..., ..., ..., ...],
]

Question 7

#

Déterminer a, b, c, d dans les extraits de carte_C et aux_C.

Comprendre la correction

a=0, b=1, c=3, d=2. La case source de a vaut 0. Pour b, la case source vaut 1 mais sa voisine de droite a une valeur auxiliaire 0, donc aucun carré de côté 2 ne peut partir de b. Pour c, les trois voisines utiles valent 2 : 1 + min(2,2,2)=3. Pour d, elles valent 1,2,1 : 1 + min(1,2,1)=2.

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

Récurrence admise : si carte[i][j]=0, alors aux[i][j]=0 ; sinon aux[i][j] = 1 + min(aux[i+1][j], aux[i][j+1], aux[i+1][j+1]).

def calcule_aux(carte):
    n = len(carte)
    aux = [[0 for j in range(n)] for i in range(n)]
    for k in range(n):
        aux[n - 1][k] = ...
        aux[...][...] = ...
    for i in range(n - 2, -1, -1):
        for j in range(...):
            if ...:
                aux[i][j] = 1 + min(aux[i+1][j], aux[i][j+1], aux[i+1][j+1])
    return aux

Question 8

#

Compléter les lignes 8,9,14 et 15 de calcule_aux.

PythonCalculer tous les carrés constructibles en une seule passeÉcrivez votre solution et mettez-la à l’épreuve

Écrivez calcule_aux(carte) pour une carte carrée non vide de 0 et de 1. aux[i][j] doit contenir la taille du plus grand carré entièrement constitué de 1 dont (i,j) est le coin supérieur gauche. Renvoyez toute la matrice aux, sans modifier carte. Respectez l’idée dynamique : les cases à droite, en dessous et en diagonale doivent être connues avant la case courante.

def calcule_aux(carte):
    # À vous de jouer
    pass

Les cas de test proposés :

  • Une seule case : La bordure constitue déjà toute la matrice quand n vaut 1.
  • Un terrain entièrement libre : La taille dépend du coin choisi : elle n’est pas égale au maximum global partout.
  • Un obstacle au centre : Le zéro interdit tous les carrés 2×2 de cette carte, même si les bordures sont libres.
  • Une diagonale qui limite : Consulter seulement le voisin droit et le voisin du dessous accepterait un carré qui contient un obstacle.
  • Une carte asymétrique, des lignes indépendantes : L’obstacle n’est pas symétrique par rapport à tous les coins. Des lignes partagées dans aux corrompraient les cases.
Indice

La case dépend du bas, de la droite et du bas-droite.

Comprendre la correction
def calcule_aux(carte):
    n = len(carte)
    aux = [[0 for j in range(n)] for i in range(n)]
    for k in range(n):
        aux[n - 1][k] = carte[n - 1][k]
        aux[k][n - 1] = carte[k][n - 1]
    for i in range(n - 2, -1, -1):
        for j in range(n - 2, -1, -1):
            if carte[i][j] == 1:
                aux[i][j] = 1 + min(aux[i + 1][j], aux[i][j + 1], aux[i + 1][j + 1])
    return aux

La dernière ligne et la dernière colonne sont des cas de base copiés de carte. On remplit ensuite depuis le bas et la droite, car les trois valeurs utilisées doivent déjà être disponibles.

Le minimum est le facteur limitant : pour agrandir un carré d’un cran, il faut que les zones à droite, dessous et en diagonale disposent toutes de cette taille. Si la cellule source vaut 0, aux reste à sa valeur initiale 0. La compréhension imbriquée crée des lignes indépendantes ; [[0]*n]*n partagerait une seule ligne et corromprait le calcul.

Voir la question dans le sujet PDF, p. 12 (nouvel onglet)
def plus_grande_base(carte):
    n = len(carte)
    aux = calcule_aux(carte)
    taille_max = aux[0][0]
    i_max = 0
    j_max = 0
    ...

Question 9

#

Compléter plus_grande_base à partir de la ligne 7 pour renvoyer la taille maximale et les coordonnées correspondantes.

Comprendre la correction
def plus_grande_base(carte):
    n = len(carte)
    aux = calcule_aux(carte)
    taille_max = aux[0][0]
    i_max = 0
    j_max = 0
    for i in range(n):
        for j in range(n):
            if aux[i][j] > taille_max:
                taille_max = aux[i][j]
                i_max = i
                j_max = j
    return (taille_max, i_max, j_max)

On mémorise ensemble la valeur et sa position. Mettre à jour seulement la taille perdrait les coordonnées. Avec un test strict >, le premier maximum dans l’ordre de parcours est conservé ; le sujet accepte n’importe quelle base de taille maximale.

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

Question 10

#

Une carte 1000×1000 est traitée en 0,4 s. Estimer le temps pour 3000×3000 parmi 0,4 s,1,2 s,3,6 s. Justifier.

Comprendre la correction

3,6 secondes. Construire aux puis chercher son maximum demande un travail proportionnel au nombre de cellules, soit n². Tripler le côté multiplie le nombre de cellules par 9 : 0,4 × 9 = 3,6.

C’est une estimation à machine et conditions comparables. Elle s’appuie sur la complexité quadratique, pas sur une mesure réellement effectuée pour la carte 3000.

Voir la question dans le sujet PDF, p. 13 (nouvel onglet)
Chaque case connaît son plus grand carréUn atelier pour expérimenter

Choisissez carte_A ou carte_B puis une cellule. Comparez sa valeur auxiliaire à celles de ses trois voisines et visualisez le carré qu’elle peut réellement commencer.

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

Depuis(2,1), carré de côté3

1 + min(2, 2, 2) = 3. Le plus petit voisin limite la croissance.

Ligne01234
00 (aux=0)0 (aux=0)1 (aux=1)1 (aux=2)1 (aux=1)
11 (aux=1)1 (aux=1)0 (aux=0)1 (aux=1)1 (aux=1)
20 (aux=0)■ 1 (aux=3)■ 1 (aux=2)■ 1 (aux=1)0 (aux=0)
30 (aux=0)■ 1 (aux=2)■ 1 (aux=2)■ 1 (aux=1)0 (aux=0)
40 (aux=0)■ 1 (aux=1)■ 1 (aux=1)■ 1 (aux=1)0 (aux=0)

La table mémorise un sous-problème par cellule. Son ordre de remplissage garantit que les trois dépendances sont déjà calculées, supprimant les vérifications répétées de grands carrés.

Revoir les notions de cet exercice

Exercice 3 · 8 points

Papillons : SQL, tri par taille et recherche d’une séquence ADN

Une étude s’intéresse à la présence de papillons, aux plantes de leur environnement et à la classification de nouvelles espèces. La base faune_flore.db contient trois relations.

Partie A : croiser les observations

papillon contient num, nomCo (nom commun), nomSc (nom scientifique), taille moyenne enmm, habitat principal et zone. plante contient num, nomCo, nomSc, habitat et zone. Dans ces deux tables, zone référence zone_geographique.num. Les valeurs et graphies des extraits sont conservées comme données du sujet.

papillon.numnomConomSctaillehabitatzone
458MonarqueDanaus plexippu100Prairies3
459Citron de ProvenceGonepteryx cleopatra30Prairies1
460Paon-du-jourAglais io6Jardins6
461MachaonPapilio machaon85Forêts2
462Petite TortueAglais urticae30Prairies5
463Robert-le-DiablePolygonia c-album25Forêts4
plante.numnomConomSchabitatzone
128Orchidée PhalaenopsisPhalaenopsisForêts5
129BambouBambusoideaeForêts3
130RoseRosaHaies2
131LilasSyringaHaies6
132CoquelicotPapaver rhoeasJardins4
133LavandeLavandulaCollines1
zone_geographique.numzone
1Afrique du Nord
2Amérique du Nord
3Amérique du Sud
4Asie
5Asie du Sud
6Europe

Clauses proposées : SELECT, FROM, WHERE, AND, OR, JOIN...ON, UPDATE, INSERT.

Question 1

#

Donner la définition d’une clé primaire.

Comprendre la correction

Une clé primaire est un attribut ou ensemble d’attributs choisi pour identifier chaque enregistrement de façon unique. Ses valeurs sont uniques et non nulles. Elle permet de référencer sans ambiguïté une ligne depuis d’autres tables.

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

Question 2

#

Pourquoi habitat ne peut-il pas être clé primaire de papillon ?

Comprendre la correction

Plusieurs papillons partagent un habitat : Prairies apparaît pour Monarque, Citron de Provence et Petite Tortue. La valeur ne distingue donc pas chaque ligne de la table. Les doublons visibles suffisent à réfuter l’unicité.

Voir la question dans le sujet PDF, p. 16 (nouvel onglet)
SELECT taille FROM papillon WHERE nomCo = 'Machaon';

Question 4

#

Mettre à jour la taille moyenne de Petite Tortue, désormais 50 mm.

Comprendre la correction
UPDATE papillon SET taille = 50 WHERE nomCo = 'Petite Tortue';

La modification porte sur taille dans la ligne de l’espèce concernée. Sans WHERE, toutes les tailles seraient remplacées. L’extrait Python de la partieBintègre déjà cette nouvelle valeur 50.

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

Question 5

#

Afficher les noms communs des papillons des prairies dont la taille est strictement inférieure à 55 mm.

Comprendre la correction
SELECT nomCo FROM papillon
WHERE habitat = 'Prairies' AND taille < 55;

La taille 55 est exclue. Dans l’extrait, Citron de Provence et Petite Tortue conviennent, que l’on considère la taille initiale 30 de cette dernière ou sa valeur mise à jour 50.

Voir la question dans le sujet PDF, p. 16 (nouvel onglet)
SELECT nomSc FROM plante
JOIN zone_geographique ON plante.zone = zone_geographique.num
WHERE zone_geographique.zone = 'Asie';

Question 6

#

Donner le résultat de la requête sur les plantes de la zone Asie.

Comprendre la correction
nomSc
Papaver rhoeas

Asie a l’identifiant 4. La plante de cette zone est Coquelicot, dont le nom scientifique est Papaver rhoeas. Le résultat demande nomSc, pas nomCo.

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

Question 7

#

Afficher le nom commun des papillons et des plantes partageant le même habitat, pour les papillons de taille strictement inférieure à 55 mm.

SQLAssocier les espèces partageant un habitatÉcrivez votre solution et mettez-la à l’épreuve

Renvoyez le nom commun du papillon et celui de la plante lorsqu’ils partagent le même habitat, en ne conservant que les papillons de taille strictement inférieure à 55 mm. La zone géographique n’est pas le critère de jointure de cette question.

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

Les cas de test proposés :

  • Extraits officiels de faune et flore : Toutes les lignes reprennent les tableaux du sujet. Une même espèce de papillon peut produire plusieurs couples lorsque plusieurs plantes ont le même habitat.
  • Cas complémentaire : habitat commun, zones différentes : Jeu complémentaire pédagogique, distinct des données officielles. La zone diffère pour le couple attendu. La taille 55 doit être exclue ; partager seulement une zone ne suffit pas.
Indice

Le critère commun demandé est habitat, non zone.

Comprendre la correction
SELECT papillon.nomCo, plante.nomCo
FROM papillon JOIN plante ON papillon.habitat = plante.habitat
WHERE papillon.taille < 55;

On joint ici sur l’habitat demandé, et non sur la zone. Plusieurs plantes peuvent partager cet habitat : une même espèce de papillon peut donc apparaître dans plusieurs couples. Ce produit de correspondances est voulu par la question.

Voir la question dans le sujet PDF, p. 16 (nouvel onglet)
SELECT papillon.nomCo, plante.nomCo
FROM papillon
JOIN zone_geographique ON papillon.zone = zone_geographique.num
JOIN plante ON plante.zone = zone_geographique.num
WHERE zone_geographique.zone = 'Europe';

Question 8

#

Donner le résultat de la requête associant les papillons et plantes d’Europe.

Comprendre la correction
papillon.nomCoplante.nomCo
Paon-du-jourLilas

Europe correspond à la zone 6. Paon-du-jour et Lilas sont les seules lignes des extraits qui y appartiennent. Leur habitat diffère, mais la requête filtre la zone et ne demande pas un habitat commun.

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

Question 9

#

Afficher les noms communs des papillons situés dans la même zone que les coquelicots.

Comprendre la correction
SELECT papillon.nomCo
FROM papillon JOIN plante ON papillon.zone = plante.zone
WHERE plante.nomCo = 'Coquelicot';

La table intermédiaire des zones n’est pas nécessaire : les deux clés étrangères peuvent être comparées directement. Le nom de plante sert à sélectionner sa zone sans supposer que son identifiant restera 4. Dans l’extrait, Robert-le-Diable est renvoyé.

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

Partie B : classification et recherche textuelle

La collection est une liste de dictionnaires limitée à num, nomCo, nomSc, taille :

papillon = [
  {
    "num": 458,
    "nomCo": "Monarque",
    "nomSc": "Danaus plexippu",
    "taille": 100
  },
  {
    "num": 459,
    "nomCo": "Citron de Provence",
    "nomSc": "Gonepteryx cleopatra",
    "taille": 30
  },
  {
    "num": 460,
    "nomCo": "Paon-du-jour",
    "nomSc": "Aglais io",
    "taille": 6
  },
  {
    "num": 461,
    "nomCo": "Machaon",
    "nomSc": "Papilio machaon",
    "taille": 85
  },
  {
    "num": 462,
    "nomCo": "Petite Tortue",
    "nomSc": "Aglais urticae",
    "taille": 50
  },
  {
    "num": 463,
    "nomCo": "Robert-le-Diable",
    "nomSc": "Polygonia c-album",
    "taille": 25
  }
]
def tri_collec(collec):
    for i in range(1, len(collec)):
        pap = collec[i]
        j = ...
        while j > 0 and collec[j - 1]['taille'] > ...:
            collec[j] = collec[j - 1]
            j = ...
        collec[j] = pap
    return ...

Question 10

#

Compléter les lignes 12,13,15 et 17 du tri de la collection de papillons par taille croissante.

Comprendre la correction
def tri_collec(collec):
    for i in range(1, len(collec)):
        pap = collec[i]
        j = i
        while j > 0 and collec[j - 1]['taille'] > pap['taille']:
            collec[j] = collec[j - 1]
            j = j - 1
        collec[j] = pap
    return collec

On sauvegarde le papillon courant dans pap. Les éléments précédents plus grands sont décalés d’une case vers la droite jusqu’à libérer la position j. On y insère alors pap. Il faut comparer les champs taille des dictionnaires, pas les dictionnaires eux-mêmes.

Avec les six valeurs de la partieB, l’ordre devient Paon-du-jour 6, Robert-le-Diable 25, Citron de Provence 30, Petite Tortue 50, Machaon 85, Monarque 100. Le test strict conserve l’ordre initial entre tailles égales : le tri est stable.

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

Question 12

#

Choisir et justifier le coût dans le pire cas : linéaire, quadratique, logarithmique ou exponentiel.

Comprendre la correction

Quadratique, O(n²). Dans une liste initialement décroissante, le nouvel élément doit passer devant tous ceux déjà traités. Le nombre de décalages vaut 1 + 2 + ... + (n - 1) = n(n - 1)/2. Deux boucles ne suffisent pas à elles seules à justifier un carré ; le décompte des déplacements donne ici la raison.

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

Question 13

#

Expliquer brièvement le principe des k plus proches voisins pour classifier la nouvelle espèce photographiée.

Comprendre la correction

On représente les observations connues et la nouvelle par des caractéristiques comparables, puis on calcule leur distance. On sélectionne les k observations connues les plus proches et on attribue à la nouvelle la classe majoritaire parmi elles, avec une règle de départage définie en cas d’égalité.

Le choix de k, des caractéristiques et de l’échelle des distances influence la décision. Un tri selon la taille seule ne suffit pas à valider une classification utilisant aussi les motifs ou couleurs.

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

La nouvelle espèce ressemble à Aglais io, mais diffère par la taille et la couleur des motifs. On cherche une séquence caractéristique dans son ADN, représenté par une chaîne. Trouver une séquence est ici une opération informatique ; cela ne prouve pas à elle seule une hypothèse de mutation.

def recherche_seq(seq, chaine):
    for i in range(len(chaine) - len(seq) + 1):
        j = 0
        while j < len(seq) and ...:
            j += 1
        if ...:
            return i
    return -1

Question 14

#

Compléter la recherche naïve recherche_seq(seq, chaine), qui renvoie le début de la première occurrence ou-1.

PythonRepérer la première séquence ADN sans sauter le dernier alignementÉcrivez votre solution et mettez-la à l’épreuve

Complétez recherche_seq(seq, chaine), la recherche naïve demandée dans le sujet. Renvoyez l’indice du début de la première occurrence de seq dans chaine, ou -1. Comparez les caractères pour chaque alignement possible, sans utiliser find ni index. Une occurrence partielle ne suffit pas. Le squelette donne naturellement 0 pour un motif vide ; cette convention est explicitement conservée dans les tests.

def recherche_seq(seq,chaine):
    # À vous de jouer
    pass

Les cas de test proposés :

  • Une occurrence au début : L’indice 0 est un succès, pas une valeur qui signifie échec.
  • Le dernier alignement possible : La borne de range doit inclure len(chaine)-len(seq).
  • Des débuts qui se chevauchent : Deux premiers A identiques peuvent conduire à un échec plus loin. Il faut ensuite avancer le début d’un seul caractère.
  • Renvoyer la première occurrence : Trouver une autre occurrence plus loin ne doit pas remplacer la première.
  • Aucune occurrence complète : Un motif trop long et une correspondance partielle sont tous deux des échecs.
  • La convention du motif vide : Aucun caractère n’est à comparer ; le premier alignement convient selon le code demandé.
Comprendre la correction
def recherche_seq(seq, chaine):
    for i in range(len(chaine) - len(seq) + 1):
        j = 0
        while j < len(seq) and chaine[i + j] == seq[j]:
            j += 1
        if j == len(seq):
            return i
    return -1

Pour chaque alignement i, j compte les caractères consécutifs qui correspondent. La comparaison porte sur chaine[i+j] et seq[j]. Lorsque j atteint la longueur du motif, tous les caractères sont identiques et i est la première position de l’occurrence.

Le+1 dans la borne du range permet de tester le dernier alignement possible. Si le motif est plus long que la chaîne, aucun alignement n’est testé et-1 est renvoyé. Pour un motif vide, le programme renvoie 0, convention cohérente avec la recherche Python.

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

Le code de Boyer-Moore-Horspool fourni par le sujet est repris dans la correction pour l’expliquer ligne par ligne.

Question 15

#

Expliquer le principe de Boyer-Moore-Horspool et son avantage sur la recherche naïve.

Indice

Horspool regarde le caractère situé à la fin de la fenêtre.

Comprendre la correction
def dico_lettres(seq):
    d = {}
    for i in range(len(seq) - 1):
        d[seq[i]] = i
    return d

def recherche_BMH(seq, chaine):
    decalage = dico_lettres(seq)
    i = 0
    n = len(seq)
    while i <= len(chaine) - n:
        j = n - 1
        while j >= 0 and chaine[i + j] == seq[j]:
            j -= 1
        if j == -1:
            return i
        else:
            if chaine[i + n - 1] in decalage:
                i += n - decalage[chaine[i + n - 1]] - 1
            else:
                i += n
    return -1

Le motif est comparé de droite à gauche. En cas d’échec, on regarde le caractère de la chaîne aligné avec la dernière position du motif. Le dictionnaire mémorise la position la plus à droite de ce caractère dans le motif, en excluant sa dernière position. On décale pour aligner ces occurrences, ou de toute la longueur du motif si le caractère est absent.

Cette exclusion garantit un décalage positif. Les sauts peuvent ignorer plusieurs alignements impossibles et économiser des comparaisons par rapport au déplacement naïf de 1. Cela ne garantit pas un meilleur coût sur toute chaîne : le pire cas peut rester proportionnel au produit des longueurs du texte et du motif. Horspool emploie le caractère de fin de fenêtre, pas nécessairement celui où la comparaison a échoué.

Voir la question dans le sujet PDF, p. 19 (nouvel onglet)
Naïf ou Horspool : où le motif peut-il commencer ?Un atelier pour expérimenter

Choisissez un texte et un motif d’ADN. Comparez les alignements réellement essayés par la recherche naïve et par Horspool ; chaque ligne montre le saut retenu.

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

Première occurrence à l’indice5

Seuls A,C,G,T sont conservés et les lettres sont mises en majuscules. Horspool fonde son saut sur le caractère de fin de fenêtre ; le tableau rend chaque saut vérifiable.

Alignement iFenêtreComparaisonsDécalage ou résultat
0ACGTT25
5ACGAT5Trouvé

Le gain de Horspool vient des alignements ignorés sans risque. La preuve porte sur la table de décalage ; voir moins de comparaisons sur un exemple ne prouve pas une amélioration de tous les pires cas.

Revoir les notions de cet exercice

Du sujet à la méthode

Votre prochaine séance de révision

  • Ne comptez pas le coût pour traverser un réseau déjà atteint : repérez son routeur d’accès avant de compléter la métrique.
  • Pour la programmation dynamique, écrivez les trois dépendances autour d’une case et choisissez l’ordre de parcours qui les rend disponibles.
  • Dans Horspool, distinguez le caractère où la comparaison échoue du caractère utilisé pour calculer le décalage.

Retrouver ces notions dans d’autres sujets

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

Énoncé : sujet 25-NSIJ1ME3 (PDF) · Publication d’origine (nouvel onglet). Corrigé et explications pédagogiques proposés par Sofien.