Devoir Surveillé informatique MP, PC, PSI

publicité
NOM :
Classe :
Devoir Surveillé informatique MP, PC, PSI
L’utilisation des calculatrices n’est pas autorisée pour cette épreuve.
Le langage de programmation choisi est Python.
L’espace laissé pour les réponses est suffisant (sauf si vous utilisez ces feuilles comme
brouillon, ce qui est fortement déconseillé).
Comparaison de deux méthodes de tri
L’emploi de la méthode .sort() et de la fonction max prédéfinies dans Python n’est pas autorisé.
Partie I/ Le tri par insertion
On considère un tableau T de valeurs numériques représenté par une liste de valeurs numériques non triée.
On définit une méthode intuitive pour trier la liste par ordre croissant : le tri insertion dont le pseudo-code est
donné ci-dessous.
procédure tri_insertion(tableau T)
pour i de la deuxième valeur à la dernière valeur du tableau
x ← T[i]
j←i
tant que j > 0 et T[j - 1] > x
T[j] ← T[j – 1]
j←j–1
fin tant que
T[j] ← x
fin pour
fin procédure
1- Proposer un script écrit en langage Python qui effectue le tri par insertion d’un tableau de valeurs.
1/8
2- On dit que ce tri est « en place ». Que signifie ce terme et en quoi est-ce un aspect positif de cette méthode
de tri ?
3- Donner, en justifiant brièvement, la complexité en temps de cet algorithme dans le meilleur et dans le pire
des cas en fonction de n = len(T).
4- Quelle est la meilleure performance que l’on puisse attendre en termes de complexité temporelle pour un
tri par comparaison ? À ce titre, le tri par insertion est-il performant ?
Partie II/ Le tri par tas
Le but de cette partie est l’écriture d’un algorithme de tri de tableaux basé sur la notion de tas.
Les questions se suivent logiquement, mais beaucoup sont indépendantes. Les fonctions définies dans les
différentes questions peuvent être utilisées dans les questions suivantes même si celles-ci n’ont pas été écrites
complètement.
A- Des fonctions élémentaires pour se déplacer dans un tableau
indice
T(indice)
0
5
1
2
2
6
3
0
4
1
5
9
6
1
7
5
(a) Vision tabulaire
(b) Vision arborescente (arbre binaire)
Figure 1 – Deux vues différentes d’un même tableau
La figure 1 montre qu’un même tableau peut être dessiné avec des cases contigües, ou bien avec des cases
dispersées dans une arborescence. Avec la vue contigüe, on utilise généralement une variable i qui parcourt
les indices du tableau.
2/8
Précisons maintenant un peu plus les termes désignant les différents composants d’un arbre binaire.
Tout d’abord, chaque élément d’un arbre se nomme un nœud. Les nœuds sont reliés les uns aux autres par
des relations d’ordre ou de hiérarchie. Ainsi on dira qu’un nœud possède un père, c’est-à-dire un nœud qui
lui est supérieur dans cette hiérarchie. Il possède éventuellement un ou deux fils.
Il existe un nœud qui n’a pas de père, on l’appelle alors la racine de l’arbre. Un nœud qui n’a pas de fils est
appelé feuille ou nœud externe. Tout autre nœud de l’arbre sera appelé nœud interne.
Voici donc un schéma qui résume les différents composants d’un arbre :
(a) Structure d’un arbre binaire
(b) Arbre binaire complet
Figure 2 – Généralités sur les arbres
Le nombre de niveaux total, autrement dit la distance entre la feuille la plus éloignée et la racine, est appelé
hauteur de l’arbre. Le niveau d’un nœud est appelé profondeur. Sur la figure 2b, l’arbre a une hauteur égale à
2.
On appellera arbre binaire complet tout arbre qui est localement complet, dont chaque nœud interne possède
deux fils et dont toutes les feuilles ont la même profondeur. Dans ce type d’arbre, on peut exprimer le
nombre de nœuds n de l’arbre en fonction de la hauteur h : n = 2h+1 -1.
Avec la vue arborescente, on peut évidemment utiliser une variable i qui parcourt les indices du tableau, mais
on utilise également trois fonctions qui permettent de suivre les liens bidirectionnels (réels ou virtuels) de
l’arborescence :
- gauche(indice) représente les liens pointillés du haut vers le bas de l’arborescence.
Par exemple, dans la figure 1b, gauche(1)=3, gauche(4)=9 et gauche(2)=5.
- droite(indice) représente les liens en trait plein du haut vers le bas de l’arborescence.
Par exemple, dans la figure 1b, droite(1)=4, droite(3)=8 et droite(0)=2.
- pere(indice) représente les liens du bas vers le haut de l’arborescence.
Par exemple, dans la figure 1b, pere(4)=1, pere(7)=3 et pere(2)=0. Par contre pere(0) n’est pas
défini, et sa valeur (null,-1,0,…) importe peu car jamais utilisée dans cet exercice.
Voici un programme Python possible pour la fonction gauche(indice), de complexité temporelle en O(1) :
def gauche (indice) :
'' '' '' Retourne le lien à gauche vers le bas de l'arborescence '' '' ''
return 2*indice+1
5- Écrire un programme Python droite(indice) qui retourne un entier d tel qu’il existe un lien en trait plein du
haut vers le bas reliant les indices i à d. La complexité en temps doit être en O(1).
3/8
6- Écrire un programme Python pere(indice) qui retourne un entier p tel qu’il existe un lien du bas vers le
haut reliant les indices i à p. La complexité en temps doit être en O(1).
B- Construction d’un tas à partir d’un tableau.
Un tas est un tableau d’entiers tel que pour tous les indices i strictement positifs, la valeur de T[i] est
inférieure ou égale à celle de T[pere(i)].
Le but de cette partie de l’exercice est d’effectuer la transformation représentée par la figure 3.
(a) Vue arborescente du tableau initial
(b) Tas obtenu par construction
Figure 3 – Construction d’un tas
7- Écrire un programme Python estunTas(T) qui retourne True si le tableau T est un tas, False sinon. La
complexité en temps doit être en O(n) avec n = len(T).
4/8
8- Pour un tableau T d’éléments T[i], on définit une valeur limite telle que 0  i  limite et
limite  longueur(T). Définir une fonction maximum(T,i,limite) qui retourne l’indice (inférieur à limite) de
la plus grande des trois valeurs T[i], T[gauche(i)] et T[droite(i)]. Si on note iMax la valeur retournée par la
fonction maximum(T,i,limite), iMax a donc les propriétés suivantes :
iMax  limite   iMax  i, gauche(i ), droite(i )
T [iMax]  T [i ]


 gauche(i )  limite  T [iMax]  T [ gauche(i )]
droite(i )  limite  T [iMax]  T [droite(i )]
En cas de valeurs égales, le plus petit indice est retourné. Par exemple sur la figure 3a, maximum(T,0,8) = 2,
maximum(T,2,8) = 5, maximum(T,3,8) = 7 et maximum(T,3,7) = 3. La complexité en temps doit être en
O(1).
9- Soit l’algorithme récursif écrit en langage Python suivant :
def entasserRecursif (T,i,limite) :
iMax = maximum(T,i,limite)
if iMax!= i :
echange (T,i,iMax )
entasserRecursif (T,iMax,limite)
avec :
def echange (T, i, j) :
aux = T[i]
T[i] = T[j]
T[j] = aux
Justifier pourquoi cet algorithme est récursif.
10- Compléter l’arborescence avec les valeurs du tableau après l’appel entasserRecursif(T,0,8).
(a) Avant entasser
(b) Après entasser
Figure 4 – Entasser(T,0,8)
5/8
11- Proposer un algorithme non récursif écrit en langage Python que l’on nommera entasser(T,i,limite)
équivalent à l’algorithme récursif entasserRecursif(T,i,limite).
12- Donner les complexités en temps dans le meilleur et dans le pire des cas de l’algorithme
entasser(T,i,limite) en fonction de n = len(T).
13- L’algorithme entasser(T,i,limite) échange des valeurs du tableau de haut en bas, en suivant une branche
de l’arborescence. Cela a pour effet de faire descendre des petites valeurs, et de faire monter les grandes
valeurs. Il est donc possible de construire un tas, en itérant cet algorithme sur les indices décroissants du
tableau.
En utilisant entasserRecursif(T,i,limite) ou entasser(T,i,limite), proposer un algorithme construireTas(T),
écrit en langage Python, qui transforme un tableau en un tas.
14- Donner la complexité en temps dans le meilleur et dans le pire des cas de l’algorithme construireTas(T)
en fonction de n = len(T).
6/8
15- Tri d’un tas.
Le but de cette partie de l’exercice est d’effectuer la transformation représentée par la figure 5.
(a) Tas initial
(b) Vue arborescente du tableau trié
Figure 5 – Tri d’un tas
a) Dans un tas, la valeur maximale est à la racine de l’arborescence, donc en T[0]. Dans le tableau trié, cette
valeur doit être en T[len(T)-1]. Il suffit donc d’échanger ces deux valeurs pour progresser vers la solution.
Une fois cet échange fait, si l’on exclut la dernière valeur du tableau, le tas est peu changé. En fait
entasser(T,0,len(T)-1) va créer un nouveau tas pour les valeurs du tableau dont les indices sont inférieurs à
limite = len(T)-1. Il suffit donc d’itérer ces deux étapes (échange, entasser) pour trier un tas.
Écrire un algorithme trierTas(T) en langage Python qui transforme un tas en un tableau trié en ordre
croissant.
b) Donner la complexité en temps dans le meilleur et dans le pire des cas de l’algorithme trierTas(T) en
fonction de n = len(T).
c) Écrire un algorithme triParTas(T) en langage Python qui trie un tableau d’entiers T en construisant
d’abord un tas, puis en le triant.
7/8
d) Donner la complexité en temps dans le meilleur et dans le pire des cas l’algorithme triParTas(T) en
fonction de n = len(T). Commenter.
FIN DU SUJET
8/8
Téléchargement