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);