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.
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
| Liaison | Réseau | Interface du premier routeur | Interface du second |
|---|---|---|---|
| R1-R2 | 50.50.50.8/30 | G3 | G3 |
| R1-R4 | 50.50.50.4/30 | G2 | G0 |
| R1-R5 | 50.50.50.0/30 | G1 | G2 |
| R2-R3 | 50.50.50.16/30 | G1 | G1 |
| R2-R4 | 50.50.50.12/30 | G2 | G1 |
| R4-R5 | 50.50.50.20/30 | G3 | G1 |
| R4-R6 | 50.50.50.24/30 | G2 | G1 |
| Réseau local | Passerelle, interface |
|---|---|
| 192.168.1.0/24 | R1,G0 |
| 192.168.2.0/24 | R2,G0 |
| 192.168.3.0/24 | R3,G0 |
| 10.0.0.0/8 | R5,G0 |
| 172.16.0.0/16 | R6,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.
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.
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
| Adresse | Rôle |
|---|---|
| 50.50.50.4 | Réseau |
| 50.50.50.5 | Hôte : R1,G2 |
| 50.50.50.6 | Hôte : R4,G0 |
| 50.50.50.7 | Diffusion |
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.
| Réseau destination | Interface donnée | Métrique donnée |
|---|---|---|
| 192.168.1.0/24 | G0 | 0 |
| 192.168.2.0/24 | … | … |
| 192.168.3.0/24 | … | … |
| 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 |
| 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 destination | Interface | Métrique |
|---|---|---|
| 192.168.1.0/24 | G0 | 0 |
| 192.168.2.0/24 | G3 | 1 |
| 192.168.3.0/24 | G3 | 2 |
| 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 |
| 50.50.50.20/30 | G1 | 1 |
| 50.50.50.24/30 | G2 | 1 |
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.
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.
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.00000000Question 6
#Donner mask_for_size(30).
Comprendre la correction
(255, 255, 255, 252)Les 24 premiers bits donnent trois octets 255. Les 6 bits réseau restants forment 11111100, soit 252 ; les deux bits hôtes restent nuls.
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.0000010050 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.
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 TrueLe 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.
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 NoneOn 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é.
| Liaison | Type | Débit |
|---|---|---|
| R1-R2 | Gigabit Ethernet | 1 Gb/s |
| R1-R4 | Fast Ethernet | 100 Mb/s |
| R1-R5 | Gigabit Ethernet | 1 Gb/s |
| R2-R3 | Gigabit Ethernet | 1 Gb/s |
| R2-R4 | Fibre | 10 Gb/s |
| R4-R5 | Gigabit Ethernet | 1 Gb/s |
| R4-R6 | Fibre | 10 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
| Type | Débit | Coût |
|---|---|---|
| Gigabit Ethernet | 1 Gbit/s =10⁹bit/s | 10 |
| Fast Ethernet | 100 Mbit/s =10⁸bit/s | 100 |
| Fibre | 10 Gbit/s =10¹⁰bit/s | 1 |
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.
| Réseau | Interface donnée | Métrique donnée |
|---|---|---|
| 192.168.1.0/24 | G0 | 0 |
| 192.168.2.0/24 | … | … |
| 192.168.3.0/24 | G3 | 20 |
| 172.16.0.0/16 | … | … |
| 10.0.0.0/8 | … | … |
| 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 | G3 | 10 |
| 50.50.50.16/30 | G3 | 10 |
| 50.50.50.20/30 | … | … |
| 50.50.50.24/30 | G3 | 11 |
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éseau | Interface | Métrique |
|---|---|---|
| 192.168.1.0/24 | G0 | 0 |
| 192.168.2.0/24 | G3 | 10 |
| 192.168.3.0/24 | G3 | 20 |
| 172.16.0.0/16 | G3 | 12 |
| 10.0.0.0/8 | G1 | 10 |
| 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 | G3 | 10 |
| 50.50.50.16/30 | G3 | 10 |
| 50.50.50.20/30 | G1 | 10 |
| 50.50.50.24/30 | G3 | 11 |
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.
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éseau | RIP : interface / métrique | OSPF : interface / métrique |
|---|---|---|
| 172.16.0.0/16 | G2 / 2 | G3 / 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 / colonne | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| 0 | 0 | 0 | 1 | 1 | 1 |
| 1 | 1 | 1 | 0 | 1 | 1 |
| 2 | 0 | 1 | 1 | 1 | 0 |
| 3 | 0 | 1 | 1 | 1 | 0 |
| 4 | 0 | 1 | 1 | 1 | 0 |
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 / colonne | 0 | 1 | 2 | 3 |
|---|---|---|---|---|
| 0 | 1 | 1 | 1 | 1 |
| 1 | 0 | 1 | 1 | 1 |
| 2 | 0 | 1 | 1 | 1 |
| 3 | 1 | 0 | 1 | 1 |
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.
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.
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 * tailleLa 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.
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 + 1Question 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 + 1Pour 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.
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.
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 / colonne | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| 0 | 0 | 0 | 1 | 2 | 1 |
| 1 | 1 | 1 | 0 | 1 | 1 |
| 2 | 0 | 3 | 2 | 1 | 0 |
| 3 | 0 | 2 | 2 | 1 | 0 |
| 4 | 0 | 1 | 1 | 1 | 0 |
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 / colonne | 0 | 1 | 2 | 3 |
|---|---|---|---|---|
| 0 | 1 | 3 | 2 | 1 |
| 1 | 0 | 2 | 2 | 1 |
| 2 | 0 | 1 | 2 | 1 |
| 3 | 1 | 0 | 1 | 1 |
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.
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.
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 auxQuestion 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
passLes 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 auxLa 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.
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.
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.
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.
| Ligne | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| 0 | 0 (aux=0) | 0 (aux=0) | 1 (aux=1) | 1 (aux=2) | 1 (aux=1) |
| 1 | 1 (aux=1) | 1 (aux=1) | 0 (aux=0) | 1 (aux=1) | 1 (aux=1) |
| 2 | 0 (aux=0) | ■ 1 (aux=3) | ■ 1 (aux=2) | ■ 1 (aux=1) | 0 (aux=0) |
| 3 | 0 (aux=0) | ■ 1 (aux=2) | ■ 1 (aux=2) | ■ 1 (aux=1) | 0 (aux=0) |
| 4 | 0 (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.num | nomCo | nomSc | taille | habitat | zone |
|---|---|---|---|---|---|
| 458 | Monarque | Danaus plexippu | 100 | Prairies | 3 |
| 459 | Citron de Provence | Gonepteryx cleopatra | 30 | Prairies | 1 |
| 460 | Paon-du-jour | Aglais io | 6 | Jardins | 6 |
| 461 | Machaon | Papilio machaon | 85 | Forêts | 2 |
| 462 | Petite Tortue | Aglais urticae | 30 | Prairies | 5 |
| 463 | Robert-le-Diable | Polygonia c-album | 25 | Forêts | 4 |
| plante.num | nomCo | nomSc | habitat | zone |
|---|---|---|---|---|
| 128 | Orchidée Phalaenopsis | Phalaenopsis | Forêts | 5 |
| 129 | Bambou | Bambusoideae | Forêts | 3 |
| 130 | Rose | Rosa | Haies | 2 |
| 131 | Lilas | Syringa | Haies | 6 |
| 132 | Coquelicot | Papaver rhoeas | Jardins | 4 |
| 133 | Lavande | Lavandula | Collines | 1 |
| zone_geographique.num | zone |
|---|---|
| 1 | Afrique du Nord |
| 2 | Amérique du Nord |
| 3 | Amérique du Sud |
| 4 | Asie |
| 5 | Asie du Sud |
| 6 | Europe |
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.
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é.
SELECT taille FROM papillon WHERE nomCo = 'Machaon';Question 3
#Donner le résultat de la requête sur le Machaon.
Comprendre la correction
| taille |
|---|
| 85 |
La clause WHERE sélectionne le Machaon, puis SELECT ne conserve que sa taille moyenne donnée dans l’extrait.
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.
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.
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.
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.
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.nomCo | plante.nomCo |
|---|---|
| Paon-du-jour | Lilas |
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.
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é.
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 collecOn 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.
Question 11
#Nommer le tri utilisé.
Comprendre la correction
Le tri par insertion. Il maintient un préfixe déjà trié et y insère un nouvel élément à chaque itération.
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.
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.
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 -1Question 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
passLes 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 -1Pour 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.
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 -1Le 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é.
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 i | Fenêtre | Comparaisons | Décalage ou résultat |
|---|---|---|---|
| 0 | ACGTT | 2 | 5 |
| 5 | ACGAT | 5 | Trouvé |
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.
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.
