Algorithmique Structures de données : Les tableaux

Transcription

Algorithmique Structures de données : Les tableaux
1 de 1
Algorithmique
Structures de données : Les
tableaux
Florent Hivert
Mél : [email protected]
Page personnelle : http://www.lri.fr/˜hivert
2 de 1
Algorithmes et structures de données
La plupart des bons algorithmes fonctionnent grâce à une méthode
astucieuse pour organiser les données. On distingue quatre grandes
classes de structures de données :
Les structures de données séquentielles (tableaux) ;
Les structures de données linéaires (liste chaînées) ;
Les arbres ;
Les graphes.
Structures séquentielles : les tableaux
Structures
séquentielles : les
tableaux
3 de 1
Structures séquentielles : les tableaux
4 de 1
Structure de donnée séquentielle (tableau)
En anglais : array, vector.
Définition
Un tableau est une structure de donnée T qui permet de stocker
un certain nombre d’éléments T [i] repérés par un index i. Les
tableaux vérifient généralement les propriétés suivantes :
tous les éléments ont le même type de base ;
le nombre d’éléments stockés est fixé ;
l’accès et la modification de l’élément numéro i est en temps
constant Θ(1), indépendant de i et du nombre d’éléments
dans le tableau.
Structures séquentielles : les tableaux
5 de 1
Tableau en C
On suppose déclaré un type elem pour les éléments.
Espace mémoire nécessaire au stockage d’un élément exprimé
en mots mémoire (octets en général) : sizeof(elem).
Retenir
définition statique :
elem t[taille];
définition dynamique en deux temps (déclaration, allocation) :
#include <stdlib.h>
elem *t;
t = (elem*) malloc(taille*sizeof(elem));
l’adresse de t[i] est noté t + i. Calculée par
Addr(t[i]) = Addr(t[0]) + sizeof(elem) ∗ i
Structures séquentielles : les tableaux
5 de 1
Tableau en C
On suppose déclaré un type elem pour les éléments.
Espace mémoire nécessaire au stockage d’un élément exprimé
en mots mémoire (octets en général) : sizeof(elem).
Retenir
définition statique :
elem t[taille];
définition dynamique en deux temps (déclaration, allocation) :
#include <stdlib.h>
elem *t;
t = (elem*) malloc(taille*sizeof(elem));
l’adresse de t[i] est noté t + i. Calculée par
Addr(t[i]) = Addr(t[0]) + sizeof(elem) ∗ i
Structures séquentielles : les tableaux
5 de 1
Tableau en C
On suppose déclaré un type elem pour les éléments.
Espace mémoire nécessaire au stockage d’un élément exprimé
en mots mémoire (octets en général) : sizeof(elem).
Retenir
définition statique :
elem t[taille];
définition dynamique en deux temps (déclaration, allocation) :
#include <stdlib.h>
elem *t;
t = (elem*) malloc(taille*sizeof(elem));
l’adresse de t[i] est noté t + i. Calculée par
Addr(t[i]) = Addr(t[0]) + sizeof(elem) ∗ i
Structures séquentielles : les tableaux
5 de 1
Tableau en C
On suppose déclaré un type elem pour les éléments.
Espace mémoire nécessaire au stockage d’un élément exprimé
en mots mémoire (octets en général) : sizeof(elem).
Retenir
définition statique :
elem t[taille];
définition dynamique en deux temps (déclaration, allocation) :
#include <stdlib.h>
elem *t;
t = (elem*) malloc(taille*sizeof(elem));
l’adresse de t[i] est noté t + i. Calculée par
Addr(t[i]) = Addr(t[0]) + sizeof(elem) ∗ i
Structures séquentielles : les tableaux
Opérations de base (1)
Hypothèses :
tableau de taille max_taille alloué
éléments 0 ≤ i < taille ≤ max_taille initialisés
Retenir (Opérations de base : accès)
accès au premier élément : Θ(1)
accès à l’élément numéro i : Θ(1)
accès au dernier élément : Θ(1)
6 de 1
Structures séquentielles : les tableaux
7 de 1
Opérations de base (2)
Hypothèses :
tableau de taille max_taille alloué
éléments 0 ≤ i < taille ≤ max_taille initialisés
Retenir (Opérations de base : insertions)
insertion d’un élément au début : Θ(taille)
insertion d’un élément en position i : Θ(taille −i) ⊂ O(taille)
insertion d’un élément à la fin : Θ(1)
Structures séquentielles : les tableaux
Opérations de base (3)
Hypothèses :
tableau de taille max_taille alloué
éléments 0 ≤ i < taille ≤ max_taille initialisés
Retenir (Opérations de base : suppressions)
suppression d’un élément au début : Θ(taille)
suppression d’un élément en position i :
Θ(taille −i) ⊂ O(taille)
suppression d’un élément à la fin : Θ(1)
8 de 1
Structures séquentielles : les tableaux
9 de 1
Algorithmes de base
Hypothèses :
tableau de taille taille alloué et initialisé
Retenir (Algorithmes de base)
accumulation d’une opération sur les éléments du tableaux
(par exemple : somme, produit, maximum, etc.) : Θ(taille)
nombre d’opération.
cas particulier ; comptage du nombre d’éléments qui vérifie une
certaine condition : Θ(taille) tests de la condition
recherche d’une occurence, d’un élement qui vérifie une
certaine condition : O(taille) tests de la condition
Structures séquentielles : les tableaux
Problème de la taille maximum
On essaye d’insérer un élément dans un tableau où
taille = max_taille
Il n’y a plus de place disponible.
Comportements possibles :
Erreur (arrêt du programme, exception)
Ré-allocation du tableau avec recopie, coût : Θ(taille)
10 de 1
Structures séquentielles : les tableaux
Problème de la taille maximum
On essaye d’insérer un élément dans un tableau où
taille = max_taille
Il n’y a plus de place disponible.
Comportements possibles :
Erreur (arrêt du programme, exception)
Ré-allocation du tableau avec recopie, coût : Θ(taille)
10 de 1
Structures séquentielles : les tableaux
11 de 1
Ré-allocation (2)
On ajoute 1 par 1 n éléments. On suppose que l’on réalloue une
case supplémentaire à chaque débordement. Coût (nombre de
copies d’éléments) :
n+
n−1
X
i=1
i=
n(n + 1)
∈ Θ(n2 )
2
Remarque : si on alloue des blocs de taille b, en notant k = d bn e :
n+
k−1
X
i=1
bi = n+b
k(k − 1)
(bk)2
n2
≈
=
∈ Θ(n2 )
2
b
b
La vitesse est divisée par b mais la complexité reste la même.
Structures séquentielles : les tableaux
11 de 1
Ré-allocation (2)
On ajoute 1 par 1 n éléments. On suppose que l’on réalloue une
case supplémentaire à chaque débordement. Coût (nombre de
copies d’éléments) :
n+
n−1
X
i=1
i=
n(n + 1)
∈ Θ(n2 )
2
Remarque : si on alloue des blocs de taille b, en notant k = d bn e :
n+
k−1
X
i=1
bi = n+b
k(k − 1)
(bk)2
n2
≈
=
∈ Θ(n2 )
2
b
b
La vitesse est divisée par b mais la complexité reste la même.
Structures séquentielles : les tableaux
12 de 1
Ré-allocation par doublement de taille
Retenir (Solution au problème de la réallocation)
À chaque débordement on ré-alloue dK · max_taillee où K > 1 est
une constante fixée (par exemple K = 2).
Si l’on veut à la fin un tableaux de n éléments, le nombre de copies
d’éléments (en comptant la copie dans le tableau) est environ
C=
m
X
i=1
Ki =
K m+1 − 1
∈ Θ(K m )
K −1
où m est le nombre de ré-allocations c’est-à-dire le plus petit entier
tel que K m ≥ n, soit m = dlogK (n)e. On a donc
n ≤ K m < Kn
Finalement le coût est Θ(n).
Structures séquentielles : les tableaux
13 de 1
Nombre moyen de copies
Selon la valeur de K , la constante de complexité varie de manière
importante : Le nombre de recopies d’éléments est
approximativement
C=
m
X
i=1
Ki =
K m+1 − 1
K
≈
n
K −1
K −1
Quelques valeurs :
K
K
K −1
1.01 1.1 1.2 1.5 2 3
4
5
10
101 11 6
3 2 1.5 1.33 1.25 1.11
Interprétation : Si l’on augmente la taille de 10% à chaque étape,
chaque nombre sera recopié en moyenne 11 fois. Si l’on double la
taille à chaque étape, chaque nombre sera en moyenne recopié deux
fois.
Structures séquentielles : les tableaux
13 de 1
Nombre moyen de copies
Selon la valeur de K , la constante de complexité varie de manière
importante : Le nombre de recopies d’éléments est
approximativement
C=
m
X
i=1
Ki =
K m+1 − 1
K
≈
n
K −1
K −1
Quelques valeurs :
K
K
K −1
1.01 1.1 1.2 1.5 2 3
4
5
10
101 11 6
3 2 1.5 1.33 1.25 1.11
Interprétation : Si l’on augmente la taille de 10% à chaque étape,
chaque nombre sera recopié en moyenne 11 fois. Si l’on double la
taille à chaque étape, chaque nombre sera en moyenne recopié deux
fois.
Structures séquentielles : les tableaux
14 de 1
Bilan
Retenir (Nombre de copies)
Dans un tableau de taille n, coût de l’ajout d’un élément dans le
pire des cas :
Coût en temps ≈ n,
Coût en espace ≈ (K − 1)n .
En moyenne, sur un grand nombre d’éléments ajoutés :
Coût en temps ≈
K
,
K −1
Coût en espace ≈ K .
On dit que l’algorithme travaille en temps constant amortis
(Constant Amortized Time (CAT) en anglais).
Structures séquentielles : les tableaux
15 de 1
Compromis Espace/Temps
Quand K augmente, la vitesse augmente mais la place mémoire
gaspillée (≈ (K − 1)n) augmente aussi. Le choix de la valeur de K
dépend donc du besoin de vitesse par rapport au coût de la
mémoire.
Retenir
C’est une situation très classique : dans de nombreux problèmes, il
est possible d’aller plus vite en utilisant plus de mémoire.
Exemple : on évite de faire plusieurs fois les même calculs en
stockant le résultat.
Structures séquentielles : les tableaux
15 de 1
Compromis Espace/Temps
Quand K augmente, la vitesse augmente mais la place mémoire
gaspillée (≈ (K − 1)n) augmente aussi. Le choix de la valeur de K
dépend donc du besoin de vitesse par rapport au coût de la
mémoire.
Retenir
C’est une situation très classique : dans de nombreux problèmes, il
est possible d’aller plus vite en utilisant plus de mémoire.
Exemple : on évite de faire plusieurs fois les même calculs en
stockant le résultat.
Structures séquentielles : les tableaux
15 de 1
Compromis Espace/Temps
Quand K augmente, la vitesse augmente mais la place mémoire
gaspillée (≈ (K − 1)n) augmente aussi. Le choix de la valeur de K
dépend donc du besoin de vitesse par rapport au coût de la
mémoire.
Retenir
C’est une situation très classique : dans de nombreux problèmes, il
est possible d’aller plus vite en utilisant plus de mémoire.
Exemple : on évite de faire plusieurs fois les même calculs en
stockant le résultat.
Structures séquentielles : les tableaux
16 de 1
En pratique . . .
On utilise void *realloc(void *ptr, size_t size);
Extrait des sources du langage Python
Fichier listobject.c, ligne 41-91 :
/* This over-allocates proportional to the list size, making room
* for additional growth. The over-allocation is mild, but is
* enough to give linear-time amortized behavior over a long
* sequence of appends() in the presence of a poorly-performing
* system realloc().
* The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
*/
new_allocated = (newsize >> 3) + (newsize < 9 ? 3 : 6);

Documents pareils