Skip to content

Optimisation au concours V2 : Informatique MPI/MPI*

🎯 Objectif & Analyse des tendances des Concours

Section titled “🎯 Objectif & Analyse des tendances des Concours”

En tant que président de jury et concepteur de sujets pour les concours scientifiques (filière MPI/MPI*), je constate une exigence accrue de rigueur formelle, tant dans la maîtrise des structures de données avancées et de la théorie de la complexité que dans la manipulation formelle des langages et des bases de données.

Les épreuves écrites (X-ENS, Mines-Ponts, Centrale-Supélec, CCINP) ne cherchent plus seulement à évaluer une intuition algorithmique ou l’écriture superficielle de code, mais exigent :

  1. Des preuves de correction rigoureuses (invariants de boucle, récurrence bien fondée, arguments de monotonicité).
  2. Une maîtrise absolue des complexités temporelles et spatiales (en pire cas, en moyenne, avec ou sans amortissement).
  3. Une syntaxe et une sémantique irréprochables (qu’il s’agisse de code OCaml, de requêtes SQL ou de raisonnements mathématiques sur les automates et langages).

Ce guide “OPTIMISATION AU CONCOURS - QUESTIONS RECURRENTES V2” a pour but de vous transposer directement dans la tête du correcteur. Pour chaque grande thématique récurrente en informatique MPI, vous trouverez l’intitulé type, la méthode canonique attendue au tableau ou sur copie, une rédaction modèle notée 20/2020/20, ainsi que les pièges éliminatoires relevés par les rapports de jury.


📌 Question Type 1 : Algorithme de Bellman-Ford : détection de cycles de poids négatif et preuve de correction

Section titled “📌 Question Type 1 : Algorithme de Bellman-Ford : détection de cycles de poids négatif et preuve de correction”

💡 Méthode canonique & Étapes de rédaction

Section titled “💡 Méthode canonique & Étapes de rédaction”

L’algorithme de Bellman-Ford permet de calculer les plus courts chemins depuis un sommet source unique dans un graphe orienté dont les poids des arêtes peuvent être négatifs, tout en détectant la présence éventuelle de cycles absorbants (de poids total strictement négatif).

Pour répondre aux attentes d’un jury de concours, la rédaction doit suivre rigoureusement ces étapes :

  1. Définition de l’invariant : Poser clairement que, à l’étape kk, les distances stockées représentent le plus court chemin de la source vers chaque sommet en utilisant au plus kk arêtes.
  2. Phase de relaxation : Répéter ∣V∣−1|V| - 1 fois la mise à jour des distances pour toutes les arêtes (u,v)(u, v) de poids w(u,v)w(u, v) : si d[u]+w(u,v)<d[v] alors d[v]←d[u]+w(u,v)\text{si } d[u] + w(u, v) < d[v] \text{ alors } d[v] \leftarrow d[u] + w(u, v)
  3. Détection de cycle négatif : Effectuer une ∣V∣|V|-ième relaxation. S’il existe encore une arête (u,v)(u, v) telle que d[u]+w(u,v)<d[v]d[u] + w(u, v) < d[v], alors il existe un cycle de poids négatif accessible depuis la source.
  4. Justification de la complexité : Établir clairement que la complexité temporelle est en O(∣V∣⋅∣E∣)\mathcal{O}(|V| \cdot |E|) et la complexité spatiale en O(∣V∣)\mathcal{O}(|V|).

📋 Exemple de sujet & Rédaction modèle type Concours

Section titled “📋 Exemple de sujet & Rédaction modèle type Concours”

Énoncé type : Étant donné un graphe orienté G=(V,E)G = (V, E) valué par une fonction w:E→Rw : E \to \mathbb{R} et un sommet source s∈Vs \in V, proposer une fonction implémentant l’algorithme de Bellman-Ford en OCaml ou en Python, capable de renvoyer le tableau des distances minimales ou de lever une exception en présence d’un cycle de poids négatif.

Rédaction modèle (Barème /20) :

  • Analyse et Initialisation (4/20) : On initialise un tableau de distances d de taille ∣V∣|V| tel que d[s]=0d[s] = 0 et d[v]=+∞d[v] = +\infty pour tout v≠sv \neq s.

  • Code source en Python rigoureux (8/20) :

def bellman_ford(vertices, edges, source):
# vertices: liste des sommets [0..V-1]
# edges: liste de tuples (u, v, w)
V = len(vertices)
d = {v: float('inf') for v in vertices}
d[source] = 0
# Phase 1 : V-1 relaxations
for _ in range(V - 1):
modified = False
for u, v, w in edges:
if d[u] + w < d[v]:
d[v] = d[u] + w
modified = True
# Optimisation facultative mais appréciée : arrêt précoce
if not modified:
break
# Phase 2 : Détection de cycle de poids négatif
for u, v, w in edges:
if d[u] + w < d[v]:
raise ValueError("Presence d'un cycle de poids negatif")
return d
  • Justification théorique et Preuve de correction (8/20) :
    • Invariant de boucle : Après kk iterations de la boucle principale, pour tout sommet vv, d[v]d[v] est inférieur ou égal au poids du plus court chemin de ss à vv comportant au plus kk arêtes.
    • Terminaison : La boucle principale tourne au plus ∣V∣−1|V|-1 fois, chaque itération parcourant l’ensemble des ∣E∣|E| arêtes. La complexité temporelle est donc de O(∣V∣⋅∣E∣)\mathcal{O}(|V| \cdot |E|).
    • Détection : S’il existe une modification possible à la ∣V∣|V|-ième étape, cela contredit le fait que tout plus court chemin simple comporte au plus ∣V∣−1|V|-1 arêtes, prouvant l’existence d’un cycle absorbant.

⚠️ Les pièges majeurs soulignés dans les rapports de jury

Section titled “⚠️ Les pièges majeurs soulignés dans les rapports de jury”
  • Oublier de propager l’infini : Écrire d[u] + w < d[v] sans vérifier si d[u] est égal à +∞+\infty peut générer des erreurs de dépassement ou des incohérences logiques (ex: +∞+(−5)=+∞+\infty + (-5) = +\infty). Il faut s’assurer que d[u] != float('inf').
  • Confondre Dijkstra et Bellman-Ford : Appliquer une approche gloutonne de type file de priorité sur des poids négatifs mène à une catastrophe algorithmique. Le jury sanctionne lourdement cette confusion.
  • Négliger la structure des données : Fournir une complexité en O(∣V∣3)\mathcal{O}(|V|^3) sur un graphe implicite ou mal représenté au lieu de parcourir explicitement la liste des arêtes.

📌 Question Type 2 : Application du Lemme de l’étoile (Pumping Lemma) pour prouver qu’un langage n’est pas rationnel

Section titled “📌 Question Type 2 : Application du Lemme de l’étoile (Pumping Lemma) pour prouver qu’un langage n’est pas rationnel”

💡 Méthode canonique & Étapes de rédaction

Section titled “💡 Méthode canonique & Étapes de rédaction”

Le lemme de l’étoile est l’outil fondamental par l’absurde pour démontrer qu’un langage n’est pas reconnaissable par un automate fini (donc non rationnel). La structure de la preuve suit toujours un raisonnement par l’absurde basé sur un jeu de rôles contre un adversaire (le démon).

Les étapes canoniques de rédaction sont :

  1. Supposons par l’absurde que le langage LL est rationnel.
  2. D’après le lemme de l’étoile, il existe une constante de pumping N∈N∗N \in \mathbb{N}^* associée à LL.
  3. Choisir un mot w∈Lw \in L astucieusement tel que ∣w∣≥N|w| \ge N (ce choix dépend généralement de NN et doit être paramétré par ce dernier).
  4. Décomposer le mot w=xyzw = xyz en respectant les contraintes du lemme : ∣xy∣≤N|xy| \le N et ∣y∣≥1|y| \ge 1.
  5. Exhiber une contradiction en montrant qu’il existe un entier i≥0i \ge 0 (souvent i=0i = 0 ou i=2i = 2) tel que le mot pompé xyiz∉Lxy^iz \notin L.
  6. Conclure que LL n’est pas rationnel.

📋 Exemple de sujet & Rédaction modèle type Concours

Section titled “📋 Exemple de sujet & Rédaction modèle type Concours”

Énoncé type : Soit l’alphabet Σ={a,b}\Sigma = \{a, b\}. Démontrer que le langage L={anbn∣n∈N}L = \{a^n b^n \mid n \in \mathbb{N}\} n’est pas rationnel.

Rédaction modèle (Barème /20) :

  • Hypothèse par l’absurde et invocation du lemme (4/20) : Supposons par l’absurde que le langage L={anbn∣n∈N}L = \{a^n b^n \mid n \in \mathbb{N}\} soit rationnel. D’après le lemme de l’étoile (Pumping Lemma), il existe un entier N≥1N \ge 1 (la constante de pumping) tel que tout mot w∈Lw \in L de longueur ∣w∣peutsedeˊcomposeren|w| peut se décomposer en w = xyz$ avec les trois conditions suivantes :

    1. ∣xy∣≤N|xy| \le N
    2. ∣y∣≥1|y| \ge 1
    3. Pour tout entier i≥0i \ge 0, le mot xyiz∈Lxy^i z \in L.
  • Choix judicieux du mot ww (6/20) : Considérons le mot w=aNbNw = a^N b^N. Il est évident que w∈Lw \in L et que sa longueur ∣w∣=2N≥N|w| = 2N \ge N. Les conditions d’application du lemme sont donc vérifiées.

  • Exploitation de la contrainte ∣xy∣≤N|xy| \le N (5/20) : Puisque la condition ∣xy∣≤N|xy| \le N est impérative et que le préfixe de ww est constitué exclusivement de pucespuces de la lettre aa, le sous-mot yy (de longueur ∣y∣≥1|y| \ge 1) ne contient que des lettres aa. Posons donc y=aky = a^k avec 1≤k≤N1 \le k \le N. Par conséquent, xx et zz sont tels que w=ax⋅ak⋅aN−kbNw = a^x \cdot a^k \cdot a^{N-k} b^N.

  • Mise en évidence de la contradiction (5/20) : Appliquons le lemme pour i=0i = 0. Le mot pompé devient : xy0z=xz=aN−kbNxy^0z = xz = a^{N-k} b^N Puisque k≥1k \ge 1, on a N−k<NN-k < N. Le nombre de lettres aa est donc strictement inférieur au nombre de lettres bb (N−k≠NN-k \neq N). Par conséquent, le mot xy0z∉Lxy^0z \notin L, ce qui contredit directement le lemme de l’étoile.

Conclusion: Le langage LL n’est donc pas rationnel. ■\blacksquare


⚠️ Les pièges majeurs soulignés dans les rapports de jury

Section titled “⚠️ Les pièges majeurs soulignés dans les rapports de jury”
  • Inversion des quantificateurs dans le choix de ww : Laisser l’adversaire (le jury ou le lemme) choisir NN, et c’est vous qui devez construire ww en fonction de NN. Choisir un NN fixe (ex: N=5N=5) rend la démonstration fausse car le lemme doit valoir pour tout NN.
  • Oublier la contrainte ∣xy∣≤N|xy| \le N : C’est l’erreur la plus fréquente. Les étudiants choisissent w=aNbNw = a^N b^N mais oublient que yy doit se trouver entièrement dans la première moitié (les aa). Si yy était à cheval sur les aa et les bb, le pompage pourrait préserver l’égalité par compensation, ce qui bloquerait la contradiction.
  • Absence de rigueur sur l’appartenance : Ne pas expliciter clairement pourquoi le mot pompé ne respecte plus la condition d’appartenance au langage.

📌 Question Type 3 : Requêtes SQL avancées : sous-requêtes corrélées, auto-jointures et expressions de table communes (CTE avec WITH)

Section titled “📌 Question Type 3 : Requêtes SQL avancées : sous-requêtes corrélées, auto-jointures et expressions de table communes (CTE avec WITH)”

💡 Méthode canonique & Étapes de rédaction

Section titled “💡 Méthode canonique & Étapes de rédaction”

Les bases de données relationnelles et le langage SQL font l’objet d’attentes rigoureuses aux concours, notamment pour manipuler des données complexes sans redondance excessive. Face à une question de requêtage avancé :

  1. Analyser le schéma relationnel : Identifier les clés primaires (PK) et les clés étrangères (FK), ainsi que les types de cardinalités (1-N, N-N).
  2. Choisir la bonne stratégie :
    • Auto-jointure (Self-Join) : Pour comparer des lignes d’une même table entre elles (ex: hiérarchies, comparaisons temporelles).
    • Sous-requête corrélée : Lorsque le calcul d’une ligne dépend des valeurs d’autres lignes de la même table (souvent évaluée ligne par ligne).
    • CTE (WITH ... AS) : Indispensable pour la lisibilité, la modularité et pour éviter l’imbrication excessive de sous-requêtes (notamment lors de calculs d’agrégats successifs ou de fenêtrage).
  3. Traiter les cas limites (valeurs NULL, doublons) : Utiliser DISTINCT, GROUP BY avec HAVING, ou des clauses de gestion des valeurs nulles si nécessaire.

📋 Exemple de sujet & Rédaction modèle type Concours

Section titled “📋 Exemple de sujet & Rédaction modèle type Concours”

Énoncé type : On considère le schéma relationnel suivant :

  • Employe (id_emp, nom, salaire, id_dept, id_manager)
  • Departement (id_dept, nom_dept, budget)
  • Écrire une requête SQL optimisée utilisant une expression de table commune (CTE) pour afficher les noms des employés dont le salaire est strictement supérieur au salaire moyen de leur propre département.

Rédaction modèle (Barème /20) :

  • Analyse de la structure et choix de la CTE (5/20) : Pour comparer le salaire d’un employé à la moyenne de son département, nous devons d’abord calculer, pour chaque département, son salaire moyen. Une Expression de Table Commune (WITH) est idéale pour isoler cette vue agrégée intermédiaire.

  • Rédaction de la requête SQL (10/20) :

WITH SalaireMoyenDept AS (
SELECT id_dept, AVG(salaire) AS moyenne_dept
FROM Employe
GROUP BY id_dept
)
SELECT e.nom
FROM Employe e
JOIN SalaireMoyenDept s ON e.id_dept = s.id_dept
WHERE e.salaire > s.moyenne_dept;
  • Justification de l’optimisation et robustesse (5/20) :
    • Performance : L’utilisation d’une CTE évite d’évaluer une sous-requête corrélée à chaque itération pour chaque ligne de la table Employe, ce qui réduirait les performances en O(N2)\mathcal{O}(N^2). Ici, l’agrégation est calculée en une seule passe (O(N)\mathcal{O}(N)), puis jointe efficacement via l’index implicite sur id_dept.
    • Gestion des NULL : La jointure interne (JOIN) exclut automatiquement les employés n’ayant pas de département (id_dept IS NULL), ce qui est cohérent puisque le salaire moyen d’un département inexistant n’est pas définissable dans ce contexte.

⚠️ Les pièges majeurs soulignés dans les rapports de jury

Section titled “⚠️ Les pièges majeurs soulignés dans les rapports de jury”
  • Utilisation abusive de sous-requêtes corrélées non optimisées : Écrire des requêtes avec des IN ou des comparaisons scalaires répétées qui s’avèrent désastreuses en termes de complexité pour de grands volumes de données.
  • Mauvaise maîtrise de la clause GROUP BY : Oublier d’inclure dans le GROUP BY toutes les colonnes non agrégées sélectionnées dans le SELECT (erreur de syntaxe directe sanctionnée par les moteurs SQL et les jurys).
  • Confusion entre WHERE et HAVING : Filtrer des fonctions d’agrégation (comme AVG() ou SUM()) dans la clause WHERE au lieu de la clause HAVING.