
Meilleur cas = pire cas = O(m∗n∗p).
Note : En théorie, on peut faire plus efficace. Trouver un algorithme optimal de multiplication de matrices
est un problème de recherche ouvert à l’heure actuelle.
2 Structures de données
Une structure de données est un assemblage de types simples (dans notre cas listes, booléens, entiers,
flottants, chaînes) permettant de représenter un objet plus complexe (exemples : un tuple d’une relation, un
arbre généalogique, un graphe de villes avec leurs distances)
Nous allons créer une structure de données pour représenter un vecteur creux, c’est-à-dire un vecteur conte-
nant beaucoup de fois la valeur 0.
Un vecteur creux se comporte de la même manière qu’un vecteur classique (np.array à 1 dimension), mais
est optimisé pour le cas où le vecteur contient une majorité de valeurs nulles 1.
2.1 Préliminaire : opérations sur les listes et complexité
Dans la suite, nous allons utiliser des listes Python pour représenter une structure de données. Voici un
rappel des opérations et de leur complexité.
Soient xune liste de longueur net iun entier. eest une valeur quelconque.
—x[i] : accès à l’élément i,Θ(1)
—x.append(e) : insertion en fin de liste, Θ(1)
—x=x+[e]: construction d’une nouvelle liste contenant les éléments de xsuivis de e,Θ(n)
—x.insert(i, e) : insertion de een position i,Θ(nombre d’éléménts déplacés) = Θ(n−i), ou Θ(1) si
aucun élément n’est déplacé.
—e = x.pop(i) : suppression de l’élément à la position i,Θ(nombre d’éléments déplacés) = Θ(n−i), ou
Θ(1) si aucun élément n’est déplacé.
Hypothèse simplificatrice : on suppose que l’espace mémoire alloué par Python pour stocker les éléments de
la liste est suffisant pour ajouter un élément en fin. L’insertion en fin est donc en Θ(1) et l’insertion en milieu
de liste en Θ(nombre d’éléments déplacés)2.
2.2 Structure de données = implantation + constructeurs + testeurs + sélecteurs
+ modifieurs (10mn)
L’utilisateur de notre structure de données ne devrait pas avoir à se soucier de la représentation interne de
notre vecteur creux (on parle de structure de données "opaque" ou "abstraite").
Lorsqu’on doit choisir une structure de données, avant de se poser la question « Comment représenter un
vecteur creux en Python ? » (c’est-à-dire « Quelle structure de données utiliser ? »), il faut se poser la question
«Pourquoi représenter un vecteur creux ? », c’est à dire « Quel jeu de fonctions proposer à l’utilisateur ? ».
Nous allons fournir à l’utilisateur des fonctions permettant de modifier ou de lire cette structure de données.
Pour notre vecteur creux, ces opérations sont les mêmes que pour np.array, mais leur implémentation et
leur complexité sera différente.
Ces fonctions sont de plusieurs types :
— les constructeurs permettent de créer une structure de données vide ou de créer une structure de données
à partir d’un fichier ou d’une autre structure de données (exemple : np.array(...)) ;
— les testeurs et sélecteurs permettent d’interroger la structure de données sans la modifier (est-ce qu’un
élément est présent dans l’ensemble ? combien d’éléments contient l’ensemble ?) (exemples : x.shape(),
x[...]) ;
— les modifieurs permettent de modifier la structure de données (ajouter un élément, en supprimer un,
extraire un élément quelconque) (exemple : x[...] = ...) ;
— des opérateurs qui prennent en entrée plusieurs instances de la structure et renvoient une valeur fonction
de ces entrées ;
1. Par exemple, l’instruction np.zeros(10**10) (créer un vecteur de taille 1010) provoque une MemoryError sur la machine de
l’auteur de ce document, alors que créer un vecteur creux (en utilisant coo_matrix de la bibliothèque scipy ou la version faite
maison) de taille 101000 ne pose aucun problème.
2. En réalité, la complexité de ces opérations fait appel à la notion de complexité amortie qui n’est pas au programme, cf.
https://wiki.python.org/moin/TimeComplexity
2