Centrale Option Informatique MP 2016Sujet, corrigé et rapport du jury
- Structures de données (listes, arbres, tas binaires)
- Algorithmique du tri
- Analyse de la complexité
- Programmation récursive en Caml
Téléchargements
Présentation du sujet
Difficulté moyenneÉtude d'un algorithme de tri performant sur des listes presque triées, à base de tas binairesAfficher ou masquer la section
Présentation du sujet
Difficulté moyenneLe sujet étudie un algorithme de tri dont l'intérêt est d'avoir une bonne complexité lorsque la liste à trier est presque triée. Sa mise en œuvre s'appuie sur la manipulation d'arbres, de listes et de tableaux, avec plusieurs études de complexité. Le problème présente d'abord des algorithmes sur les listes et les arbres (tas binaires, décomposition parfaite d'un entier, création d'une liste de tas, tri des racines, extraction), puis une implantation en place dans un tableau pour réduire la complexité spatiale.
- 1I. PrésentationPrésenter les motivations du problème, les notations et les préliminaires nécessaires.
- 2II. Algorithme sur des arbresÉtudier le tri par insertion, les tas binaires, la décomposition parfaite d'un entier, la création d'une liste de tas, le tri des racines et l'extraction des éléments.
- 3III. Implantation dans un tableauReprendre l'algorithme en réduisant la complexité spatiale à l'aide d'un tri en place dans un tableau.
Difficulté moyenne. Le rapport indique que le sujet a été correctement compris, qu'un nombre significatif de candidats a pu le traiter dans son ensemble et que le niveau global des candidats est satisfaisant, avec une fraction notable de copies tout à fait excellentes.
Ce qu'a observé le jury
5 erreurs relevéesParcours de liste inutiles nuisant à la complexité · Confusion entre types construits · Incohérence entre algorithme décrit, code Caml et résultat sur exemplesAfficher ou masquer la section
Ce qu'a observé le jury
5 erreurs relevéesLe sujet, raisonnablement progressif et de longueur volontairement raisonnable, a été correctement compris par les candidats. Le caractère progressif de la mise en œuvre de l'algorithme a permis d'éviter les blocages et les signatures des fonctions Caml imposées ont été bien respectées. Le niveau moyen est satisfaisant, même si des difficultés persistent sur l'usage des types construits et la justification des calculs de complexité.
Les erreurs les plus sanctionnées
- 1Parcours de liste inutiles nuisant à la complexité
Pour que les complexités soient correctes, il ne fallait pas introduire de parcours de liste inutile, comme compter le nombre d'éléments simplement pour tester l'égalité de ses deux premiers éléments.
- 2Confusion entre types construits
Le jury observe de grandes difficultés dans l'usage des types construits : affectations incorrectes, confusion entre la déclaration de type Vide de l'arbre et la liste vide [].
« confusion entre la déclaration de type Vide de l'arbre et la liste vide »
- 3Incohérence entre algorithme décrit, code Caml et résultat sur exemples
De nombreux candidats traitent la description d'un algorithme, sa transcription en Caml et son résultat sur des exemples de façon totalement indépendante, avec des solutions différentes pour chacun.
- 4Percolation inutile de tous les tas
Dans le tri des racines, beaucoup de candidats cherchent à percoler tous les tas alors que l'objectif est d'obtenir une bonne complexité globale sans tout reconstruire.
- 5Récursivité terminale mal gérée
L'usage systématique et mal géré de la récursivité terminale a conduit certains candidats à obtenir des listes à l'envers, empêchant l'algorithme de fonctionner correctement.
Ce qui a été bien réussi
- Les signatures des fonctions Caml imposées ont été bien respectées par les candidats.
- La partie sur l'extraction des éléments, qui produit la liste triée, a été globalement bien comprise par les candidats qui l'ont abordée.
- Une fraction notable des candidats a rendu des copies tout à fait excellentes, avec des codes élégants et clairs.
Conseils du jury
- Vérifier la cohérence globale entre les différentes fonctions programmées, notamment lorsqu'un cas particulier n'est volontairement pas traité dans l'une d'elles.
- Prendre le recul nécessaire pour comprendre l'objectif de complexité visé avant d'implémenter chaque étape de l'algorithme.
- Écrire des codes clairs et commentés, en réutilisant les éléments précédemment définis plutôt qu'en multipliant les fonctions auxiliaires.
- S'exercer à la pratique sur machine pour s'approprier les réflexes de programmation et écrire des programmes lisibles sans l'aide d'un compilateur.
Synthèse rédigée par WikiPrépa à partir du rapport officiel du jury (à télécharger en PDF). Les citations sont extraites du rapport.
Ces sujets peuvent vous intéresser
Lecture du sujet en ligne
L'énoncé complet, avec les formules et les figures, sans ouvrir le PDF.Afficher ou masquer la section
Lecture du sujet en ligne
Un algorithme de tri
I Présentation
I.A - Motivation
Or, dans une application réelle, les données que l'on veut trier ne sont pas quelconques mais suivent une certaine distribution aléatoire, qui est loin d'être uniforme : ainsi, il est fréquent que les données soient déjà presque triées. De plus, de nombreux algorithmes de tris (même les plus performants) atteignent leur complexité maximale lorsque les données sont déjà triées.
Le but de ce problème est d'étudier un algorithme de tri, proche du tri par tas mais présentant avec lui quelques différences significatives et notamment des performances intéressantes lorsque les données qu'il reçoit sont presque triées.
Dans tout le problème, on triera, par ordre croissant, des valeurs entières.
Dans toutes les questions de complexité en temps, la mesure de complexité à considérer est le nombre de comparaisons par la relation d'ordre
I.B - Notations et préliminaires
Étant donné deux fonctions
-
f = O(g) pour exprimer qu'il existe une constanteC telle que, pour toutn suffisamment grand,f(n) ⩽ Cg(n) ; -
f = Ω(g) pour exprimer qu'il existe une constanteC > 0 telle que pour, toutn suffisamment grand,f(n) ⩾ Cg(n) ; -
f = Θ(g) pour exprimer qu'on af = O(g) etf = Ω(g) .
De manière générale, lorsqu'on dira que deux structures de données contiennent les mêmes éléments, ce sera toujours en tenant compte des répétitions. Par exemple les listes
II Algorithme sur des arbres
II.A - Tri par insertion
- v contient les mêmes éléments que
x : u ; - v est triée.
II.A.2) Écrire la fonction tri_insertion : int list→ int list triant la liste reçue en argument en utilisant la fonction précédente.
II.A.3) Pourn ∈ ℕ , on noteP_I(n) le nombre de comparaisons effectuées par l'appel (tri_insertion l) dans le cas le pire pour une liste 1 de longueurn . On note de mêmeM_I(n) le nombre de comparaisons effectuées dans le cas le meilleur.
DéterminerP_I(n) etM_I(n) .
II.B - Tas binaires
type arbre =
| Vide
| Noeud of int * arbre * arbre ;;
pour tous arbres binaires a1 et a2 et tout entier x .
x , a1 et a2 sont appelés respectivement la racine, le fils gauche et le fils droit de l'arbre a.
On dit que deux arbres ont mêmes éléments s'ils ont les mêmes ensembles d'étiquettes et que chaque étiquette présente apparait le même nombre de fois dans chacun des arbres.
On dit qu'un arbre binaire est parfait s'il s'agit de l'arbre vide Vide, ou s'il est de la forme Noeud(
On dit qu'un arbre binaire est un tas binaire parfait (ou simplement un tas parfait) si c'est un arbre parfait et que la valeur étiquetant chaque nœud de l'arbre est inférieure ou égale à celle de ses fils.
On dit qu'un arbre binaire est un quasi-tas si c'est un arbre de la forme Noeud(x,a1,a2) et que a1 et a2 sont des tas binaires parfaits de même taille : aucune contrainte d'ordre n'est donc imposée sur l'étiquette de la racine x . Étant donné un arbre non vide
II.B.1) Pour
II.B.2) Écrire la fonction min_tas: arbre
II.B.3) Écrire la fonction min_quasi: arbre
II.B.4) Écrire la fonction percole: arbre -> arbre telle que (percole a) renvoie a si a est l'arbre vide et, si a est un quasi-tas, renvoie un tas binaire parfait contenant les mêmes éléments. Donner la complexité de percole dans le cas le pire, en fonction de la hauteur
II.C - Décomposition parfaite d'un entier
Étant donné un entier naturel
- ou
r = 2 etk_1 ⩽ k_2 ; - ou
r ⩾ 3 etk_1 ⩽ k_2 < k_3 < ⋯ < k_r .
La propriété remarquable des nombres
pour tout entier naturel non nuln , il existe un unique entierr et un uniquer -uplet(k_1, …, k_r) d'entiers naturels non nuls vérifiant la propriété QSC et tel quen = m_(k_1) + ⋯ + m_(k_r)( cette somme étant par convention nulle sir = 0) .
On peut remarquer que, du fait de la stricte croissance de la suite d'entiers
II.C.1) Donner la décomposition parfaite des entiers
II.C.2) Soit
II.D - Création d'une liste de tas
Une liste de tas est implantée en Caml par le type (arbre * int) list.
Étant donnée une liste de tas
- la longueur de
h , notée long(h), par
- la taille de
h , notée|h| , par
- la hauteur de
h , notée haut(h), par
- le minimum de
h , notémin_H(h) , par
On dit qu'une liste de tas
II.D.1)
b) Même question si
II.D.2) Considérons un arbre réduit à sa racine (c'est-à-dire un couple (
a) On considère

b) Décrire le plus précisément possible un algorithme qui consiste à construire
c) Écrire la fonction ajoute: int -> (arbre * int) list -> (arbre * int) list telle que (ajoute
II.D.3) On définit la fonction suivante, de type int list
let rec constr_liste_tas l = match l with
| [] -> []
| x :: r -> ajoute x (constr_liste_tas r)
;;
a) Montrer que le coût en temps de l'appel (constr_liste_tas l) pour une liste l: int list déjà triée de longueur
b) Montrer que, pour une liste 1 : int list de longueur
II.E - Tri des racines
On considère une liste de tas
II.E.1) Écrire la fonction echange_racines: arbre
II.E.2) On considère une liste de tas non vide
a) si
b)
II.E.3) On examine maintenant trois exemples de couples (
Les couples considérés sont notés

II.E.4) Décrire et justifier le plus précisément possible un algorithme qui, à partir d'un quasi-tas
Montrer que sa complexité en temps est en
Montrer que sa complexité en temps est en
II.E.5) Écrire la fonction
insere_quasi: arbre -> int -> (arbre * int) list -> (arbre * int) list
II.E.6) Écrire la fonction tri_racines: (arbre * int) list -> (arbre * int) list transformant une liste de tas
II.E.7) Montrer que la fonction tri_racines, appliquée à une liste de tas
II.F - Extraction des éléments d'une liste de tas
II.F.1) Montrer que
II.F.2) Donner la complexité temporelle de l'évaluation de (insere_quasi
II.F.3) Écrire la fonction extraire: (arbre * int) list
II.F.4) Montrer que la fonction extraire, appliquée à une liste de tas
II.G - Synthèse
II.G.2) Montrer que la complexité de cette fonction est en
II.G.3) Déterminer la complexité temporelle de la fonction tri_lisse dans le cas particulier où la liste passée en argument est déjà triée.
III Implantation dans un tableau
Le but de cette partie est de proposer un algorithme avec la même complexité temporelle que tri_lisse et une meilleure complexité spatiale.
III.
- si
a est vide, la représentation dea ne nécessite aucune place ; - si
a est de la formeNoeud(x, a_1, a_2) , oùa_1 eta_2 sont de tailles respectivesk_1 etk_2 , on metx dans la casep du tableau, puis on représentea_1 dans lesk_1 cases commençant à l'indicep + 1 , puis on représentea_2 dans le tableaut dans lesk_2 cases commençant à l'indicep + 1 + k_1 (cf. figure 3 ).

Par la suite, tous les arbres que l'on représentera seront ou bien l'arbre vide, ou bien des quasi-tas (qui pourront éventuellement être des tas). On définit le type enregistrement suivant pour représenter l'arbre vide ou un quasi-tas stocké dans un tableau:
type tasbin = {donnees : int vect ; pos : int ; taille : int} ;;
Si le tableau stocké dans le champ donnees de cet enregistrement est le tableau
Par la suite, tous les éléments
III.B - Écrire les fonctions fg: tasbin
III.
III.

Écrire la fonction ajoute_vect : int vect
(* constr_liste_tas_aux : int vect -> int -> tasbin -> tasbin
ajoute les elements du tableau d d'indice i,
pour i<p, a la liste de h.
Precondition : h est vide vide ou la position du premier tas dans h
est p. *)
let rec constr_liste_tas_aux d p h =
if p = 0 then h
else
let h' = ajoute_vect d (p-1) h in
constr_liste_tas_aux d (p-1) h
;;
let constr_liste_tas_vect d = constr_liste_tas d (vect_length d) [];;
III.
III.
III.I - Écrire la fonction extraire_vect : tasbin list
III.
III.
III.L - Quelle est la complexité temporelle de tri_lisse_vect pour un tableau déjà trié ?
III.
^1 On peut en fait démontrer que cette complexité est unΘ(n) mais cela n'est pas demandé.
Questions fréquentes
3 questionsSur quels chapitres porte le sujet d'option informatique Centrale MP 2016 ?Afficher ou masquer la section
Questions fréquentes
3 questionsSur quels chapitres porte le sujet d'option informatique Centrale MP 2016 ?
Le sujet porte sur les structures de données (listes, arbres, tas binaires), l'algorithmique du tri, l'analyse de complexité et la programmation récursive en Caml.
Le sujet d'option informatique Centrale MP 2016 est-il difficile ?
Le rapport indique un sujet correctement compris par les candidats, avec un niveau global satisfaisant et une fraction notable de copies excellentes, ce qui en fait un sujet de difficulté moyenne bien progressif.
Quelles erreurs le jury a-t-il le plus relevées sur ce sujet d'option informatique Centrale MP 2016 ?
Le jury relève des confusions dans l'usage des types construits, des incohérences entre la description d'un algorithme et sa transcription en Caml, et une mauvaise gestion de la récursivité terminale.
Pas de description pour le moment
