Aller au contenu
La Belle Copie
ConnexionCommencer gratuitement

Informatique B

X-ENSMP2026

Réponses complètes dans la numérotation du sujet : pour vérifier et s'autocorriger.

Rédigé par IA, non vérifié

Signaler une erreur
# X-ENS MP-PC-PSI — Informatique B 2026 · Corrigé brut

*Corrigé rédigé par IA (La Belle Copie), non officiel. Signalez toute erreur.*

Sujet : « Cycles et unification ». Durée 2 h, calculatrice interdite. Notations : $s\rightsquigarrow t$ signifie qu'il existe un chemin (éventuellement vide) de $s$ à $t$ ; on note `époque`, `sortie`, `présent`, `compteur` les variables globales de l'énoncé.

Rappel du graphe de la figure 1 (listes d'adjacence, l'étiquette d'un arc est son indice) :

```text
a : [c, d]    b : [d, c]    c : [X, Y]    d : [Y, X]
e : [W, Z]    W : [Z]       X : []        Y : []        Z : []
```

## Partie I. Détection de cycles

### Question 1

Avec `présent = 1`, `compteur = 0`, `pp('a')` visite $a$, puis $c$, $X$ (sortie 0), $Y$ (sortie 1), termine $c$ (sortie 2), visite $d$ dont les deux cibles $Y$ et $X$ portent déjà l'époque 1, termine $d$ (sortie 3), puis $a$ (sortie 4). Les sommets $b,e,W,Z$ ne sont pas visités.

![Figure 1 — État des dictionnaires après pp('a') : époque par le remplissage, sortie dans la pastille, sur le graphe de la figure 1 du sujet](/figures/corriges/x-ens_mp-pc-psi_informatique-b_2026/q01-parcours-pp-a.svg)

| | a | b | c | d | e | W | X | Y | Z |
|---|---|---|---|---|---|---|---|---|---|
| `époque` | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | 0 |
| `sortie` | 4 | 0 | 2 | 3 | 0 | 0 | 0 | 1 | 0 |

### Question 2

Soit $p$ la valeur de `présent` avant l'incrémentation. On est hors de `pp`, donc A2 est vrai : $\texttt{époque}[t]\le p$ pour tout sommet $t$. Après l'incrémentation, `présent` vaut $p+1$, et l'incrémentation ne modifie pas `époque`. Donc

$$\texttt{époque}[t]\le p<p+1=\texttt{présent},$$

d'où **$\texttt{époque}[t]\neq\texttt{présent}$ pour tout sommet $t$**.

### Question 3

Soit $p+1$ la valeur de `présent` après la ligne 1. Pendant `pp(s)`, `présent` n'est pas modifié. Une époque n'est modifiée qu'à la ligne 16, et elle reçoit alors la valeur `présent`, sur un sommet visité, c'est-à-dire un sommet sur lequel `pp` est appelée.

*Sens indirect.* On suppose $\texttt{époque}[t]=\texttt{présent}$ après l'appel. D'après Q2, cette égalité était fausse avant l'appel, donc $t$ a été visité. Or `pp` n'est appelée récursivement que sur un successeur d'un sommet visité, en partant de $s$. Par récurrence sur l'ordre des appels, tout sommet visité est accessible depuis $s$. Donc $s\rightsquigarrow t$.

*Sens direct.* Montrons d'abord que si $x$ est visité et $x\to y$ est un arc, alors $y$ est visité. Dans `pp(x)`, la boucle examine $y$ : soit $\texttt{époque}[y]=\texttt{présent}$, et $y$ a déjà été visité pendant l'appel (Q2), soit `pp(y)` est appelée. Soit alors un chemin $s=x_0\to x_1\to\dots\to x_k=t$. Le sommet $x_0$ est visité, puis chaque $x_{i+1}$ l'est par récurrence. Donc $t$ est visité et reçoit l'époque `présent`, qui n'est ensuite plus modifiée.

$$\boxed{\ s\rightsquigarrow t\iff \texttt{époque}[t]=\texttt{présent}\ \text{après l'exécution}\ }$$

### Question 4

On se place, comme dans `accessible`, juste après `présent += 1`. D'après Q2, au début de `pp(s)` aucun sommet ne porte l'époque courante. Comme $s\rightsquigarrow u$, le sommet $u$ est visité (Q3), donc `pp(u)` s'exécute une fois. La valeur de `compteur` croît strictement à chaque affectation de `sortie`. Considérons l'instant où la boucle de `pp(u)` examine $t=v$.

- Si $\texttt{époque}[v]\neq\texttt{présent}$, alors `pp(v)` est appelée. Elle se termine, en affectant `sortie[v]`, avant que `pp(u)` n'affecte `sortie[u]` après sa boucle. Donc $\texttt{sortie}[v]<\texttt{sortie}[u]$.
- Si $\texttt{époque}[v]=\texttt{présent}$, alors `pp(v)` a déjà commencé pendant cet appel. Supposons que `pp(v)` ne soit pas terminée. Alors `pp(u)` a été appelée, directement ou non, depuis `pp(v)`. La pile d'appels fournit un chemin $v\rightsquigarrow u$, qui forme un cycle avec l'arc $u\to v$, ce qu'exclut A1. Donc `pp(v)` est terminée, et `sortie[v]` a reçu une valeur de `compteur` strictement inférieure à celle que recevra `sortie[u]`.

Dans les deux cas, $\boxed{\texttt{sortie}[u]>\texttt{sortie}[v]}$, donc a fortiori $\texttt{sortie}[u]\ge\texttt{sortie}[v]$.

### Question 5

**Le test des lignes 5–6 n'élimine jamais à tort.** On suppose $s\rightsquigarrow t$ par un chemin $s=x_0\to\dots\to x_k=t$. Par A3 appliqué à chaque arc, $\texttt{époque}[x_0]\le\dots\le\texttt{époque}[x_k]$, donc $\texttt{époque}[s]\le\texttt{époque}[t]$. Si de plus $\texttt{époque}[s]=\texttt{époque}[t]$, la suite croissante a des extrémités égales, donc elle est constante. A4 s'applique alors à chaque arc du chemin : $\texttt{sortie}[x_0]\ge\dots\ge\texttt{sortie}[x_k]$, donc $\texttt{sortie}[s]\ge\texttt{sortie}[t]$. La condition des lignes 5–6 est donc fausse. Pour $t=s$, cela couvre le chemin vide.

**Conclusion.**

- Si $s\rightsquigarrow t$, on exécute `présent += 1` puis `pp(s)`. Pour $t\neq s$, Q3 donne $\texttt{époque}[t]=\texttt{présent}$. Pour $t=s$, la ligne 16 l'impose. La fonction renvoie `True`.
- Si $s\not\rightsquigarrow t$, alors $t\neq s$. Soit le test renvoie `False`, soit `pp(s)` s'exécute, et Q3 donne $\texttt{époque}[t]\neq\texttt{présent}$ : la fonction renvoie `False`.

**`accessible(s, t)` renvoie `True` si et seulement si $s\rightsquigarrow t$.**

### Question 6

On suppose A1–A4 vrais à l'appel. Si la fonction sort ligne 7, rien n'est modifié. Sinon, soit $p$ la valeur de `présent` avant la ligne 9. Après `pp(s)`, on note $V$ l'ensemble des sommets visités. D'après Q3, $V=\{x : s\rightsquigarrow x\}$, et ce sont exactement les sommets d'époque $p+1$. Les sommets hors de $V$ gardent leurs `époque` et `sortie` inchangées, et leur époque reste $\le p$ (A2 avant l'appel). Enfin, $V$ est stable par les arcs : si $x\in V$ et $x\to y$, alors $y\in V$.

- **A2.** Si $x\in V$, $\texttt{époque}[x]=p+1=\texttt{présent}$. Sinon, $\texttt{époque}[x]\le p<\texttt{présent}$.
- **A3.** Soit un arc $x\to y$. Si $x\in V$, alors $y\in V$, et les deux époques valent $p+1$. Si $x\notin V$ et $y\in V$, alors $\texttt{époque}[x]\le p<p+1=\texttt{époque}[y]$. Si $x,y\notin V$, rien n'a changé et A3 valait avant.
- **A4.** Soit un arc $x\to y$ dont les deux extrémités ont la même époque. Un sommet de $V$ et un sommet hors de $V$ ont des époques différentes, donc $x$ et $y$ sont tous deux dans $V$ ou tous deux hors de $V$. S'ils sont dans $V$, on a $s\rightsquigarrow x$, et Q4 donne $\texttt{sortie}[x]\ge\texttt{sortie}[y]$. Sinon, rien n'a changé et A4 valait avant.

`accessible` n'ajoute aucun arc. **A2, A3 et A4 sont préservés.**

### Question 7

`lier(u, s)` appelle `accessible(s, u)`, qui préserve A2–A4 (Q6) sans modifier le graphe. Si le résultat est `True`, rien d'autre n'est fait. Sinon, $s\not\rightsquigarrow u$ (Q5) et l'on ajoute l'arc $u\to s$. Il reste à étudier ce cas.

- **A1.** Le graphe était acyclique. Un cycle du nouveau graphe emprunterait donc l'arc $u\to s$ et se refermerait par un chemin $s\rightsquigarrow u$ de l'ancien graphe, ce qui est impossible. En particulier $u\neq s$, car `accessible(s, s)` renvoie `True`.
- **A2.** L'ajout d'un arc ne modifie ni `époque` ni `présent`.
- **A3, A4.** Les arcs anciens vérifient A3–A4 (Q6), et `époque`/`sortie` ne changent plus. Il suffit d'étudier le nouvel arc $u\to s$, selon la branche qui a renvoyé `False` :
  - sortie ligne 7 avec $\texttt{époque}[s]>\texttt{époque}[u]$ : A3 est strict pour $u\to s$, et A4 est vide ;
  - sortie ligne 7 avec $\texttt{époque}[s]=\texttt{époque}[u]$ et $\texttt{sortie}[s]<\texttt{sortie}[u]$ : A3 tient (égalité), et A4 tient puisque $\texttt{sortie}[u]>\texttt{sortie}[s]$ ;
  - sortie ligne 12 : `pp(s)` a posé $\texttt{époque}[s]=\texttt{présent}$, et $\texttt{époque}[u]\neq\texttt{présent}$. Comme $\texttt{époque}[u]\le\texttt{présent}$ par A2, on a $\texttt{époque}[u]<\texttt{époque}[s]$ : A3 est strict, et A4 est vide.

**`lier(u, s)` préserve A1, A2, A3 et A4.**

## Partie II. Unification

### Question 8

D'après les longueurs des listes :

- **sommets internes** (degré 2) : $a,b,c,d,e$ ;
- **sommet lié** (degré 1) : $W$ ;
- **sommets libres** (degré 0) : $X,Y,Z$.

### Question 9

On pose $N(s)$ = longueur, en nombre d'arcs, du plus long chemin issu de $s$. Elle est bien définie : $G$ est fini et acyclique (A1), donc un chemin ne repasse jamais par un sommet et a au plus $|S|-1$ arcs. Pour tout arc $s\to t$, on prolonge un plus long chemin issu de $t$ par l'arc $s\to t$, d'où $N(s)\ge 1+N(t)>N(t)$. Les appels récursifs de `fn(s)` portent sur $s^*$, $s_0$ ou $s_1$, qui sont des cibles d'arcs issus de $s$. Donc $N$, entier naturel, décroît strictement à chaque appel récursif, et **`fn` termine**.

### Question 10

On note $C(s)$ le nombre d'appels à `fn` déclenchés par `fn(s)`, cet appel compris. On a $C(s)=1$ si $s$ est libre, $C(s)=1+C(s^*)$ si $s$ est lié, et $C(s)=1+C(s_0)+C(s_1)$ si $s$ est interne.

Pour $m\ge 0$, on considère le graphe à $n=2(m+1)$ sommets $x_0,y_0,\dots,x_m,y_m$, avec $x_0,y_0$ libres et, pour $1\le i\le m$ :

```text
x_i : [x_{i-1}, y_{i-1}]      y_i : [y_{i-1}, x_{i-1}]
```

![Figure 2 — Famille de graphes de la Q10 : deux sommets par étage, arcs vers l'étage inférieur, et nombre d'appels de fn par étage](/figures/corriges/x-ens_mp-pc-psi_informatique-b_2026/q10-famille-etages.svg)

Ce graphe est acyclique (les arcs vont de l'étage $i$ à l'étage $i-1$), de degré sortant au plus 2, et sans arc multiple. Par symétrie, $C(x_i)=C(y_i)=:c_i$, avec $c_0=1$ et $c_i=1+2c_{i-1}$. Comme $c_i+1=2(c_{i-1}+1)$, on obtient $c_m=2^{m+1}-1$. Donc

$$\boxed{C(x_m)=2^{n/2}-1}$$

Chaque appel coûte au moins une opération élémentaire. **La complexité dans le pire cas de `fn` est donc au moins exponentielle en le nombre de sommets.**

Si l'on accepte des arcs parallèles (`G[s_i] = [s_{i-1}, s_{i-1}]`), une simple chaîne donne même $2^n-1$ appels pour $n$ sommets.

### Question 11

On mémoïse le résultat par sommet dans un dictionnaire :

```python
def fn_efficace(s):
    memo = {}

    def aux(x):
        if x not in memo:
            if len(G[x]) == 0:          # libre
                memo[x] = x
            elif len(G[x]) == 1:        # lié
                memo[x] = aux(G[x][0])
            else:                       # interne
                memo[x] = (aux(G[x][0]), aux(G[x][1]))
        return memo[x]

    return aux(s)
```

*Correction.* `aux` suit exactement la définition de $\mathrm{fn}$. La valeur mémorisée pour $x$ est $\mathrm{fn}(x)$, et elle termine par Q9.

*Complexité.* Le corps principal de `aux(x)` (calcul et affectation de `memo[x]`) s'exécute au plus une fois par sommet. Hors appels récursifs, il coûte $O(1)$ : tests, accès au dictionnaire, construction d'un couple. Un couple ne contient que deux références vers des valeurs déjà construites : les sous-termes sont **partagés** et ne sont pas recopiés. Le nombre total d'appels à `aux` est au plus $1+2|S|$, car chaque exécution du corps lance au plus deux appels. **La complexité est $O(|S|)$.**

### Question 12

$T(s)=1$ si $s$ est libre, car le seul chemin est le chemin vide. Sinon, un chemin issu de $s$ qui aboutit à un sommet libre commence par l'un des arcs sortants de $s$, ce qui donne une partition : $T(s)=\sum_{s\to t}T(t)$. La récursion termine par Q9. On mémoïse pour obtenir un coût linéaire :

```python
def taille(s):
    memo = {}

    def aux(x):
        if x not in memo:
            if len(G[x]) == 0:
                memo[x] = 1
            else:
                total = 0
                for y in G[x]:
                    total = total + aux(y)
                memo[x] = total
        return memo[x]

    return aux(s)
```

Vérification sur la figure 1 : $T(a)=T(c)+T(d)=2+2=4$ et $T(W)=T(Z)=1$. **Complexité $O(|S|)$** grâce à la mémoïsation. Sans elle, le coût serait exponentiel sur l'exemple de Q10.

### Question 13

Soit $H$ une extension de $G$. Une extension n'ajoute d'arcs qu'aux sommets libres, donc $c$ reste interne dans $H$, avec $c_0=X$ et $c_1=Y$ :

$$\mathrm{fn}_H(c)=\big(\mathrm{fn}_H(X),\ \mathrm{fn}_H(Y)\big).$$

On définit la taille d'une forme normale par $|v|=1$ pour un sommet et $|(\alpha,\beta)|=1+|\alpha|+|\beta|$. Les formes normales sont finies par Q9. On a donc

$$|\mathrm{fn}_H(c)|=1+|\mathrm{fn}_H(X)|+|\mathrm{fn}_H(Y)|>|\mathrm{fn}_H(X)|.$$

Ainsi $\mathrm{fn}_H(c)\neq\mathrm{fn}_H(X)$, quelle que soit l'extension $H$, que $X$ y reste libre ou y devienne lié. **Le problème $\{(c,X)\}$ n'a pas de solution.**

### Question 14

Ligne 17, on complète par :

```python
        elif len(G[s]) == 0:
            if s != t and not lier(s, t):
                return False
```

*Justification.* Cette branche n'est atteinte que si $s$ est libre et $t$ n'est pas lié, c'est-à-dire $t$ libre ou interne. Les cas « $t$ lié » et « $s,t$ internes » sont traités avant, et la branche `else` ramène le cas « $s$ interne, $t$ libre » à celui-ci par T1.

- Si $s=t$, la paire est satisfaite par toute extension. On la retire simplement de $P$. Le test `s != t` est indispensable : `lier(s, s)` renverrait `False`, puisque $s\rightsquigarrow s$.
- Si $s\neq t$, on est dans les hypothèses de T4 : $P$ a une solution si et seulement si $P'$ en a une qui contient l'arc $s\to t$. Une extension est acyclique et contient $G$. Si $t\rightsquigarrow s$, aucune extension ne contient $s\to t$, et `lier(s, t)` renvoie `False` (partie I) : $P$ n'a pas de solution. Sinon, `lier` ajoute l'arc $s\to t$, étiqueté 0 puisque $s$ était libre. Le nouveau $G$ est une extension, et l'on poursuit avec $P'$, déjà privé de $(s,t)$ par `P.pop()`.

### Question 15

On renvoie les arcs qui violent A3 ou A4. Les deux invariants sont vérifiés si et seulement si le résultat est vide.

```sql
SELECT a.source, a.cible
FROM arcs AS a
JOIN sommets AS s ON s.id = a.source
JOIN sommets AS t ON t.id = a.cible
WHERE s.époque > t.époque
   OR (s.époque = t.époque AND s.sortie < t.sortie);
```

La violation de A3 s'écrit $\texttt{époque}[s]>\texttt{époque}[t]$. La négation de l'implication A4 s'écrit $\texttt{époque}[s]=\texttt{époque}[t]$ et $\texttt{sortie}[s]<\texttt{sortie}[t]$. On peut aussi remplacer la première ligne par `SELECT COUNT(*)`, qui doit valoir 0.

### Question 16

**Lemme.** Il existe deux sommets $s\neq t$ tels que $\mathrm{fn}(s)=\mathrm{fn}(t)$ si et seulement si (i) $G$ a un sommet lié, ou (ii) deux sommets internes distincts ont les mêmes fils : $s_0=t_0$ et $s_1=t_1$.

*Preuve.* Si $s$ est lié, alors $\mathrm{fn}(s)=\mathrm{fn}(s^*)$, et $s\neq s^*$ par acyclicité. Si $s,t$ sont internes et ont les mêmes fils, alors $\mathrm{fn}(s)=(\mathrm{fn}(s_0),\mathrm{fn}(s_1))=\mathrm{fn}(t)$.

Réciproquement, on suppose qu'aucun sommet n'est lié et qu'il existe des paires $s\neq t$ avec $\mathrm{fn}(s)=\mathrm{fn}(t)$. On en choisit une qui minimise $N(s)$ (Q9). La forme normale d'un sommet libre est un sommet, celle d'un sommet interne est un couple. Si $s$ ou $t$ est libre, les deux le sont, et $s=\mathrm{fn}(s)=\mathrm{fn}(t)=t$, ce qui est exclu. Les deux sommets sont donc internes, avec $\mathrm{fn}(s_0)=\mathrm{fn}(t_0)$ et $\mathrm{fn}(s_1)=\mathrm{fn}(t_1)$. Si $s_0\neq t_0$, la paire $(s_0,t_0)$ conviendrait avec $N(s_0)<N(s)$, ce qui contredit la minimalité. Donc $s_0=t_0$, et de même $s_1=t_1$ : on est dans le cas (ii). $\square$

![Figure 3 — Les deux motifs cherchés par la requête de la Q16 : sommet lié et sa cible, deux internes de mêmes fils, arcs nommés par leurs alias SQL](/figures/corriges/x-ens_mp-pc-psi_informatique-b_2026/q16-temoins-locaux.svg)

La requête suivante renvoie une telle paire, ou un résultat vide s'il n'en existe pas :

```sql
SELECT a.source, a.cible                      -- (i) un sommet lié et sa cible
FROM arcs AS a
JOIN sommets AS s ON s.id = a.source
WHERE s.degré = 1
UNION
SELECT a0.source, b0.source                   -- (ii) deux internes de mêmes fils
FROM arcs AS a0
JOIN arcs AS b0 ON b0.cible = a0.cible AND b0.étiquette = 0
JOIN arcs AS a1 ON a1.source = a0.source AND a1.étiquette = 1
JOIN arcs AS b1 ON b1.source = b0.source AND b1.étiquette = 1
                AND b1.cible = a1.cible
WHERE a0.étiquette = 0 AND a0.source < b0.source
LIMIT 1;
```

La présence d'un arc d'étiquette 1 garantit que les sommets de la seconde partie sont internes. La condition `a0.source < b0.source` impose $s\neq t$. Dans la première partie, $s\neq s^*$ par acyclicité. **Toute ligne renvoyée vérifie $s\neq t$ et $\mathrm{fn}(s)=\mathrm{fn}(t)$, et le lemme garantit qu'une ligne est renvoyée dès qu'une telle paire existe.** Sur la figure 1, la requête renvoie $(W,Z)$.

Aide sur le corrigé

Connectez-vous pour demander une explication.