TP 7

publicité
Lycée Alphonse Daudet MPSI
Année 2016-2017 TP Option Info
Option Informatique
TP
Partie I :
n◦ 7
: structures de données
Implémentations du type File.
Dans cette partie, on étudie trois implémentations possibles de la structure de données file.
Dans tout calcul de complexité de cette partie, c'est bien le nombre d'opérations standard que l'on demande d'estimer
(comparaisons, opérations arithmétiques, opérations structurelles des autres structures de données...).
Exercice 1. Implémentation naïve.
Dans cet exercice, on implémente le type le comme un simple alias 1 du type liste :
type 'a file == 'a list;;
1. Implémenter les opérations structuelles sur les les pour cette implémentation des les, et donner sans démonstration leur complexité.
Il y a deux solutions possibles... n'en donner qu'une !
2. Quel est le problème ?
Exercice 2. Implémentation par couples de listes.
Dans cet exercice, on implémente la version immuable type le à l'aide d'un couple de listes.
type 'a file == ('a list) * ('a list);;
La première liste est constituée des premiers éléments de la le, classés par ordre de priorité. L'élément en tête de cette
première liste est donc le premier élément de la le, etc. La seconde liste est constituée des derniers éléments de la
le, conservés dans l'ordre inverse de leur priorité. L'ajout d'un nouvel élément se fait donc par un cons en tête de la
seconde liste.
Il n'y a pas unicité de la représentation : par exemple, la le 1, 2, 3 (où 1 est le premier élément de la le et 3
le dernier) peut aussi bien être représentée par ([1;2;3],[]), ([1;2],[3]), ([1],[3;2]) et ([],[3;2;1]). Seule la
le vide a une unique représentation : ([],[]). Ainsi, l'égalité entre deux les n'est pas l'égalité naturelle de Caml,
sauf l'égalité à la le vide.
1. Implémenter les opérations structuelles sur les les pour cette implémentation des les, et donner sans démonstration leur complexité.
2. Quels sont les avantages et les inconvénients de cette implémentation du type file ?
Exercice 3. Implémentation avec un vecteur circulaire.
Dans cet exercice, on implémente la version modiable du type le à l'aide d'une structure impérative. On va faire
émerger pas-à-pas sa description.
On commence par une restriction : en pratique, lorsque l'on utilise des les, la longueur de la le est bornée. Par
exemple, le parcours en largeur d'un arbre binaire (complet) de profondeur n nécessite 2 une le de longueur au plus
2n+1 . On décide donc que la longueur d'une le ne pourra pas excéder une longueur maximale fmax que l'on aecte
une bonne fois pour toute à une valeur grande (ou moins grande suivant nos besoins), par exemple :
let fmax = 4194303;;
On considèrera uniquement des les de longueur au plus fmax.
On commence par dénir une le comme un tableau de longueur fmax, accompagné de deux indices remarquables
debut et fin : les éléments de la le, par ordre de priorité, seront alors les éléments de ce tableau compris entre debut
et fin-1. Ajouter un élément à la le revient donc à aecter la coordonnée d'indice fin du tableau (puis à incrémenter
fin), alors que la queue de la le est simplement obtenue en incrémentant debut.
Sur l'illustration suivante, on a fmax=12.
1. La construction type truc == machin indique à Caml que truc est simplement un autre nom pour machin.
2. Pourquoi, au fait ?
Lycée Alphonse Daudet MPSI
Année 2016-2017 TP Option Info
L'intérêt d'une telle implémentation est de permettre que toutes les opérations structurelles soient toujours en temps
constant.
Le problème de cette implémentation est que la longueur maximale de la le ne reste pas fmax : elle diminue à chaque
décalage de debut. On propose alors l'astuce suivante, qui consiste à imaginer que le tableau n'est pas rectiligne mais
circulaire, ce qui revient à considérer les indices du tableau modulo fmax. Une fois encore, illustration pour fmax=12.
Comme dans l'exercice précédent, la représentation n'est pas unique. Ainsi, la le 1, 2, 3 (où 1 est le premier élément de
la le et 3 le dernier) peut aussi bien être représentée par [|1;2;3;0;0;0;0;0;0;0;0;0|] avec debut=0 et fin=3, que
par [|0;0;1;2;3;0;0;0;0;0;0;0|] avec debut=2 et fin=5, ou par [|2;3;0;0;0;0;0;0;0;0;0;1|] avec debut=11
et fin=2, ou même par [|2;3;42;42;42;42;666;42;42;43;42;1|] avec debut=11 et fin=2.
Mais il reste un problème : pour debut=fin=3, quelle le représente [|1;2;3;4;5;6;7;8;9;10;11;12|] ? S'agit-il de
la le vide ou de la le 4, 5, 6, 7, 9, 9, 10, 11, 12, 1, 2, 3 ? Une solution pour régler le problème est d'indiquer non seulement
les indices debut et fin, mais aussi si la le est vide ou pas.
Ouf ! Finalement, on en est venu à l'implémentation suivante :
type 'a file = {contenu : 'a vect;
mutable debut : int; mutable fin : int;
mutable vide : bool};;
1. Implémenter les opérations structuelles sur les les pour cette implémentation des les. Vous devrez faire en sorte
qu'elles soient toutes de complexité constante.
2. Discuter de l'intérêt de cette implémentation par rapport aux deux précédentes.
Partie II :
Implémentation du parcours en largeur.
Dans cette partie on s'eorcera de n'utiliser que les opérations structurelles lorsqu'on manipulera des les.
Dans tout calcul de complexité de cette partie, c'est bien le nombre d'opérations structurelles que l'on demande
d'estimer, indépendamment de leur coût réel qui dépend de l'implémentation de la structure de données. On ne demande
pas de compter les autres opérations (comparaisons, opérations arithmétiques, ...) que l'on pourra considérer comme
"gratuites".
On rappelle l'algorithme de parcours en largeur d'un arbre binaire : on visite d'abord la racine, puis les feuilles ou
n÷uds de ses ls (feuilles et n÷uds de profondeur 1), puis les feuilles ou n÷uds des ls de ses ls (feuilles et n÷uds de
profondeur 2), etc.
On retourne alors la le des étiquettes de chaque feuille ou n÷ud dans l'ordre de parcours.
Lycée Alphonse Daudet MPSI
Année 2016-2017 TP Option Info
L'algorithme du parcours en largeur utilise une le de stockage et une seconde le pour le résultat. On procède comme
suit : on ajoute l'arbre de départ dans la le de stockage initialement vide puis, tant que la le de stockage n'est pas
vide, on retire le premier élément de la le, si c'est une feuille, on ajoute l'étiquette de cette feuille à la le résultat, si
c'est un n÷ud, on ajoute l'étiquette de ce n÷ud à la le résultat, et on ajoute successivement le ls gauche puis le ls
droit de ce n÷ud à la le de stockage.
On pourra utiliser l'implémentation des ('a,'a)-arbres vue en DS :
type 'a arbre =
| vide
| noeud of 'a * 'a arbre * 'a arbre;;
Ou celle vue en TP si on préfère.
1. Faire marcher la fonction parcours_en_largeur : 'a arbre -> 'a file qui eectue un parcours en largeur,
et qu'on a décrite en cours.
2. Quelle est sa complexité ? On ne comptera que les opérations structurelles sur les les nécessaires pour une exécution. On ne comptera pas les autres opérations (pas même les opérations structurelles sur les arbres).
3.
Rappels :
i/ Qu'apporte le fait d'utiliser une le pour le résultat plutôt qu'une liste ?
ii/ Qu'apporte le fait d'utiliser une le pour le traitement (le stockage) plutôt qu'une liste ?
iii/ Finalement, en quoi cet exemple ilustre-t-il l'intérêt de la structure de donnée file ?
Téléchargement