Complexité Algorithmique

publicité
Master SIS – Maîtrise – M1
Complexité Algorithmique
Feuille de TD n° 1 - Septembre 2011
Rappel des techniques de base en algorithmique, structures de données,
complexité et preuves.
Problème 1 – Preuves et complexité
Nous considérons la fonction C donnée ci-dessous où les valeurs de a et de b en entrée sont supposées
entières et positives :
i n t C_Quoi(int a, int b)
{
i n t x,y,m;
m = 0 ; x = a ; y = b ;
while ( y > 0 )
{
if ( y % 2 == 1 ) { y = y-1 ; m = m+x; }
else { x = 2*x ; y = y/2; }
}
return(m) ;
}
Question 1. Donnez les spécifications externes de la fonction C_Quoi.
Question 2. Démontrez la validité de la fonction C_Quoi par la méthode des invariants en considérant les
spécifications externes que vous aurez données dans la question 1.
Question 3. Donnez la complexité de la fonction C_Quoi en justifiant votre évaluation.
Question 4. Donnez une implémentation récursive de la fonction C_Quoi dont vous démontrerez la
validité. Analysez sa complexité.
Problème 2 – Représentation de suites par des structures de données.
Dans ce problème, on cherche à représenter des suites dans des tables, des listes ou des arbres.
Partie 1
On considère ici la suite entière un définie par u0 = 1 et un = 4. un-1 + 3 pour n > 0.
Question 1. Ecrire une fonction C qui prend en entrée un entier n puis renvoie la valeur de un et
construit également un tableau t de type tabent, et de sorte que t[i] = ui pour tout entier i vérifiant 0
≤ i ≤ n. Le type tabent est défini par :
typedef
int
tabent[MAX] ;
où MAX est une valeur supposée supérieure à tout entier n considéré. Le tableau résultant du calcul
figurera dans les arguments de la fonction C. Evaluez la complexité en temps de votre fonction.
Question 2. On se pose la même question que ci-dessus en considérant non plus un tableau, mais une
liste simplement chaînée. Ecrire une fonction C qui prend en entrée un entier n puis renvoie la valeur de
un et construit également une liste simplement chaînée dont les éléments seront définis avec les types C
suivant :
typedef struct ch
{
int valeur;
struct ch *suivant;
} chainon;
typedef chainon *liste;
La fonction C aura dans ses arguments, un pointeur vers la liste ainsi construite et celle-ci organisée de
telle sorte que le premier élément de la liste contienne u0, et le ième contienne ui pour tout i vérifiant 0 ≤ i ≤
n. Il faudra que cette liste soit construite sans recours à un tableau. Evaluez la complexité en temps de
votre fonction.
Question 3. On se pose la même question exactement que dans la question 1, mais on considère
maintenant la suite de Fibonacci qui est définie ici par f0 = 1, f1 = 1 et fn = fn-1 + fn-2 pour n > 1. Ecrire
une fonction C qui prend en entrée un entier n puis renvoie la valeur de fn et affecte les cases d’un
tableau t de type tabent, et de sorte que t[i] = fi pour tout i vérifiant 0 ≤ i ≤ n. Evaluez la complexité
en temps de votre fonction.
Question 4. On se pose la même question exactement que dans la question 3, mais en construisant cette
fois-ci une liste simplement chaînée. Evaluez la complexité en temps de votre fonction.
Partie 2
Pour le cas où la suite récurrente entière considérée est définie pour un un sur la base de plusieurs
prédécesseurs (comme pour le cas de la suite de Fibonacci), on peut utiliser une structure de données
arborescente pour représenter ses éléments. Par exemple, pour représenter la suite de Fibonacci, on
peut utiliser un arbre binaire. On aurait ainsi, pour représenter f0 et f1 des arbres restreints chacun à un
unique nœud dont les valeurs seraient respectivement 1 et 1. Pour représenter fn avec n > 1, il faudrait
un arbre dont la racine aurait pour valeur fn et possédant deux fils, le fils gauche représentant fn-1, et le
fils droit fn-2. Par exemple, f3 serait représenté par un arbre dont la racine aurait pour valeur f3, et dont le
fils gauche de valeur f2, aurait lui-même pour fils gauche le nœud représentant f1, et pour fils droit le
nœud représentant f0. De plus, la racine aurait pour fils droit le nœud représentant f1. Dans cette partie,
nous représenterons des arbres binaires avec les types C suivants :
typedef struct triplet
{ int valeur;
struct triplet *fg;
struct triplet *fd;
} noeud;
typedef noeud *arbre;
Question 5. On se pose la même question que précédemment en considérant cette fois-ci un arbre
binaire. Ecrire une fonction C appelée fibabr qui prend en entrée un entier n puis renvoie la valeur de
fn et construit un arbre binaire défini comme ci-dessus, c’est-à-dire dont les nœuds contiennent les
valeurs correspondantes dans la suite de Fibonacci. La fonction C aura donc dans ses arguments, un
pointeur vers l’arbre ainsi construit. Evaluez la complexité en temps et en espace de votre fonction.
Question 6. Démontrez que le nombre de feuilles de l’arborescence binaire construite dans la question 6,
est précisément égal à la valeur de fn .
Question 7. Ecrire une fonction C qui prend en entrée un arbre binaire résultant de l’exécution de la
fonction fibabr avec un entier n en entrée qui fournit la valeur de fn comme résultat, sans utiliser le
champ valeur du type noeud mais par le simple dénombrement des feuilles de l’arborescence (cf.
exploitation de la question 6).
Question 8. Il existe une structure de données, abusivement appelée « arbres cousus » qui permet un
gain significatif d’espace pour la représentation d’arbres quand certains nœuds sont répétés. Le type C
permettant de les représenter est identique au précédent, la différence se présentant au niveau de
l’exploitation qui en est faite. Dans un tel arbre, si deux nœuds possèdent un fils de même nature, alors
un seul nœud sera utilisé pour le représenter et les deux nœuds se partageront ce même fils. Par exemple,
l’arbre associé à f3 serait représenté par une structure de données ne contenant que 4 nœuds. La racine
qui aurait pour valeur f3 , aurait un fils gauche de valeur f2 , et un fils droit représentant f1 . Le nœud
représentant f2 aurait pour fils droit le nœud représentant f0 , alors qu’il aurait comme fils gauche (de
valeur f1 ) le nœud fils droit de la racine. Ecrire une fonction C qui prend en entrée un entier n, construit
un arbre cousu défini comme indiqué ci-dessus, puis renvoie la valeur de fn. La fonction C aura donc
dans ses arguments, un pointeur vers l’arbre cousu ainsi construit. Evaluez la complexité en temps et en
espace de votre fonction.
Problème 3 – Arbres binaires de recherche.
On considère dans ce problème des arbres binaires de recherche (ABR). Il est rappelé que dans un ABR,
les valeurs (supposées différentes) sont mémorisées de telle sorte que pour un nœud donné contenant
une valeur x, les valeurs contenues dans les nœuds situés dans sa sous-arborescence gauche sont
inférieures à x et celles contenues dans les nœuds situés dans sa sous-arborescence droite sont
supérieures à x. Pour représenter les ABR, vous utiliserez les types C suivants :
typedef struct noeud {
int elt; /* valeur du noeud */
struct noeud *fg, *fd; /* pointeurs vers fils */
} triplet;
t y p e d e f triplet *ABR;
A partir de la question 2, vous donnerez systématiquement une analyse de la complexité de la fonction
programmée.
Question 1. Dessinez un arbre binaire de recherche contenant les valeurs 3, 15, 26, 7, 0, 45, 12, 19, 14 et
8. Indiquez l’ordre dans lequel seront visités les nœuds par un parcours post-fixé de l’arbre.
Question 2. Ecrivez une fonction en C appelée correct qui prend en entrée un pointeur a de type ABR
et qui vérifie si ce pointeur pointe bien vers une structure de données représentant un arbre binaire de
recherche (on doit vérifier que la position des valeurs mémorisées vérifient la propriété de base des ABR).
Question 3. Ecrivez une fonction en C appelée présent qui prend en entrée un pointeur a de type ABR
et un entier x et qui a pour résultat 1 si x figure dans l’arbre binaire de recherche pointé par a. Le résultat
vaudra 0 sinon.
Question 4. Ecrivez une fonction en C appelée maximum qui prend en entrée un pointeur a de type ABR
et qui a pour résultat la plus grande valeur mémorisée dans l’arbre binaire de recherche pointé par a. Le
résultat vaudra 0 sinon.
Question 5. Ecrivez une fonction en C appelée egaux qui prend en entrée deux pointeurs a et b de type
ABR, qui pointent vers des arbres binaires de recherche et qui vérifie si ces arbres binaires de recherche
contiennent exactement les mêmes entiers. Dans ce cas le résultat de la fonction vaudra 1, et dans le cas
contraire, il vaudra 0.
Question 6. Ecrivez une fonction en C appelée identique qui prend en entrée deux pointeurs a et b de
type ABR, qui pointent vers des arbres binaires de recherche et qui vérifie si ces arbres binaires de
recherche contiennent exactement les mêmes entiers et ont exactement la même forme. Dans ce cas le
résultat de la fonction vaudra 1, et dans le cas contraire, il vaudra 0.
Téléchargement