Vous êtes sur la page 1sur 213

PRESSES UNIVERSITAIRES DE BRUXELLES

Programmation
UNIVERSITÉ LIBRE DE BRUXELLES, UNIVERSITÉ D’EUROPE

Transparents, Volume 2

Thierry MASSART

D/2011/0098/267
4e édition – Tirage 2011-12/1-S
INFO-F-101_B

™INFO-F-101_B"¨
En application de l’article 23 du Décret du 31 mars 2004, l’auteur consent à mettre son support de cours obligatoire
àConformément
la disposition àdes étudiants
la loi régulièrement
du 30 juin inscrits.
1994, modifiée par Toute
la loi dureproduction,
22 mai 2005, communication au public,
sur le droit d'auteur, commercialisation
toute reproduction
ou autre forme d’exploitation de ce fascicule est interdite et pourra faire l’objet de poursuites disciplinaires,
partielle ou totale du contenu de cet ouvrage –par quelque moyen que ce soit– est formellement
civiles et/ou pénales, conformément à la loi du 30 juin 1994 relative au droit d’auteur et aux droits voisins, interdite.
Toute citation ne peut être autorisée que par l'auteur et l’éditeur. La demande doit être adressée exclusivement
à moins qu’il s’agisse d’une reproduction effectuée par un étudiant régulièrement inscrit, dans le cadre de son usage
aux Presses
strictement Universitaires
privé de ne
d’étudiant, qui Bruxelles
porte pasa.s.b.l., avenue
préjudice Paul Héger 42,
à l’exploitation 1000de
normale Bruxelles,
l’oeuvre.
Tél. : 02-649 97 80 –TouteFax :citation
02-647 doit mentionner
79 62 le nom de l’auteur et
– Http//:www.ulb.ac.be/pub la source.
– E-mail : mpardoen@ulb.ac.be
Tél. : 02-649 97 80 – Fax : 02-647 79 62 – Http://www.ulb.ac.be/pub – E-mail : mpardoen@ulb.ac.be
« A l’obéissance à des règles imposées par autrui, nous opposons l’adhésion à
une conviction que l’on s’est formée soi-même. »

Chaïm Perelman (1912-1984)


Professeur de philosophie à l’ULB, résistant, a
remis à l’honneur l’étude de l’argumentation.
Le label FSC : la garantie d’une gestion responsable des forêts
Les Presses Universitaires de Bruxelles s’engagent !
Les P.U.B. impriment depuis de nombreuses années les syllabus sur du papier recyclé. Les différences de qualité
constatées au niveau des papiers recyclés ont cependant poussé les P.U.B. à se tourner vers un papier de meilleure
qualité et surtout porteur du label FSC.
Sensibles aux objectifs du FSC et soucieuses d’adopter une démarche responsable, les P.U.B. se sont conformé aux
exigences du FSC et ont obtenu en avril 2010 la certification FSC (n° de certificat COC spécifique aux P.U.B. : CU-COC-
809718-HA).
Seule l’obtention de ce certificat autorise les P.U.B. à utiliser le label FSC selon des règles strictes. Fortes de leur
engagement en faveur de la gestion durable des forêts, les P.U.B. souhaitent dorénavant imprimer tous les syllabus
sur du papier certifié FSC. Le label FSC repris sur les syllabus vous en donnera la garantie.
Qu’est-ce que le FSC ? Quelles garanties ?
FSC signifie “Forest Stewardship Council” ou Le système FSC repose également sur la traçabilité du
“Conseil de bonne gestion forestière”. Il s’agit d’une produit depuis la forêt certifiée dont il est issu jusqu’au
organisation internationale, non gouvernementale, consommateur final. Cette traçabilité est assurée
à but non lucratif qui a pour mission de promouvoir par le contrôle de chaque maillon de la chaîne de
dans le monde une gestion responsable et durable commercialisation/transformation du produit (Chaîne
des forêts. de Contrôle : Chain of Custody – COC). Dans le cas du
Se basant sur dix principes et critères généraux, papier et afin de garantir cette traçabilité, aussi bien le
le FSC veille à travers la certification des forêts au producteur de pâte à papier que le fabricant de papier,
respect des exigences sociales, écologiques et le grossiste et l’imprimeur doivent être contrôlés.
économiques très poussées sur le plan de la gestion Ces contrôles sont effectués par des organismes de
forestière. certification indépendants.

Les 10 principes et critères du FSC 6. Les fonctions écologiques et la diversité biologique de la


forêt doivent être protégées.
1. L’aménagement forestier doit respecter les lois nationales, 7. Un plan d’aménagement doit être écrit et mis en œuvre.
les traités internationaux et les principes et critères du FSC. Il doit clairement indiquer les objectifs poursuivis et les
2. La sécurité foncière et les droits d’usage à long terme sur moyens d’y parvenir.
les terres et les ressources forestières doivent être claire- 8. Un suivi doit être effectué afin d’évaluer les impacts de la
ment définis, documentés et légalement établis. gestion forestière.
3. Les droits légaux et coutumiers des peuples indigènes à la 9. Les forêts à haute valeur pour la conservation doivent être
propriété, à l’usage et à la gestion de leurs territoires et de maintenues (par ex : les forêts dont la richesse biologique
leurs ressources doivent être reconnus et respectés. est exceptionnelle ou qui présentent un intérêt culturel ou
4. La gestion forestière doit maintenir ou améliorer le bien- religieux important). La gestion de ces forêts doit toujours
être social et économique à long terme des travailleurs fo- être fondée sur un principe de précaution.
restiers et des communautés locales. 10. Les plantations doivent compléter les forêts naturelles,
5. La gestion forestière doit encourager l’utilisation efficace mais ne peuvent pas les remplacer. Elles doivent réduire
des multiples produits et services de la forêt pour en ga- la pression exercée sur les forêts naturelles et promouvoir
rantir la viabilité économique ainsi qu’une large variété de leur restauration et leur conservation. Les principes de 1 à
prestations environnementales et sociales. 9 s’appliquent également aux plantations.

Le label FSC apposé sur des produits


en papier ou en bois apporte la garan-
tie que ceux-ci proviennent de forêts
gérées selon les principes et critères
FSC.
® FSC A.C. FSC-SECR-0045

FSC, le label du bois et du papier responsable

Plus d’informations ?
www.fsc.be
A la recherche de produits FSC ?
www.jecherchedufsc.be

Cette page d’information n’est pas comptée dans le prix du syllabus.


MASSART Thierry Programmation Transparents - Volume 2

Chapitre 8. Tuples

Contenu du chapitre

1 Un tuple est une séquence immuable

2 Fonctions avec un nombre variable d’arguments

3 Listes et tuples

4 Dictionnaires et tuples

5 Comparaison de tuples

6 Glossaire

Programmation (Chap. 8) Tuples 1 / 29

Un tuple est une séquence immuable

Un tuple est une séquence


immuable

Programmation (Chap. 8) Tuples 2 / 29

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 1


MASSART Thierry Programmation Transparents - Volume 2

Un tuple est une séquence immuable

Un tuple est une séquence immuable

Un tuple est une séquence de valeurs. On définit un tuple en


spécifiant les valeurs, séparées par des virgules, entre parenthèses1
>>> t = (’a’, ’b’, ’c’)
>>> type(t)
<type ’tuple’>
>>> print t
(’a’, ’b’, ’c’)
>>> t = ’a’, ’b’
>>> type(t)
<type ’tuple’>

1
L’utilisation des parenthèses est facultative, mais est souvent utilisée pour plus de
clarté.
Programmation (Chap. 8) Tuples 3 / 29

Un tuple est une séquence immuable

Un tuple est une séquence immuable

les valeurs peuvent être de n’importe quel type


les valeurs sont indicées par des entiers
les tuples sont immuables
>>> t = (’a’, 1, [1, 2])
>>> t[0]
’a’
>>> t[-1]
[1, 2]
>>> t[0] = ’b’
TypeError: ’tuple’ object does not support item assignment

Programmation (Chap. 8) Tuples 4 / 29

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 2


MASSART Thierry Programmation Transparents - Volume 2

Un tuple est une séquence immuable

Un tuple est une séquence immuable

Pour distinguer syntaxiquement une unique valeur et un tuple


contenant une seule valeur, on doit utiliser une virgule après la seule
valeur du tuple.
>>> t = (’a’,)
>>> print type(t), t
<type ’tuple’> (’a’,)
>>> t = ’a’,
>>> print type(t), t
<type ’tuple’> (’a’,)
>>> t = (’a’)
>>> print type(t), t
<type ’str’> a

Ce sont donc les virgules (plutôt que les parenthèses) qui définissent
syntaxiquement les tuples. Celles-ci sont utilisées par convention (car proche
de la notation mathématique des n-uples).

Programmation (Chap. 8) Tuples 5 / 29

Un tuple est une séquence immuable

Un tuple est une séquence immuable


Pour créer un tuple vide, on peut utiliser des parenthèses ne
contenant rien ou la fonction tuple sans arguments.

Si l’argument de la fonction tuple est une séquence, elle retourne un


tuple avec les éléments de celle-ci2 .
>>> x = ()
>>> y = tuple()
>>> print x, y, type(x), type(y)
() () <type ’tuple’> <type ’tuple’>
>>> s = ’abc’
>>> t = range(3)
>>> d = {’a’:1, ’b’:2, ’c’:3}
>>> tuple(s)
(’a’, ’b’, ’c’)
>>> tuple(t)
(0, 1, 2)
>>> tuple(d)
(’a’, ’c’, ’b’)

2
Dans le cas des dictionnaires, retourne un tuple des clefs.
Programmation (Chap. 8) Tuples 6 / 29

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 3


MASSART Thierry Programmation Transparents - Volume 2

Un tuple est une séquence immuable

Comparaisons des différents types de séquences (vues)

type mutable ? indices type des valeurs syntaxe


str non entiers caractères ’abc’
list oui entiers tous types [’a’, ’b’, ’c’]
dict oui type hachable tous types {’a’:4, ’b’:2}
tuple non entiers tous types (a, b, c)

Les opérateurs applicables à une séquence immuable avec des indices


entiers sont également disponibles sur les tuples, par ex. : accès d’un
élément via [.], tranches (slices), concaténation (+), opérateur *, fonction
len, boucles for, opérateur in.
>>> t = (’a’, ’B’, ’C’)
>>> t[0] = ’A’
TypeError: ’tuple’ object does not support item assignment
>>> t = (’A’,) + t[1:]
>>> t
(’A’, ’B’, ’C’)

Programmation (Chap. 8) Tuples 7 / 29

Un tuple est une séquence immuable

Assignation de tuples

Un tuple de variables peut se placer à gauche d’une assignation. Il


faut que le membre droit soit un tuple d’expressions ayant le même
nombre d’éléments.
>>> a = 17
>>> b = 21
>>> (a, b) = (b, a)
>>> print a, b
21 17
>>> a, b = 1 * 2, 3 + 4
>>> print a, b
2 7
>>> a, b = 1, 2, 3
ValueError: too many values to unpack

Programmation (Chap. 8) Tuples 8 / 29

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 4


MASSART Thierry Programmation Transparents - Volume 2

Un tuple est une séquence immuable

Assignation de tuples

De manière plus générale, le membre droit de l’assignation peut être


n’importe quel type de séquence, du moment que le nombre
d’éléments correspond.
>>> a, b, c = ’xyz’
>>> print a
x
>>> uname, domain = ’jack@umons.ac.be’.split(’@’)
>>> print uname
jack
>>> print domain
umons.ac.be
>>> key1, key2 = {’a’:12, ’b’:21}
>>> print key2
b

Programmation (Chap. 8) Tuples 9 / 29

Un tuple est une séquence immuable

Assignation de tuples

Strictement parlant, une fonction ne peut retourner qu’une seule


valeur. Utiliser un tuple comme valeur de retour permet de contourner
cette limitation.
def min_max(t):
min = max = t[0]
for k in t:
if k > max:
max = k
if k < min:
min = k
return min, max

>>> print min_max(range(3,8))


(3, 7)

Programmation (Chap. 8) Tuples 10 / 29

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 5


MASSART Thierry Programmation Transparents - Volume 2

Fonctions avec un nombre variable d’arguments

Fonctions avec un nombre


variable d’arguments

Programmation (Chap. 8) Tuples 11 / 29

Fonctions avec un nombre variable d’arguments

Fonctions avec un nombre variable d’arguments


Une fonction peut prendre un nombre variable d’arguments. Par
exemple les fonctions min et max peuvent être appliquées sur des
séquences, mais également sur un nombre indéfini d’arguments.
>>> help(max)
Help on built-in function max in module __builtin__:

max(...)
max(iterable[, key=func]) -> value
max(a, b, c, ...[, key=func]) -> value

With a single iterable argument, return its largest item.


With two or more arguments, return the largest argument.
>>> max(3, 8, 5)
8
>>> a = 2
>>> b = 5
>>> c = 1
>>> d = 2
>>> max(a, b, c, d)
5
>>> min(a,b)
2
Programmation (Chap. 8) Tuples 12 / 29

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 6


MASSART Thierry Programmation Transparents - Volume 2

Fonctions avec un nombre variable d’arguments

Fonctions avec un nombre variable d’arguments

Pour définir ses propres fonctions avec un nombre indéfini


d’arguments, on utilise un paramètre dont le nom commence par ∗.
Ce paramètre rassemble (gather) les arguments en un tuple, quelque
soit leur nombre.
def f(*args):
print type(args), ’of length’, len(args)
print args

>>> f(2, 4)
<type ’tuple’> of length 2
(2, 4)
>>> f(’hello’,2.0,[1, 2])
<type ’tuple’> of length 3
(’hello’, 2.0, [1, 2])

Remarque : il ne peut y avoir qu’un seul paramètre de ce type, et il


doit se trouver à la fin de la liste des paramètres.

Programmation (Chap. 8) Tuples 13 / 29

Fonctions avec un nombre variable d’arguments

Fonctions avec un nombre variable d’arguments

On peut combiner l’opérateur gather ∗ avec des paramètres requis et


optionnels (avec valeurs par défaut).

On commence par les paramètres requis, puis les optionnels et enfin


le paramètre de taille variable.
def g(required, optional=0, *args):
print ’required:’, required
print ’optional:’, optional
print ’others:’, args

Programmation (Chap. 8) Tuples 14 / 29

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 7


MASSART Thierry Programmation Transparents - Volume 2

Fonctions avec un nombre variable d’arguments

Fonctions avec un nombre variable d’arguments

>>> g()
TypeError: g() takes at least 1 argument (0 given)
>>> g(1)
required: 1
optional: 0
others: ()
>>> g(1,2)
required: 1
optional: 2
others: ()
>>> g(1,2,3)
required: 1
optional: 2
others: (3,)
>>> g(1,2,3,4)
required: 1
optional: 2
others: (3, 4)

Programmation (Chap. 8) Tuples 15 / 29

Fonctions avec un nombre variable d’arguments

Opérateur ∗: gather and scatter


Gather : Utilisé devant le nom d’un paramètre dans l’en-tête d’une
fonction, l’opérateur ∗ rassemble les arguments en un tuple.

Scatter : L’opérateur ∗ appliqué sur un tuple lors de l’appel à une


fonction permet également de faire l’inverse : il sépare le tuple en une
séquence d’arguments.
>>> help(divmod)
Help on built-in function divmod in module __builtin__:

divmod(...)
divmod(x, y) -> (div, mod)

Return the tuple ((x-x%y)/y, x%y). Invariant: div*y + mod == x.


>>> t = (7,3)
>>> divmod(t)
TypeError: divmod expected 2 arguments, got 1
>>> divmod(*t)
(2, 1)

Programmation (Chap. 8) Tuples 16 / 29

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 8


MASSART Thierry Programmation Transparents - Volume 2

Listes et tuples

Listes et tuples

Programmation (Chap. 8) Tuples 17 / 29

Listes et tuples

Listes et tuples

La fonction zip prend deux ou plusieurs séquences en paramètre et


les fusionne en une liste de tuples. Si le nombre d’éléments des
séquences n’est pas le même, la liste retournée a une taille
équivalente à la taille de la plus petite séquence.
>>> s = ’abc’
>>> t = range(3)
>>> zip(s, t)
[(’a’, 0), (’b’, 1), (’c’, 2)]
>>> zip(’Joe’,’Jack’)
[(’J’, ’J’), (’o’, ’a’), (’e’, ’c’)]

Programmation (Chap. 8) Tuples 18 / 29

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 9


MASSART Thierry Programmation Transparents - Volume 2

Listes et tuples

Listes et tuples

L’assignation de tuples peut être utilisée dans une boucle for


>>> t = zip(’abc’,range(3))
>>> for letter, number in t:
print number, letter

0 a
1 b
2 c

Programmation (Chap. 8) Tuples 19 / 29

Listes et tuples

Listes et tuples

Combiner zip, une boucle for et l’assignation de tuples permet de


traverser en une fois plusieurs séquences.

Par exemple, la fonction suivante retourne vrai ssi une valeur est
identique dans les deux séquences pour au moins un indice.
def has_match(t1, t2):
for x, y in zip(t1,t2):
if x == y:
return True
return False
Remarque : le code ci-dessus est beaucoup plus intuitif que celui-ci :
for i in range(min(len(t1), len(t2))):
if t1[i] == t2[i]:

Programmation (Chap. 8) Tuples 20 / 29

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 10


MASSART Thierry Programmation Transparents - Volume 2

Listes et tuples

Listes et tuples

Si l’on doit traverser une séquence et travailler à la fois sur les indices
et les valeurs, on peut utiliser la fonction enumerate qui, à chaque
itération, assigne un tuple (indice, valeur), pour chaque élément de la
séquence.
>>> for index, value in enumerate(’abc’):
print index, value

0 a
1 b
2 c

Programmation (Chap. 8) Tuples 21 / 29

Dictionnaires et tuples

Dictionnaires et tuples

Programmation (Chap. 8) Tuples 22 / 29

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 11


MASSART Thierry Programmation Transparents - Volume 2

Dictionnaires et tuples

Dictionnaires et tuples
Les dictionnaires ont une méthode items qui retourne une liste de
tuples : chaque tuple est une paire clef-valeur (dont l’ordre est
indéterminé).

Inversément, on peut utiliser une liste de tuples pour initialiser un


dictionnaire avec la fonction dict. Combiner dict et zip permet de
créer de manière concise un dictionnaire.
>>> d = {’a’:0, ’b’:1, ’c’:2}
>>> t = d.items()
>>> print t
[(’a’, 0), (’c’, 2), (’b’, 1)]
>>> t = [(’d’, 3), (’e’, 4), (’f’, 5)]
>>> d = dict(t)
>>> d
{’e’: 4, ’d’: 3, ’f’: 5}
>>> d = dict(zip(’abc’,range(3)))
>>> print d
{’a’: 0, ’c’: 2, ’b’: 1}

Programmation (Chap. 8) Tuples 23 / 29

Dictionnaires et tuples

Dictionnaires et tuples

La méthode update des dictionnaires prend une liste de tuples en


argument et les ajoute au dictionnaire existant.

En combinant items, l’assignation de tuples et une boucle for, on


peut facilement traverser les clefs et les valeurs d’un dictionnaire.
>>> d[’a’] = ’0’
>>> d.update(zip(’bcd’,range(1,4)))
>>> for key, val in d.items():
print key, val

a 0
c 2
b 1
d 3

Programmation (Chap. 8) Tuples 24 / 29

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 12


MASSART Thierry Programmation Transparents - Volume 2

Dictionnaires et tuples

Dictionnaires et tuples

Il est fréquent d’utiliser des tuples comme clefs d’un dictionnaire


(principalement car on ne peut pas utiliser des listes).

Par exemple, on peut utiliser un dictionnaire pour stocker un répertoire


téléphonique dont les clefs sont un tuple (nom, prénom).
>>> tel = {}
>>> tel[’Baroud’,’Bill’] = ’065-37-07-56’
>>> tel[’II’,’Albert’] = ’02-256-89-14’
>>> tel[’Poelvoorde’,’Benoit’] = ’081-23-89-65’
>>> for last, first in tel:
print first, last, tel[last, first]

Benoit Poelvoorde 081-23-89-65


Bill Baroud 065-37-07-56
Albert II 02-256-89-14

Programmation (Chap. 8) Tuples 25 / 29

Comparaison de tuples

Comparaison de tuples

Programmation (Chap. 8) Tuples 26 / 29

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 13


MASSART Thierry Programmation Transparents - Volume 2

Comparaison de tuples

Comparaison de tuples

Les opérateurs de comparaison fonctionnent avec les tuples (et les


autres séquences). Python commence par comparer le premier
élément de chaque séquence. S’ils sont égaux, il compare l’élément
suivant et ainsi de suite jusqu’à trouver le premier élément qui diffère.
La comparaison s’effectue sur ce premier élément qui diffère, les
autres éléments sont ignorés, quelles que soient leurs valeurs.

On parle d’ordre lexicographique (car c’est le même comportement


pour des chaînes de caractères).
>>> (0, 1, 2) < (0, 3, 1)
True
>>> (0, 1, 20000000) < (0, 3, 1)
True
>>> (0, 1, 2) < (0, 1, 1)
False

Programmation (Chap. 8) Tuples 27 / 29

Glossaire

Glossaire

Programmation (Chap. 8) Tuples 28 / 29

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 14


MASSART Thierry Programmation Transparents - Volume 2

Glossaire

Glossaire

tuple : séquence immuable d’éléments.


gather : opération qui consiste à assembler un argument de taille variable en un tuple
(opérateur ∗ dans l’en-tête d’une définition de fonction).
scatter : opération qui consiste à traiter une séquence comme une liste d’arguments
(opérateur ∗ lors de l’appel d’une fonction).

Programmation (Chap. 8) Tuples 29 / 29

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 15


MASSART Thierry Programmation Transparents - Volume 2

Chapitre 9. Récursivité

Contenu du chapitre

1 Concept de récursivité

2 Glossaire

Programmation (Chap. 9) Récursivité 1 / 33

Concept de récursivité

Concept de récursivité

Programmation (Chap. 9) Récursivité 3 / 33

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 16


MASSART Thierry Programmation Transparents - Volume 2

Concept de récursivité

Preuve par récurrence


La récursivité est une notion classique en mathématiques quand il
s’agit de prouver une assertion (preuve par récurrence) : cf. cours
Mathématiques élémentaires

Rappel du principe

A prouver : assertion S (n) = f (n) ∀n ≥ n0

Base : preuve de l’assertion pour certaines petites valeurs de n (n0


par exemple)

Etape de récurrence : on suppose que c’est vrai pour n ≤ k (avec


k ≥ n0 ), prouver que c’est également vrai pour n = k + 1

Remarque : on peut aussi supposer que c’est vrai pour n ≤ k − 1 et prouver


que c’est vrai pour n = k. C’est ce qu’on fera ici : souvent plus intuitif d’un
point de vue informatique.

Programmation (Chap. 9) Récursivité 4 / 33

Concept de récursivité

Preuve par récurrence

Exemple

Soit S (n) = ∑ni=1 i. Prouver que

n(n + 1)
S (n) = ∀n ≥ 1 (1)
2

Base : si n = 1, alors S(1) = 1 par définition et (1) devient

1×2
1= ,
2
ce qui est exact.

Programmation (Chap. 9) Récursivité 5 / 33

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 17


MASSART Thierry Programmation Transparents - Volume 2

Concept de récursivité

Preuve par récurrence


Etape de récurrence : on suppose que (1) est vraie pour
1 ≤ n ≤ k − 1, prouvons que c’est également vrai pour n = k. Par
hypothèse de récurrence,

(k − 1)k
S (k − 1) = (2)
2
Comment exprimer S (k ) en fonction de S (k − 1) ? Par définition,
k k −1
S (k ) = ∑ i = ∑ i + k = S(k − 1) + k .
i =1 i =1

Par (2), il s’ensuit


(k − 1)k
S (k ) = + k.
2
Or,
(k − 1)k k2 − k 2k k2 + k k (k + 1)
+k = + = = .
2 2 2 2 2
Programmation (Chap. 9) Récursivité 6 / 33

Concept de récursivité

Preuve par récurrence

On a donc bien
n(n + 1)
S (n) = ∀n ≥ 1.
2

Qu’avons nous du déterminer pour utiliser une preuve par


récurrence ?
(base) résoudre un cas simple
(étape de récurrence)
� supposer le résultat vrai pour n ≤ k − 1
� exprimer S (k ) en fonction de S (k − 1) (ou en fonction d’autres
valeurs plus petites que k − 1)
� retrouver le résultat en combinant les deux points ci-dessus

Programmation (Chap. 9) Récursivité 7 / 33

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 18


MASSART Thierry Programmation Transparents - Volume 2

Concept de récursivité

Récursivité

En informatique, on peut également utiliser la récursivité

Problème
Comment calculer la somme des n premiers nombres entiers ?

Solution 1 : utiliser la formule prouvée précédemment (formule d’Euler)

>>> def sum_1_to_n(n):


return n * (n + 1) / 2

>>> print sum_1_to_n(10)

55

Programmation (Chap. 9) Récursivité 8 / 33

Concept de récursivité

Récursivité

Solution 2 : utiliser la récursivité, c’est à dire la possibilité pour une


fonction de s’appeler elle même.
(base) Si n = 1, alors la somme S (n) vaut 1
(étape de récurrence) Si n > 1, S (n) = S (n − 1) + n
Ces deux éléments suffisent pour résoudre le problème de manière
récursive

>>> def sum_rec(n):


if n == 1:
return 1
else:
return sum_rec(n - 1) + n

>>> print sum_rec(10)

55

Programmation (Chap. 9) Récursivité 9 / 33

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 19


MASSART Thierry Programmation Transparents - Volume 2

Concept de récursivité

Récursivité
Intuition : le programmeur paresseux

Supposons que Bill soit paresseux et qu’il doive résoudre un problème


complexe. Il préfère laisser à Bob le soin de faire le gros du travail. Si
Bill est capable de
résoudre le problème pour les cas les plus simples;
récupérer le travail de Bob et réaliser un travail de taille un peu
plus grande à partir de celui-ci;
alors le problème peut être résolu par récursivité, de manière simple.

Exemple : Bill doit calculer S (n) = ∑ni=1 i. Bill peut demander à Bob de
calculer S (k ) pour k = 2, 3, . . . n − 1. Il lui reste simplement à
calculer S (1),
déterminer comment calculer S (n) en utilisant S (n − 1) (ou
d’autres valeurs calculées par Bob).

Programmation (Chap. 9) Récursivité 10 / 33

Concept de récursivité

Récursivité

Problème
Ecrire une fonction qui calcule la factorielle d’un nombre entier.

Programmation (Chap. 9) Récursivité 11 / 33

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 20


MASSART Thierry Programmation Transparents - Volume 2

Concept de récursivité

Récursivité

Problème
Ecrire une fonction qui calcule la factorielle d’un nombre entier.

Une solution récursive :


(base) 0! = 1
(étape de récurrence) n! = n × (n − 1)!

Programmation (Chap. 9) Récursivité 11 / 33

Concept de récursivité

Récursivité
En Python :

>>> def factorial(n):


if n == 0:
return 1
else:
recurse = factorial(n - 1)
result = n * recurse
return result

>>> print factorial(4)

24
Plus simplement :

>>> def factorial(n):


if n == 0:
return 1
else:
return n * factorial(n - 1)

Programmation (Chap. 9) Récursivité 12 / 33

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 21


MASSART Thierry Programmation Transparents - Volume 2

Concept de récursivité

Récursivité

Problème
Ecrire une fonction qui calcule an , si n ≥ 1.

Programmation (Chap. 9) Récursivité 13 / 33

Concept de récursivité

Récursivité

Problème
Ecrire une fonction qui calcule an , si n ≥ 1.

Une solution récursive :


(base) a1 = a
(étape de récurrence) an = an−1 × a

Programmation (Chap. 9) Récursivité 13 / 33

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 22


MASSART Thierry Programmation Transparents - Volume 2

Concept de récursivité

Récursivité

En Python :

>>> def expo(a, n):


if n == 1:
return a
else:
return expo(a,n - 1) * a

>>> print expo(3,3), (3**3)

27 27

Programmation (Chap. 9) Récursivité 14 / 33

Concept de récursivité

Rappels concernant les fonctions


Au chapitre 3, nous avons vu que :
une fonction peut être utilisée dans une expression comme une valeur
(= sa valeur de retour)
une fonction peut en appeler une autre
La récursivité n’est rien d’autre que l’application de ses deux faits sur la
fonction elle-même !
def expo(a, n):
if n == 1:
return a
else:
return expo(a,n - 1) * a

Pour comprendre et valider une fonction récursive, il s’agit


d’accepter qu’un appel récursif retourne la bonne valeur
de s’assurer que le(s) cas de base(s) soi(en)t bien géré(s)
de s’assurer que chaque appel récursif réduise la taille du problème
jusqu’à atteindre un cas de base
Programmation (Chap. 9) Récursivité 15 / 33

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 23


MASSART Thierry Programmation Transparents - Volume 2

Concept de récursivité

Récursivité

Les exemples précédents montrent que l’on peut résoudre très


simplement certains problèmes si on arrive à :
résoudre le problème pour les cas les plus simples;
récupérer une solution pour une certaine taille du problème et
construire une solution de plus grande taille à partir de celui-ci.

Dans beaucoup de cas, ces deux tâches sont plus faciles à résoudre
qu’une solution “générale” ou “complète” au problème !

Mais. . . cela peut paraître “magique” : on peut avoir du mal à


“accepter” que cela fonctionne aussi simplement.

On peut analyser le flot d’exécution pour s’en convaincre.

Programmation (Chap. 9) Récursivité 16 / 33

Concept de récursivité

Récursivité et flot d’exécution

Analysons le flot d’exécution d’un appel à factorial(3)

>>> def factorial(n):


if n == 0:
return 1
else:
recurse = factorial(n - 1)
result = n * recurse
return result

>>> print factorial(3)

Programmation (Chap. 9) Récursivité 17 / 33

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 24


MASSART Thierry Programmation Transparents - Volume 2

Concept de récursivité

Analyse du flot d’exécution lors de l’appel factorial(3)


• appel à factorial(n → 3) : comme n �= 0, appel à factorial(n → 2)
• détail de l’appel à factorial(n → 2) :

Programmation (Chap. 9) Récursivité 18 / 33

Concept de récursivité

Analyse du flot d’exécution lors de l’appel factorial(3)


• appel à factorial(n → 3) : comme n �= 0, appel à factorial(n → 2)
• détail de l’appel à factorial(n → 2) :
appel à factorial(n → 2) : comme n �= 0, appel à factorial(n → 1)

détail de l’appel à factorial(n → 1) :

Programmation (Chap. 9) Récursivité 18 / 33

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 25


MASSART Thierry Programmation Transparents - Volume 2

Concept de récursivité

Analyse du flot d’exécution lors de l’appel factorial(3)


• appel à factorial(n → 3) : comme n �= 0, appel à factorial(n → 2)
• détail de l’appel à factorial(n → 2) :
appel à factorial(n → 2) : comme n �= 0, appel à factorial(n → 1)

détail de l’appel à factorial(n → 1) :


� appel à factorial(n → 1) : comme n �= 0, appel à
factorial(n → 0)
� détail de l’appel à factorial(n → 0) :

Programmation (Chap. 9) Récursivité 18 / 33

Concept de récursivité

Analyse du flot d’exécution lors de l’appel factorial(3)


• appel à factorial(n → 3) : comme n �= 0, appel à factorial(n → 2)
• détail de l’appel à factorial(n → 2) :
appel à factorial(n → 2) : comme n �= 0, appel à factorial(n → 1)

détail de l’appel à factorial(n → 1) :


� appel à factorial(n → 1) : comme n �= 0, appel à
factorial(n → 0)
� détail de l’appel à factorial(n → 0) :
� appel à factorial(n → 0) : comme n = 0, retourne 1

Programmation (Chap. 9) Récursivité 18 / 33

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 26


MASSART Thierry Programmation Transparents - Volume 2

Concept de récursivité

Analyse du flot d’exécution lors de l’appel factorial(3)


• appel à factorial(n → 3) : comme n �= 0, appel à factorial(n → 2)
• détail de l’appel à factorial(n → 2) :
appel à factorial(n → 2) : comme n �= 0, appel à factorial(n → 1)

détail de l’appel à factorial(n → 1) :


� appel à factorial(n → 1) : comme n �= 0, appel à
factorial(n → 0)
� détail de l’appel à factorial(n → 0) :
� appel à factorial(n → 0) : comme n = 0, retourne 1

� valeur de retour de factorial(n → 0) est 1 (recurse → 1), puis


multipliée par 1 (result → 1) : appel à factorial(n → 1)
terminé, retourne 1

Programmation (Chap. 9) Récursivité 18 / 33

Concept de récursivité

Analyse du flot d’exécution lors de l’appel factorial(3)


• appel à factorial(n → 3) : comme n �= 0, appel à factorial(n → 2)
• détail de l’appel à factorial(n → 2) :
appel à factorial(n → 2) : comme n �= 0, appel à factorial(n → 1)

détail de l’appel à factorial(n → 1) :


� appel à factorial(n → 1) : comme n �= 0, appel à
factorial(n → 0)
� détail de l’appel à factorial(n → 0) :
� appel à factorial(n → 0) : comme n = 0, retourne 1

� valeur de retour de factorial(n → 0) est 1 (recurse → 1), puis


multipliée par 1 (result → 1) : appel à factorial(n → 1)
terminé, retourne 1

valeur de retour de factorial(n → 1) est 1 (recurse → 1), puis


multipliée par 2 (result → 2) : appel à factorial(n → 2) terminé,
retourne 2

Programmation (Chap. 9) Récursivité 18 / 33

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 27


MASSART Thierry Programmation Transparents - Volume 2

Concept de récursivité

Analyse du flot d’exécution lors de l’appel factorial(3)


• appel à factorial(n → 3) : comme n �= 0, appel à factorial(n → 2)
• détail de l’appel à factorial(n → 2) :
appel à factorial(n → 2) : comme n �= 0, appel à factorial(n → 1)

détail de l’appel à factorial(n → 1) :


� appel à factorial(n → 1) : comme n �= 0, appel à
factorial(n → 0)
� détail de l’appel à factorial(n → 0) :
� appel à factorial(n → 0) : comme n = 0, retourne 1

� valeur de retour de factorial(n → 0) est 1 (recurse → 1), puis


multipliée par 1 (result → 1) : appel à factorial(n → 1)
terminé, retourne 1

valeur de retour de factorial(n → 1) est 1 (recurse → 1), puis


multipliée par 2 (result → 2) : appel à factorial(n → 2) terminé,
retourne 2

• valeur de retour de factorial(n → 2) est 2 (recurse → 2), puis multipliée


par 3 (result → 6) : appel à factorial(n → 3) terminé, retourne 6
Programmation (Chap. 9) Récursivité 18 / 33

Concept de récursivité

Analyse du flot d’exécution lors de l’appel factorial(3)


Diagramme de pile :

factorial n 3
6
recurse 2
result 6
2

factorial n 2
recurse 1
result 2
1

factorial n 1
recurse 1
result 1
1

factorial n 0

Note : recurse et result n’existent pas dans le dernier appel car leur
portée est limitée au else

Programmation (Chap. 9) Récursivité 19 / 33

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 28


MASSART Thierry Programmation Transparents - Volume 2

Concept de récursivité

Récursivité et flot d’exécution

Problème
Soit la fonction récursive suivante, comment prédire ce qu’elle va
faire ? Essayez de décrire le flot d’exécution après un appel à
post_count(3).

>>> def post_count(n):


if n <= 0:
print ’Boum’
else:
print n
post_count(n - 1)
>>> post_count(3)

Programmation (Chap. 9) Récursivité 20 / 33

Concept de récursivité

Analyse du flot d’exécution lors de l’appel post_count(3)


• appel à post_count avec n → 3 : comme n est plus grand que 0, affiche la
valeur 3, puis appelle post_count avec n → 2
• détail de l’appel à post_count avec n → 2 :

• l’appel à post_count avec n → 3 est terminé

Programmation (Chap. 9) Récursivité 21 / 33

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 29


MASSART Thierry Programmation Transparents - Volume 2

Concept de récursivité

Analyse du flot d’exécution lors de l’appel post_count(3)


• appel à post_count avec n → 3 : comme n est plus grand que 0, affiche la
valeur 3, puis appelle post_count avec n → 2
• détail de l’appel à post_count avec n → 2 :
appel à post_count avec n → 2 : comme n est plus grand que 0,
affiche la valeur 2, puis appelle post_count avec n → 1

détail de l’appel à post_count avec n → 1 :

l’appel à post_count avec n → 2 est terminé

• l’appel à post_count avec n → 3 est terminé

Programmation (Chap. 9) Récursivité 21 / 33

Concept de récursivité

Analyse du flot d’exécution lors de l’appel post_count(3)


• appel à post_count avec n → 3 : comme n est plus grand que 0, affiche la
valeur 3, puis appelle post_count avec n → 2
• détail de l’appel à post_count avec n → 2 :
appel à post_count avec n → 2 : comme n est plus grand que 0,
affiche la valeur 2, puis appelle post_count avec n → 1

détail de l’appel à post_count avec n → 1 :


� appel à post_count avec n → 1 : comme n est plus grand que 0,
affiche la valeur 1, puis appelle post_count avec n → 0
� détail de l’appel à post_count avec n → 0 :

� l’appel à post_count avec n → 1 est terminé

l’appel à post_count avec n → 2 est terminé

• l’appel à post_count avec n → 3 est terminé

Programmation (Chap. 9) Récursivité 21 / 33

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 30


MASSART Thierry Programmation Transparents - Volume 2

Concept de récursivité

Analyse du flot d’exécution lors de l’appel post_count(3)


• appel à post_count avec n → 3 : comme n est plus grand que 0, affiche la
valeur 3, puis appelle post_count avec n → 2
• détail de l’appel à post_count avec n → 2 :
appel à post_count avec n → 2 : comme n est plus grand que 0,
affiche la valeur 2, puis appelle post_count avec n → 1

détail de l’appel à post_count avec n → 1 :


� appel à post_count avec n → 1 : comme n est plus grand que 0,
affiche la valeur 1, puis appelle post_count avec n → 0
� détail de l’appel à post_count avec n → 0 :
� appel à post_count avec n → 0 : comme n n’est pas plus grand

que 0, affiche le mot ‘Boum’ et se termine


� l’appel à post_count avec n → 1 est terminé

l’appel à post_count avec n → 2 est terminé

• l’appel à post_count avec n → 3 est terminé

Programmation (Chap. 9) Récursivité 21 / 33

Concept de récursivité

Analyse du flot d’exécution lors de l’appel post_count(3)

L’ordre des instructions d’affichage de l’analyse précédente explique


pourquoi on obtient :

>>> post_count(3)
3
2
1
Boum

Programmation (Chap. 9) Récursivité 22 / 33

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 31


MASSART Thierry Programmation Transparents - Volume 2

Concept de récursivité

Récursivité et flot d’exécution

Problème
Que se passe-t-il si on modifie légèrement la fonction précédente en
déplaçant l’appel récursif avant l’instruction “print n” ?

>>> def pre_count(n):


if n <= 0:
print ’Boum’
else:
pre_count(n - 1)
print n
>>> pre_count(3)

Programmation (Chap. 9) Récursivité 23 / 33

Concept de récursivité

Récursivité et flot d’exécution


Pour résoudre ce problème, on peut refaire l’analyse du flot détaillée,
mais ce n’est pas nécessaire si l’on “accepte”1 que la récursivité
fonctionne et qu’on lit le corps du else comme suit :

Etape de recurrence:
pre_count(n - 1)
print n

Ce qui s’interprète par


on réalise d’abord le travail pour les valeurs plus petites que n
ensuite on affiche n

>>> pre_count(3)
Boum
1
2
3
1
C’est ce que Downey appelle le “leap of faith” (acte de foi).
Programmation (Chap. 9) Récursivité 24 / 33

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 32


MASSART Thierry Programmation Transparents - Volume 2

Concept de récursivité

Définitions récursives

La récursivité est très naturelle pour résoudre des problèmes qui sont
définis récursivement.

Par exemple, la séquence des nombres de Fibonacci est définie


mathématiquement de manière récursive :

F0 = 0,
F1 = 1,
Fn = Fn−1 + Fn−2 , ∀n ≥ 2.

Programmation (Chap. 9) Récursivité 25 / 33

Concept de récursivité

Définitions récursives

En Python :

def fibo(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return fibo(n - 1) + fibo(n - 2)

Programmation (Chap. 9) Récursivité 26 / 33

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 33


MASSART Thierry Programmation Transparents - Volume 2

Concept de récursivité

Fibonacci amélioré

Retour sur Fibonacci : les dictionnaires permettent d’écrire le calcul de


Fibonacci récursivement, tout en utilisant le principe de la version
itérative : pour éviter de recalculer un grand nombre de fois les mêmes
valeurs, on les stocke en mémoire.
known = {0 : 0, 1 : 1}

def fib_dict(n):
if n in known:
return known[n]
res = fib_dict(n-1) + fib_dict(n-2)
known[n] = res
return res

Remarque : définir known en dehors de toute fonction, permet d’y


accéder dans toutes les fonctions (variable “globale”).

Programmation (Chap. 9) Récursivité 27 / 33

Concept de récursivité

Récursion infinie

Si une récursion n’atteint jamais un cas de base, le programme ne se


termine (théoriquement) jamais ! C’est une récursion infinie.

def recurse():
recurse()

→ voir IDLE pour le comportement de cette fonction

Important de considérer les cas de bases et le fait que chaque appel


récursif doit s’approcher d’un cas de base.

Programmation (Chap. 9) Récursivité 28 / 33

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 34


MASSART Thierry Programmation Transparents - Volume 2

Concept de récursivité

Récursion infinie

En réalité, la récursion infinie n’existe (heureusement) pas, car une


exception est lancée quand le nombre d’appels récursif dépasse une
certaine limite. On peut la connaître (et la modifier) grâce à certaines
fonctions du module sys.

>>> import sys


>>> print sys.getrecursionlimit()
1000
>>> sys.setrecursionlimit(10000)

Si cette limite n’était pas définie, le programme “planterait” une fois


que la mémoire allouée sur la machine pour appeler les milliers
d’appels récursifs serait dépassée, ce qui serait bien plus gênant
qu’une exception (gérable, voir plus tard).

Programmation (Chap. 9) Récursivité 29 / 33

Concept de récursivité

Récursion infinie

Que se passe-t-il si on appelle factorial avec 1.5 comme


argument ?

>>> sys.setrecursionlimit(10000)
>>> factorial(1.5)
...
RuntimeError: maximum recursion depth exceeded

La base (n == 0) n’est jamais atteinte :


1.5 → 0.5 → -0.5 → -1.5 → ...

Programmation (Chap. 9) Récursivité 30 / 33

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 35


MASSART Thierry Programmation Transparents - Volume 2

Concept de récursivité

Tester les types


Pour éviter ce genre de problèmes, on peut utiliser la fonction
booléenne isinstance qui vérifie si un argument est d’un type donné.

>>> def factorial(n):


if not isinstance(n, int):
print ’Factorial is only defined for integers.’
return None
elif n < 0:
print ’Factorial is only defined for positive integers.’
return None
elif n == 0:
return 1
else:
return n * factorial(n - 1)

>>> factorial(’fred’)
Factorial is only defined for integers.
None
>>> factorial(-2)
Factorial is only defined for positive integers.
None

Programmation (Chap. 9) Récursivité 31 / 33

Glossaire

Glossaire

Programmation (Chap. 9) Récursivité 32 / 33

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 36


MASSART Thierry Programmation Transparents - Volume 2

Glossaire

Glossaire

récursivité : le fait pour une fonction de s’appeler elle-même


cas de base : branche conditionnelle dans une fonction récursive qui ne fait pas d’appel
récursif
récursion infinie : fonction qui s’appelle elle-même indéfiniment (sans atteindre jamais un
cas de base). Provoque une exception.
variable globale : variable définie en dehors de toute fonction, sa portée est globale.

Programmation (Chap. 9) Récursivité 33 / 33

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 37


MASSART Thierry Programmation Transparents - Volume 2

Chapitre 10. Prouver les propriétés des algorithmes

Contenu du chapitre

1 Quelques algorithmes

2 Preuve d’arrêt d’un algorithme

3 La notion d’invariant de boucle

4 Preuve d’exactitude d’un algorithme itératif

5 Preuve d’exactitude d’un algorithme récursif

Programmation (Chap. 10) Prouver les propriétés des algorithmes 1 / 48

Quelques algorithmes

Quelques algorithmes

Programmation (Chap. 10) Prouver les propriétés des algorithmes 2 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 38


MASSART Thierry Programmation Transparents - Volume 2

Quelques algorithmes

Preuves des propriétés des algorithmes

Etant donné un (morceau) d’algorithme ou de programme itératif ou


récursif, on désire prouver que si si les préconditions sont
respectées. :
l’arrêt de l’algorithme : prouver qu’il n’y a pas de boucle ou de
récursion infinie, que le programme s’arrête toujours.
l’exactitude de l’algorithme : prouver que l’algorithme fait bien ce
qu’il est supposé faire, que les postconditions sont respectées.

Nous allons présenter quelques algorithmes et prouver leurs


propriétés (preuves par récurrence).

Programmation (Chap. 10) Prouver les propriétés des algorithmes 3 / 48

Quelques algorithmes

Algorithme : division euclidienne par soustraction


Problème
Soit a et b deux entiers tels que a ≥ 0 et b > 0. Comment déterminer
le quotient (entier) et le reste de la division de a par b ?

Idée de l’algorithme de division euclidienne par soustraction.

Initialiser le reste à a. Ensuite, soustraire b au reste tant que celui-ci


est plus grand que b. Le nombre de soustraction est égal au quotient.

Exemple : Soit a = 14 et b = 5.

# soustractions reste
0 14
1 9
2 4

a
Le quotient de b
est 2 et le reste est 4.
Programmation (Chap. 10) Prouver les propriétés des algorithmes 4 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 39


MASSART Thierry Programmation Transparents - Volume 2

Quelques algorithmes

Algorithme : division euclidienne par soustraction

Fonction en Python :
1 def division(a, b):
2 r = a
3 q = 0
4 while r >= b:
5 r = r - b
6 q = q + 1
7 return (q, r)
>>> division(14, 5)
(2, 4)
>>> 14 / 5
2
>>> 14 % 5
4

Programmation (Chap. 10) Prouver les propriétés des algorithmes 5 / 48

Quelques algorithmes

Algorithme : division euclidienne par soustraction

A prouver : supposons que a et b soient deux entiers tels que a ≥ 0 et


b > 0,
la fonction division s’arrête toujours ? (preuve de l’arrêt)
après l’exécution de la fonction division les valeurs retournées
correspondent-elles bien au quotient et au reste de ba ? (preuve
de l’exactitude)

Programmation (Chap. 10) Prouver les propriétés des algorithmes 6 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 40


MASSART Thierry Programmation Transparents - Volume 2

Quelques algorithmes

Algorithme : nombres de Fibonacci

Fonction en Python :
1 def fibo(n):
2 if n == 0:
3 return 0
4 elif n == 1:
5 return 1
6 else:
7 return fibo(n-1) + fibo(n-2)
>>> for i in range(10):
print fibo(i),

0 1 1 2 3 5 8 13 21 34

Programmation (Chap. 10) Prouver les propriétés des algorithmes 7 / 48

Quelques algorithmes

Algorithme : nombres de Fibonacci

A prouver : supposons que n soit un entier tel que n ≥ 0,


la fonction fibo s’arrête toujours ?
après l’exécution de la fonction fibo, la valeur retournée
correspond-elle bien au nième nombre de Fibonacci ?

Programmation (Chap. 10) Prouver les propriétés des algorithmes 8 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 41


MASSART Thierry Programmation Transparents - Volume 2

Quelques algorithmes

Algorithme : calcul de l’exposant


Problème
Soit a un nombre réel et n un entier ≥ 0. Comment calculer an ?

Idée : au lieu de calculer a × a × . . . × a, on va utiliser utiliser l’idée


suivante qui devrait effectuer beaucoup moins de multiplications.
Si n est pair, alors pour calculer x n , on remplace x par x 2 et n par
n
2
;
Si n est impair, alors on calcule x · x n−1 dont le deuxième terme
nous ramène au cas pair.

Exemple : Pour calculer x 9 , on va calculer


�� � �2
9 2 2
x = x · x,

ce qui revient à faire 4 multiplications (au lieu de 8).


Programmation (Chap. 10) Prouver les propriétés des algorithmes 9 / 48

Quelques algorithmes

Algorithme : calcul de l’exposant

Fonction en Python :
1 def expo(a, n):
2 r = 1
3 while n > 0:
4 if n % 2 == 0:
5 a = a * a
6 n = n / 2
7 else:
8 r = r * a
9 n = n - 1
10 return r
>>> expo(3,3)
27
>>> expo(2,9)
512
>>> expo(2,0)
1

Programmation (Chap. 10) Prouver les propriétés des algorithmes 10 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 42


MASSART Thierry Programmation Transparents - Volume 2

Quelques algorithmes

Algorithme : calcul de l’exposant

A prouver : supposons que a soit un réel et n un entier ≥ 0,


la fonction expo s’arrête toujours ?
après l’exécution de la fonction expo la valeur retournée
correspond-elle bien à an ?

Programmation (Chap. 10) Prouver les propriétés des algorithmes 11 / 48

Quelques algorithmes

Algorithme : tri par sélection

Problème
Soit une séquence de n entiers (n ≥ 0), comment trier (par ordre
croissant) les éléments de la séquence ?

Idée du tri par sélection.

Imaginons que nous devions trier une main


d’un jeu de cartes. On pose les cartes faces
visibles sur la table. On sélectionne la plus
“petite” carte et on la prend en main. On
sélectionne ensuite la plus petite carte parmi
celles restées sur la table et on la place à
droite de la carte en main. On répète le
processus jusqu’à avoir sélectionné toutes les
cartes posées sur la table.

Programmation (Chap. 10) Prouver les propriétés des algorithmes 12 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 43


MASSART Thierry Programmation Transparents - Volume 2

Quelques algorithmes

Algorithme : tri par sélection


Idée du tri par sélection.

Pour appliquer cette idée à une séquence d’entiers, on représente la


séquence comme suit : la partie grisée est la partie triée de la
séquence (les cartes en main) et la partie blanche représente la partie
non triée (les cartes sur la table).

3 7 2 6 5 1 4
1 7 2 6 5 3 4
1 2 7 6 5 3 4
1 2 3 6 5 7 4
1 2 3 4 5 7 6
1 2 3 4 5 7 6
1 2 3 4 5 6 7
Programmation (Chap. 10) Prouver les propriétés des algorithmes 13 / 48

Quelques algorithmes

Algorithme : tri par sélection


Idée du tri par sélection.

Pour appliquer cette idée à une séquence d’entiers, on représente la


séquence comme suit : la partie grisée est la partie triée de la
séquence (les cartes en main) et la partie blanche représente la partie
non triée (les cartes sur la table).
sélectionner une valeur revient à
3 7 2 6 5 1 4 échanger la première valeur non
1 7 2 6 5 3 4 triée avec la plus petite valeur non
triée (avec les cartes cela revient à
1 2 7 6 5 3 4
les trier par échanges successifs en
1 2 3 6 5 7 4 les laissant sur la table)
1 2 3 4 5 7 6 quand il ne reste plus qu’un élément
1 2 3 4 5 7 6 à trier, on peut s’arrêter
deux boucles nécessaires
1 2 3 4 5 6 7
(voyez-vous pourquoi ?)
Programmation (Chap. 10) Prouver les propriétés des algorithmes 13 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 44


MASSART Thierry Programmation Transparents - Volume 2

Quelques algorithmes

Algorithme : tri par sélection

Une première boucle permet de sélectionner chaque élément un par


un : quand l’indice de la première boucle vaut i, les i premiers
éléments sont triés (indices 0 à i − 1). Appelons small l’indice de la
plus petite valeur des éléments non triés. Une deuxième boucle est
nécessaire pour le déterminer.

i small i small

... 1 2 7 6 5 3 4 ... 1 2 3 4 5 7 6 ...


0 1 2 3 4 5 6 0 1 2 3 4 5 6

Programmation (Chap. 10) Prouver les propriétés des algorithmes 14 / 48

Quelques algorithmes

Algorithme : tri par sélection

Fonction en Python :
1 def selection_sort(t):
2 n = len(t)
3 for i in range(n-1):
4 small = i
5 for j in range(i+1,n):
6 if t[j] < t[small]:
7 small = j
8 (t[i], t[small]) = (t[small], t[i])
>>> t = [3, 7, 2, 6, 5, 1, 4]
>>> selection_sort(t)
>>> t
[1, 2, 3, 4, 5, 6, 7]

Programmation (Chap. 10) Prouver les propriétés des algorithmes 15 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 45


MASSART Thierry Programmation Transparents - Volume 2

Quelques algorithmes

Algorithme : tri par sélection

A prouver : supposons que A soit une séquence de n entiers (n ≥ 0),


la fonction selection_sort s’arrête toujours ?
après l’exécution de la fonction selection_sort les éléments de
A sont-ils triés par ordre croissant ?

Programmation (Chap. 10) Prouver les propriétés des algorithmes 16 / 48

Preuve d’arrêt d’un algorithme

Preuve d’arrêt d’un algorithme

Programmation (Chap. 10) Prouver les propriétés des algorithmes 17 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 46


MASSART Thierry Programmation Transparents - Volume 2

Preuve d’arrêt d’un algorithme

Preuve d’arrêt d’un algorithme

Pour prouver qu’un algorithme s’arrête (si les préconditions sont


respectées), il faut prouver que
chaque boucle se termine en un nombre fini d’itérations.
chaque fonction récursive atteint un cas de base en un nombre
fini d’étapes.
Remarque : en fonction du problème, ce n’est pas toujours facile (ou
possible) d’écrire une preuve d’arrêt (cf. chap. 6 et le calcul de la suite
de Syracuse).

Programmation (Chap. 10) Prouver les propriétés des algorithmes 18 / 48

Preuve d’arrêt d’un algorithme

Preuve d’arrêt d’un algorithme itératif : boucle while


Exemple : Nous avons déjà prouvé (cf. chap. 6) que la boucle while
suivante s’arrête quand n est un entier ≥ 0 fini.
1 def countdown(n):
2 while n > 0:
3 print n
4 n = n - 1
5 print ’Boum’

Preuve de l’arrêt de countdown (version itérative).


Base. On suppose que n est un entier ≥ 0 (fini). Si n = 0, la
condition (2) est évaluée à faux, et la boucle se termine.
Réc. Supposons que la boucle s’arrête pour n ≤ k (k ≥ 0), et
prouvons le pour n = k + 1. Quand n = k + 1, l’instruction 3 est
exécutée (affiche k + 1), puis n = k lors de l’instruction 4.
La boucle est répétée avec n = k . Celle-ci s’arrêtera par
hypothèse de récurrence.

Programmation (Chap. 10) Prouver les propriétés des algorithmes 19 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 47


MASSART Thierry Programmation Transparents - Volume 2

Preuve d’arrêt d’un algorithme

Preuve d’arrêt d’un algorithme itératif : boucle while

Problème
Prouver que la division euclidienne par soustraction s’arrête quand
a ≥ 0 et b > 0.

1 def division(a, b):


2 r = a
3 q = 0
4 while r >= b:
5 r = r - b
6 q = q + 1
7 return (q, r)

Preuve.
Au tableau.

Programmation (Chap. 10) Prouver les propriétés des algorithmes 20 / 48

Preuve d’arrêt d’un algorithme

Preuve d’arrêt d’un algorithme itératif : boucle while

Problème
Prouver que le calcul de l’exposant s’arrête quand n ≥ 0.

1 def expo(a, n):


2 r = 1
3 while n > 0:
4 if n % 2 == 0:
5 a = a * a
6 n = n / 2
7 else:
8 r = r * a
9 n = n - 1
10 return r

Preuve.
Au tableau.

Programmation (Chap. 10) Prouver les propriétés des algorithmes 21 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 48


MASSART Thierry Programmation Transparents - Volume 2

Preuve d’arrêt d’un algorithme

Preuve d’arrêt d’un algorithme itératif : boucle for

Prouver qu’une boucle for s’arrête est très simple si celle-ci est
appliquée sur une séquence :
si la boucle itère sur chaque élément de la séquence (une et une
seule fois);
si la séquence est finie;
si le corps de la boucle ne modifie pas le nombre d’éléments de
la séquence;
alors il est évident que la boucle se termine en un nombre fini
d’étapes.

Programmation (Chap. 10) Prouver les propriétés des algorithmes 22 / 48

Preuve d’arrêt d’un algorithme

Preuve d’arrêt d’un algorithme itératif : boucle for

En Python, les deux premiers points sont automatiquement gérés par


la syntaxe d’une boucle for et par le fait qu’une séquence est toujours
finie.

Si le corps de la boucle ne modifie pas (n’augmente pas) le nombre


d’éléments de la séquence, la preuve de l’arrêt est immédiate.
1 def sum_all(liste):
2 res = 0
3 for item in liste:
4 res += item
5 return res

Remarque : pour le moment nous travaillons avec des séquences finies,


donc ce qui est dit ici est correct, mais il existe une notion de générateurs qui
permettent de générer des séquences infinies. Dans ce cas, la preuve d’arrêt
doit être faite.

Programmation (Chap. 10) Prouver les propriétés des algorithmes 23 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 49


MASSART Thierry Programmation Transparents - Volume 2

Preuve d’arrêt d’un algorithme

Preuve d’arrêt d’un algorithme itératif : boucle for

La fonction suivante prend une liste d’entiers en argument et modifie la


liste de telle sorte que chaque élément est suivi par son carré.
1 def add_square(liste):
2 index = 0
3 for item in liste:
4 if index % 2 == 0:
5 liste.insert(index+1, item*item)
6 index = index + 1
>>> t = [1, 2, 3]
>>> add_square(t)
>>> t
[1, 1, 2, 4, 3, 9]

Ici une preuve serait nécessaire, mais cette fonction peut être réécrite
de telle sorte qu’une preuve est inutile (voir slide suivant). Ce genre de
code est à éviter !

Programmation (Chap. 10) Prouver les propriétés des algorithmes 24 / 48

Preuve d’arrêt d’un algorithme

Preuve d’arrêt d’un algorithme itératif : boucle for

On construit une nouvelle liste et la preuve devient inutile. On retourne


la liste modifiée plutôt que de la transformer dans la fonction.
1 def add_square(liste):
2 res = []
3 for item in liste:
4 res.extend([item, item * item])
5 return res
>>> t = [1, 2, 3]
>>> t = add_square(t)
>>> t
[1, 1, 2, 4, 3, 9]

Programmation (Chap. 10) Prouver les propriétés des algorithmes 25 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 50


MASSART Thierry Programmation Transparents - Volume 2

Preuve d’arrêt d’un algorithme

Preuve d’arrêt d’un algorithme itératif


S’il y a plusieurs boucles, il faut prouver que chacune d’elles s’arrêtent.

Problème
Prouver que le tri par sélection s’arrête.

1 def selection_sort(t):
2 n = len(t)
3 for i in range(n-1):
4 small = i
5 for j in range(i+1,n):
6 if t[j] < t[small]:
7 small = j
8 (t[i], t[small]) = (t[small], t[i])

Preuve.
On observe d’abord que, sous l’hypothèse qu’un indice i < n − 1 est
fixé, la boucle for (instr. 5 – 7) s’arrête, car elle s’applique sur une
séquence finie. Ensuite, on observe que la boucle for (instr. 3 – 8)
s’applique sur une séquence finie et s’arrête également.

Programmation (Chap. 10) Prouver les propriétés des algorithmes 26 / 48

Preuve d’arrêt d’un algorithme

Preuve d’arrêt d’un algorithme récursif


Prouver qu’un algorithme récursif s’arrête est naturel : on utilise une
preuve par récurrence dont les cas de base et récursifs correspondent
à leurs équivalents dans l’algorithme.
1 def countdown(n):
2 if n == 0:
3 print ’Boum’
4 else:
5 print n
6 countdown(n-1)

Preuve de l’arrêt de countdown (version récursive).


Base. On suppose que n est un entier ≥ 0 (fini). Si n = 0, la condition
(2) est évaluée à vrai, et il fonction s’arrête car il n’y a pas d’appels
récursifs.

Réc. Supposons que la fonction s’arrête pour n ≤ k (k ≥ 0), et


prouvons le pour n = k + 1. Quand n = k + 1 > 0, l’instruction 5 est
exécutée (affiche k + 1), puis il y a un appel récursif sur n = k lors de
l’instruction 6. Celui-ci s’arrêtera par hypothèse de récurrence.

Programmation (Chap. 10) Prouver les propriétés des algorithmes 27 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 51


MASSART Thierry Programmation Transparents - Volume 2

Preuve d’arrêt d’un algorithme

Preuve d’arrêt d’un algorithme récursif

Problème
Prouver que le calcul du nième nombre de Fibonacci s’arrête toujours
si n ≥ 0

1 def fibo(n):
2 if n == 0:
3 return 0
4 elif n == 1:
5 return 1
6 else:
7 return fibo(n-1) + fibo(n-2)

Preuve.
Au tableau.

Programmation (Chap. 10) Prouver les propriétés des algorithmes 28 / 48

La notion d’invariant de boucle

La notion d’invariant de boucle

Programmation (Chap. 10) Prouver les propriétés des algorithmes 29 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 52


MASSART Thierry Programmation Transparents - Volume 2

La notion d’invariant de boucle

Invariant de boucle

Pour prouver l’exactitude d’un algorithme itératif, on utilise la notion


d’invariant de boucle.

Un invariant de boucle est une assertion (une propriété) qui


est vraie avant l’entrée dans la boucle;
est toujours vraie au début de chaque itération de la boucle.
Remarques :
cela implique que si une boucle s’arrête, l’invariant est toujours
vrai après l’exécution complète de celle-ci;
l’invariant ne doit pas être vérifié à tout moment dans le corps de
la boucle, mais au début de chaque itération de celle-ci.

Programmation (Chap. 10) Prouver les propriétés des algorithmes 30 / 48

La notion d’invariant de boucle

Invariant de boucle

La preuve complète qu’un algorithme itératif est exact revient à :


montrer qu’il s’arrête
à exhiber, pour chaque boucle, une propriété (invariant de boucle)
qui, si elle est valide avant l’exécution d’un tour de boucle, est
aussi valide après l’exécution du tour de boucle; et la prouver
vérifier que les conditions initiales rendent la propriété vraie en
entrée du premier tour de boucle
conclure que cette propriété est vraie en sortie du dernier tour de
boucle; un bon choix de la propriété prouvera qu’on a bien
produit le résultat souhaité.
La principale difficulté de ce type de preuve réside dans la
détermination de l’invariant de boucle.

Programmation (Chap. 10) Prouver les propriétés des algorithmes 31 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 53


MASSART Thierry Programmation Transparents - Volume 2

La notion d’invariant de boucle

Invariant de boucle

Exemple : Dans la division euclidienne par soustraction, l’assertion

a = b × q + r, (1)

est un invariant de boucle.


1 def division(a, b):
2 r = a
a b q r b×q +r = a
3 q = 0 14 5 0 14 5 × 0 + 14 = 14
4 while r >= b:
14 5 1 9 5 × 1 + 9 = 14
5 r = r - b
6 q = q + 1 14 5 2 4 5 × 2 + 4 = 14
7 return (q, r)

Programmation (Chap. 10) Prouver les propriétés des algorithmes 32 / 48

La notion d’invariant de boucle

Invariant de boucle

Calcul de l’exposant :
1 def expo(a, n):
1 def expo(a, n): 2 r = 1
2 r = 1 3 b = a
3 while n > 0: 4 i = n
4 if n % 2 == 0: 5 while i > 0:
5 a = a * a → 6 if i % 2 == 0:
6 n = n / 2 7 b = b * b
7 else: 8 i = i / 2
8 r = r * a 9 else:
9 n = n - 1 10 r = r * b
10 return r 11 i = i - 1
12 return r

Programmation (Chap. 10) Prouver les propriétés des algorithmes 33 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 54


MASSART Thierry Programmation Transparents - Volume 2

La notion d’invariant de boucle

Invariant de boucle

Il n’est pas toujours facile de déterminer un invariant. Dans le calcul


de l’exposant, l’assertion

??? (exercice) (2)

est un invariant de boucle.


1 def expo(a, n):
2 r = 1
3 b = a
4 i = n
5 while i > 0:
6 if i % 2 == 0:
7 b = b * b
8 i = i / 2
9 else:
10 r = r * b
11 i = i - 1
12 return r

Programmation (Chap. 10) Prouver les propriétés des algorithmes 34 / 48

La notion d’invariant de boucle

Invariant de boucle

Il n’est pas toujours facile de déterminer un invariant. Dans le calcul


de l’exposant, l’assertion
an = r × b i , (2)

est un invariant de boucle.


Soit a = 2 et n = 9, alors an = 512.
1 def expo(a, n):
2 r = 1
3 b = a r b i r × bi
i = n
4
1 2 9 1 × 29 = 512
5 while i > 0:
6 if i % 2 == 0: 2 2 8 2 × 28 = 512
7 b = b * b 2 4 4 2 × 44 = 512
8 i = i / 2
9 else: 2 16 2 2 × 162 = 512
10 r = r * b 2 256 1 2 × 2561 = 512
11 i = i - 1
12 return r 512 256 0 512 × 2560 = 512

Programmation (Chap. 10) Prouver les propriétés des algorithmes 34 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 55


MASSART Thierry Programmation Transparents - Volume 2

La notion d’invariant de boucle

Invariant de boucle
Tri par sélection :
1 def selection_sort(t):
2 n = len(t)
3 for i in range(n-1):
4 small = i
5 for j in range(i+1,n):
6 if t[j] < t[small]:
7 small = j
8 (t[i], t[small]) = (t[small], t[i])

Invariants des boucles ?

Programmation (Chap. 10) Prouver les propriétés des algorithmes 35 / 48

La notion d’invariant de boucle

Invariant de boucle
Tri par sélection :
1 def selection_sort(t):
2 n = len(t)
3 for i in range(n-1):
4 small = i
5 for j in range(i+1,n):
6 if t[j] < t[small]:
7 small = j
8 (t[i], t[small]) = (t[small], t[i])

Invariants des boucles ?


Boucle intérieure : La valeur de small est l’indice du plus petit élément
de la sous-séquence t [i ], . . . , t [j − 1].

Programmation (Chap. 10) Prouver les propriétés des algorithmes 35 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 56


MASSART Thierry Programmation Transparents - Volume 2

La notion d’invariant de boucle

Invariant de boucle
Tri par sélection :
1 def selection_sort(t):
2 n = len(t)
3 for i in range(n-1):
4 small = i
5 for j in range(i+1,n):
6 if t[j] < t[small]:
7 small = j
8 (t[i], t[small]) = (t[small], t[i])

Invariants des boucles ?


Boucle intérieure : La valeur de small est l’indice du plus petit élément
de la sous-séquence t [i ], . . . , t [j − 1].
Boucle extérieure : La sous-séquence t [0], . . . , t [i − 1] est triée, c-à-d
t [0] ≤ t [1] ≤ . . . ≤ t [i − 1] et toutes les valeurs de la sous-séquence
t [i ], . . . , t [n − 1] sont supérieures ou égales à n’importe laquelle des
valeurs dans t [0], . . . , t [i − 1].

Programmation (Chap. 10) Prouver les propriétés des algorithmes 35 / 48

Preuve d’exactitude d’un algorithme itératif

Preuve d’exactitude d’un


algorithme itératif

Programmation (Chap. 10) Prouver les propriétés des algorithmes 36 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 57


MASSART Thierry Programmation Transparents - Volume 2

Preuve d’exactitude d’un algorithme itératif

Preuve d’exactitude d’un algorithme itératif


Supposons que nous ayons prouvé qu’un algorithme itératif s’arrête,
prouver qu’il est exact revient à :
déterminer un invariant pour chaque boucle;
prouver les invariants de boucles;
utiliser l’invariant (et les valeurs des variables) au sortir de la
boucle pour vérifier l’exactitude du résultat final.
Pour prouver un invariant de boucle :
on prouve l’assertion avant d’entrer dans la boucle;
on suppose l’assertion vraie pour k − 1 ≥ 1 premières itérations
de la boucle et l’on prouve que c’est toujours vrai après la k ième
itération;
en pratique, on fait la distinction des valeurs pour chaque variable
avant et après une exécution d’une itération de la boucle :
notations aold , anew .

Programmation (Chap. 10) Prouver les propriétés des algorithmes 37 / 48

Preuve d’exactitude d’un algorithme itératif

Preuve d’exactitude d’un algorithme itératif

Exemple : division euclidienne.


1 def division(a, b):
2 r = a
3 q = 0
4 while r >= b:
5 r = r - b
6 q = q + 1
7 return (q, r)

Les variables q et r étant modifiées dans la boucle, on utilise les


notations, pour une itération donnée :
qold et rold : valeurs de q et r au début de l’itération;
qnew et rnew : valeurs de q et r à la fin de l’itération;

Programmation (Chap. 10) Prouver les propriétés des algorithmes 38 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 58


MASSART Thierry Programmation Transparents - Volume 2

Preuve d’exactitude d’un algorithme itératif

Preuve d’exactitude d’un algorithme itératif

A prouver : a = b × q + r est un invariant de boucle.

Preuve.
Avant d’entrer dans la boucle : q est initialisé à 0 et r à a, donc
b × q + r = b × 0 + a = a et l’assertion est vraie.

Par hypothèse, a = b × qold + rold . Considérons une itération pour


vérifier que a = b × qnew + rnew est toujours vrai. On a rnew = rold − b et
qnew = qold + 1 par les instructions 5 et 6. Donc,

a = b × qold + rold = b ×(qnew − 1)+(rnew + b) = b × qnew + rnew + b − b,

ce qui donne a = b × qnew + rnew après l’exécution de l’itération.

Programmation (Chap. 10) Prouver les propriétés des algorithmes 39 / 48

Preuve d’exactitude d’un algorithme itératif

Preuve d’exactitude d’un algorithme itératif

La preuve complète de l’exactitude de la fonction division est donc :


preuve de l’arrêt (voir avant)
preuve que a = b × q + r est un invariant de la boucle (voir slide
précédent)
le résultat retourné est correct car :
� la boucle se termine quand la condition est fausse, c-à-d, quand
r < b (= négation de la condition);
� à ce moment là, a = b × q + r est vrai, donc, q = a− b
r
et r < b, ce
qui correspond à la définition du quotient et du reste d’une
division1 .

1
Ici nous utilisons le fait (sans le prouver, cf. cours de Mathématiques
élémentaires) qu’il existe un couple unique d’entiers q et r tels que a = b × q + r et
r < b.
Programmation (Chap. 10) Prouver les propriétés des algorithmes 40 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 59


MASSART Thierry Programmation Transparents - Volume 2

Preuve d’exactitude d’un algorithme itératif

Preuve d’exactitude d’un algorithme itératif

Calcul de l’exposant.
1 def expo(a, n):
2 r = 1
3 b = a
4 i = n
5 while i > 0:
6 if i % 2 == 0:
7 b = b * b
8 i = i / 2
9 else:
10 r = r * b
11 i = i - 1
12 return r

Preuve de l’exactitude de expo.


Au tableau.

Programmation (Chap. 10) Prouver les propriétés des algorithmes 41 / 48

Preuve d’exactitude d’un algorithme itératif

Preuve d’exactitude d’un algorithme itératif


Le fait qu’une boucle for s’applique sur une séquence peut nécessiter
d’exprimer l’invariant sous la forme d’une assertion S (k ) où k
représente le nombre d’itérations (= le nombre d’éléments) sur
lesquelles la boucle a été exécutée.
1 def sum_all(liste):
2 res = 0
3 for item in liste:
4 res += item
5 return res

cas de base S (0) : avant d’entrer dans la boucle;


cas récursif : si S (k ) est vrai, preuve de S (k + 1);
si n est le nombre d’éléments de la séquence, alors S (n)
représente l’invariant après l’exécution complète de la boucle;
besoin d’identifier la relation entre k et les variables utilisées
dans la boucle (par ex. indice).

Programmation (Chap. 10) Prouver les propriétés des algorithmes 42 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 60


MASSART Thierry Programmation Transparents - Volume 2

Preuve d’exactitude d’un algorithme itératif

Preuve d’exactitude d’un algorithme itératif

Exemple : l’assertion suivante est un invariant de la boucle for de la


fonction sum_all :
k −1
S (k ) ≡ res = ∑ liste[j ],
j =0

où k est le nombre d’itérations effectuées


1 def sum_all(liste):
2 res = 0
3 for item in liste:
4 res += item
5 return res

Programmation (Chap. 10) Prouver les propriétés des algorithmes 43 / 48

Preuve d’exactitude d’un algorithme itératif

Preuve d’exactitude d’un algorithme itératif

Preuve.
Base. Avant d’entrer dans la boucle, res = 0 qui est bien égal à
1
∑−
j =0 liste[j ]. Donc S (0) est vraie.

Réc. Supposons que ce soit vrai pour S (k ) (k ≥ 0), et prouvons le pour


S (k + 1). Par induction, resold = ∑kj =−01 liste[j ]. Lors de la (k + 1)ième
itération item = liste[k ], donc, par l’instruction 4,

k −1 k
resnew = resold + liste[k ] = ∑ liste[j ] + liste[k ] = ∑ liste[j ].
j =0 j =0

Cela signifie qu’après l’exécution complète de la boucle, S (n) est vraie et


donc la valeur retournée est
n−1
∑ liste[j ].
j =0

Programmation (Chap. 10) Prouver les propriétés des algorithmes 44 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 61


MASSART Thierry Programmation Transparents - Volume 2

Preuve d’exactitude d’un algorithme itératif

Preuve d’exactitude d’un algorithme itératif

Tri par sélection


1 def selection_sort(t):
2 n = len(t)
3 for i in range(n-1):
4 small = i
5 for j in range(i+1,n):
6 if t[j] < t[small]:
7 small = j
8 (t[i], t[small]) = (t[small], t[i])

Preuve de l’exactitude de selection_sort.


Au tableau.

Programmation (Chap. 10) Prouver les propriétés des algorithmes 45 / 48

Preuve d’exactitude d’un algorithme récursif

Preuve d’exactitude d’un


algorithme récursif

Programmation (Chap. 10) Prouver les propriétés des algorithmes 46 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 62


MASSART Thierry Programmation Transparents - Volume 2

Preuve d’exactitude d’un algorithme récursif

Preuve d’exactitude d’un algorithme récursif


Prouver qu’un algorithme récursif est exact est naturel grâce, encore
une fois, à une preuve par récurrence.
def sum_rec(n):
""" compute the sum of integers from 1 to n """
if n == 1:
return 1
else:
return sum_rec(n-1) + n

Preuve de l’exactitude de sum_rec.


Base. On suppose que n est un entier ≥ 1 (fini). Si n = 1, la condition
(3) est évaluée à vrai, et la valeur retournée est bien ∑1i =1 i = 1.

Réc. Supposons que la fonction soit exacte pour n ≤ k (k ≥ 1), et


prouvons le pour n = k + 1. Quand n = k + 1 > 1, l’instruction 6
retourne, par hypothèse de récurrence

n−1 n
∑ i +n = ∑ i.
i =1 i =1

Programmation (Chap. 10) Prouver les propriétés des algorithmes 47 / 48

Preuve d’exactitude d’un algorithme récursif

Preuve d’exactitude d’un algorithme récursif

Problème
Prouver que le calcul du nième nombre de Fibonacci est exact.

1 def fibo(n):
2 if n == 0:
3 return 0
4 elif n == 1:
5 return 1
6 else:
7 return fibo(n-1) + fibo(n-2)

Preuve.
Au tableau.

Programmation (Chap. 10) Prouver les propriétés des algorithmes 48 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 63


MASSART Thierry Programmation Transparents - Volume 2

Chapitre 11. Complexité : évaluer l’efficacité des


algorithmes
Contenu du chapitre
1 Efficacité des algorithmes

2 Quelques nouveaux algorithmes

3 Calculer le temps CPU des algorithmes

4 La notation grand-O et la notion de complexité dans le pire des cas

5 Evaluer la complexité des algorithmes

6 Complexité des opérations sur les dictionnaires

7 Comparaison de différents algorithmes

8 Comparaisons de la comlexité d’algorithmes sur des dictionnaires

9 Glossaire

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 1 / 126

Référence

Le contenu de ce chapitre n’est pas couvert dans le livre ThinkPython


de Downey.

En ce qui concerne la complexité, les références suivantes ont été


utilisées :
A HO, A. et U LLMAN , J., Concepts fondamentaux de
l’Informatique, Dunod (1993)
C ORMEN , T. et L EISERSON , C., Introduction to algorithms, MIT
Press (1991)

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 2 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 64


MASSART Thierry Programmation Transparents - Volume 2

Efficacité des algorithmes

Efficacité des algorithmes

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 3 / 126

Efficacité des algorithmes

Efficacité des algorithmes

Etant donné un algorithme :


comment évaluer son efficacité ?
comment comparer l’efficacité de deux algorithmes différents
résolvant le même problème ?
Pour cela, on peut :
calculer le temps d’exécution utilisé par la machine pour effectuer
un algorithme (on parle de temps CPU1 ) : temps utilisé par le
processeur pour réaliser une tâche particulière;
évaluer l’efficacité théorique d’un algorithme, la croissance du
temps mis par un algorithme en fonction de la taille de ses
entrées : on parle de la complexité de l’algorithme.

1
Central Processing Unit.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 4 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 65


MASSART Thierry Programmation Transparents - Volume 2

Efficacité des algorithmes

Efficacité des algorithmes


L’efficacité d’un algorithme peut dépendre de la taille des entrées (par
ex. nombre d’éléments dans une liste, nombre d’équations dans un
système à résoudre, etc.).

Pour une taille n donnée, l’efficacité d’un algorithme (par ex. un tri)
peut également dépendre des valeurs données en entrée. On peut
alors déterminer :
le meilleur des cas (par ex. les valeurs sont déjà triées);
le pire des cas (par ex. les valeurs sont triées par ordre
décroissant).
Ainsi, on parlera de l’efficacité ou de la complexité d’un algorithme
dans le pire des cas;
dans le meilleur des cas;
en moyenne (entrées aléatoires).

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 5 / 126

Quelques nouveaux algorithmes

Quelques nouveaux
algorithmes

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 6 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 66


MASSART Thierry Programmation Transparents - Volume 2

Quelques nouveaux algorithmes

Algorithme : tri par insertion


Problème
Soit une séquence de n entiers (n ≥ 0), comment trier (par ordre
croissant) les éléments de la séquence ?

Idée du tri par insertion.

Imaginons que nous devions trier une main


d’un jeu de cartes. On pose les cartes non
triées sur la table (faces cachées). On prend
une première carte en main : elle forme une
“sous-main” triée. On prend une deuxième
carte sur la table et on l’insére dans la main
de telle sorte que les deux cartes en main
soient triées. On répète le processus jusqu’à
avoir inséré toutes les cartes posées sur la
table.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 7 / 126

Quelques nouveaux algorithmes

Algorithme : tri par insertion


Idée du tri par insertion.

Pour appliquer cette idée à une séquence d’entiers, on représente la


séquence comme suit : la partie grisée est la partie triée de la
séquence.

3 7 2 6 5 1 4
3 7 2 6 5 1 4
2 3 7 6 5 1 4
2 3 6 7 5 1 4
2 3 5 6 7 1 4
1 2 3 5 6 7 4
1 2 3 4 5 6 7

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 8 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 67


MASSART Thierry Programmation Transparents - Volume 2

Quelques nouveaux algorithmes

Algorithme : tri par insertion


Idée du tri par insertion.

Pour appliquer cette idée à une séquence d’entiers, on représente la


séquence comme suit : la partie grisée est la partie triée de la
séquence.

3 7 2 6 5 1 4 Deux boucles nécessaires :


3 7 2 6 5 1 4 une boucle pour trier chaque élément
2 3 7 6 5 1 4 1 à 1 (sauf le premier) : indice i

2 3 6 7 5 1 4 une boucle, dans chaque itération de


la première, pour déplacer les
2 3 5 6 7 1 4
éléments plus grand que l’élément à
1 2 3 5 6 7 4 trier vers la droite : indice j
1 2 3 4 5 6 7

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 8 / 126

Quelques nouveaux algorithmes

Algorithme : tri par insertion

Exemple. Quand l’indice de la première boucle vaut i, les i premiers


éléments sont triés (indices 0 à i − 1). Appelons clef la valeur de
l’élément à trier. L’indice de la clef est i. Soit k la valeur de l’indice de
la nouvelle position de la clef, après insertion : tous les éléments de la
partie triée dont les valeurs sont plus grandes que la clef ont un indice
compris entre k et i − 1. On va utiliser une deuxième boucle d’indice j
pour décaler ces éléments vers la droite.

k i k i

... 3 7 2 6 5 1 4 ... 1 2 3 5 6 7 4
0 1 2 3 4 5 6 0 1 2 3 4 5 6

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 9 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 68


MASSART Thierry Programmation Transparents - Volume 2

Quelques nouveaux algorithmes

Algorithme : tri par insertion

Fonction en Python :
1 def insertion_sort(t):
2 n = len(t)
3 for i in range(1,n):
4 clef = t[i]
5 j = i - 1
6 while j >= 0 and t[j] > clef:
7 t[j+1] = t[j]
8 j = j - 1
9 t[j+1] = clef
>>> t = [3, 7, 2, 6, 5, 1, 4]
>>> insertion_sort(t)
>>> t
[1, 2, 3, 4, 5, 6, 7]

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 10 / 126

Quelques nouveaux algorithmes

Algorithme : tri par fusion


Problème
Soit une séquence de n entiers (n ≥ 0), comment trier (par ordre
croissant) les éléments de la séquence ?

Idée du tri par fusion.

Imaginons que nous devions trier une main


d’un jeu de cartes et que Bob soit en notre
compagnie. On divise le paquet de cartes en
2 parties (à peu près) égales. On donne la
première partie à Bob, qui s’occupe de la
trier. On lui donne alors la seconde partie
pour qu’il la trie également. Ensuite, à partir
des deux paquets de cartes triées, on
reconstitue un seul paquet de cartes trié en
fusionnant les deux paquets triés.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 11 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 69


MASSART Thierry Programmation Transparents - Volume 2

Quelques nouveaux algorithmes

Algorithme : tri par fusion


Idée du tri par fusion.

Appliquons cette idée à une séquence d’entiers : on divise (split) et on


fusionne deux parties triées (merge). Une séquence à 1 élément est
triée. Les séquences triées sont grisées.

3 7 2 6 5 1 4
split
3 7 2 6 5 1 4
split split
3 7 2 6 5 1 4
split split split

3 7 2 6 5 1
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 12 / 126

Quelques nouveaux algorithmes

Algorithme : tri par fusion


Idée du tri par fusion.

Appliquons cette idée à une séquence d’entiers : on divise (split) et on


fusionne deux parties triées (merge). Une séquence à 1 élément est
triée. Les séquences triées sont grisées.

3 7 2 6 5 1 4

3 7 2 6 5 1 4

3 7 2 6 1 5 4
merge merge merge
3 7 2 6 5 1
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 12 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 70


MASSART Thierry Programmation Transparents - Volume 2

Quelques nouveaux algorithmes

Algorithme : tri par fusion


Idée du tri par fusion.

Appliquons cette idée à une séquence d’entiers : on divise (split) et on


fusionne deux parties triées (merge). Une séquence à 1 élément est
triée. Les séquences triées sont grisées.

3 7 2 6 5 1 4

3 7 2 6 5 1 4

3 7 2 6 1 5 4

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 12 / 126

Quelques nouveaux algorithmes

Algorithme : tri par fusion


Idée du tri par fusion.

Appliquons cette idée à une séquence d’entiers : on divise (split) et on


fusionne deux parties triées (merge). Une séquence à 1 élément est
triée. Les séquences triées sont grisées.

3 7 2 6 5 1 4

2 3 6 7 1 4 5
Merge Merge
3 7 2 6 1 5 4

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 12 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 71


MASSART Thierry Programmation Transparents - Volume 2

Quelques nouveaux algorithmes

Algorithme : tri par fusion


Idée du tri par fusion.

Appliquons cette idée à une séquence d’entiers : on divise (split) et on


fusionne deux parties triées (merge). Une séquence à 1 élément est
triée. Les séquences triées sont grisées.

3 7 2 6 5 1 4

2 3 6 7 1 4 5

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 12 / 126

Quelques nouveaux algorithmes

Algorithme : tri par fusion


Idée du tri par fusion.

Appliquons cette idée à une séquence d’entiers : on divise (split) et on


fusionne deux parties triées (merge). Une séquence à 1 élément est
triée. Les séquences triées sont grisées.

1 2 3 4 5 6 7
Merge
2 3 6 7 1 4 5

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 12 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 72


MASSART Thierry Programmation Transparents - Volume 2

Quelques nouveaux algorithmes

Algorithme : tri par fusion


Idée du tri par fusion.

Appliquons cette idée à une séquence d’entiers : on divise (split) et on


fusionne deux parties triées (merge). Une séquence à 1 élément est
triée. Les séquences triées sont grisées.

1 2 3 4 5 6 7

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 12 / 126

Quelques nouveaux algorithmes

Algorithme : tri par fusion


Idée du tri par fusion.

On va écrire
une fonction split pour séparer une liste (approx.) en 2;
une fonction merge qui fusionne deux listes triées;
une fonction récursive merge_sort
� Base : une liste vide ou à un seul élément est une liste triée;
� Réc. : on divise la liste en 2, puis on trie récursivement les 2
parties, puis on fusionne les deux parties triées.
contrairement aux tris par sélection et insertion qui ne modifient
que les valeurs à l’intérieur de la liste2 , ici nous devons travailler
avec des nouvelles sous-listes. Pour éviter les problèmes (cf.
Chap. 7), les fonctions retourneront les listes.
insertion_sort(t) # la liste est modifiee par la fonction
t = merge_sort(t) # on retourne la liste triee
2
On parle de fonctions réalisant le travail “in place”.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 14 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 73


MASSART Thierry Programmation Transparents - Volume 2

Quelques nouveaux algorithmes

Algorithme : tri par fusion

Idée de l’algorithme merge.

On va écrire une fonction récursive merge qui fusionne deux


séquences A1 et A2 triées :
Base : si A1 est vide, alors A2 est la liste fusionnée; si A2 est
vide, alors A1 est la liste fusionnée3
Réc. : si A1 [0] < A2 [0], alors A1 [0] doit se trouver en premier
dans la liste fusionnée. La liste fusionnée est
A1 [0] + merge(A�1 , A2 ) où A�1 est A1 pour laquelle on a retiré le
premier élément. Le cas A1 [0] ≥ A2 [0] est géré de manière
symétrique.

3
Implique : si les deux listes sont vides, alors la liste fusionnée est vide.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 15 / 126

Quelques nouveaux algorithmes

Algorithme : tri par fusion

Fonction split en Python :


1 def split(t):
2 """ precondition: len(t) >= 2 """
3 mid = len(t) / 2
4 t1 = t[:mid]
5 t2 = t[mid:]
6 return (t1, t2)
>>> split([1,2])
([1], [2])
>>> split([1,2,3])
([1], [2, 3])
>>> split(range(10))
([0, 1, 2, 3, 4], [5, 6, 7, 8, 9])

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 16 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 74


MASSART Thierry Programmation Transparents - Volume 2

Quelques nouveaux algorithmes

Algorithme : tri par fusion

Fonction merge en Python :


1 def merge(t1, t2):
2 if len(t1) == 0:
3 return t2
4 elif len(t2) == 0:
5 return t1
6 elif t1[0] < t2[0]:
7 return [t1[0]] + merge(t1[1:], t2)
8 else:
9 return [t2[0]] + merge(t1, t2[1:])
>>> merge([],[])
[]
>>> merge([],[1])
[1]
>>> merge([1, 2, 7, 8], [3, 4, 9])
[1, 2, 3, 4, 7, 8, 9]

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 17 / 126

Quelques nouveaux algorithmes

Algorithme : tri par fusion

Fonction merge_sort en Python :


1 def merge_sort(t):
2 n = len(t)
3 if n > 1:
4 (t1, t2) = split(t)
5 t1 = merge_sort(t1)
6 t2 = merge_sort(t2)
7 return merge(t1, t2)
8 else:
9 return t
>>> merge_sort(t)
[1, 2, 3, 4, 5, 6, 7]
>>> t
[7, 4, 2, 1, 6, 5, 3]
>>> t = merge_sort(t)
>>> t
[1, 2, 3, 4, 5, 6, 7]

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 18 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 75


MASSART Thierry Programmation Transparents - Volume 2

Quelques nouveaux algorithmes

Efficacité des algorithmes de tri

Questions :
comment exprimer l’efficacité du tri par sélection (cf. Chap. 8), du
tri par insertion ou du tri par fusion en fonction du nombre n
d’éléments dans la séquence à trier ?
y-a-t il des cas (entrées) qui sont pires ou meilleurs que d’autres ?
quel est, des trois algorithmes de tri présentés, le plus efficace ?

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 19 / 126

Quelques nouveaux algorithmes

Efficacité des algorithmes du calcul de l’exposant

Les deux fonctions suivantes calculent an (cf. Chap. 8) :


1 def expo(a, n):
2 r = 1
3 while n > 0:
1 def expo(a, n):
4 if n % 2 == 0:
2 r = 1
5 a = a * a
3 for i in range(n):
6 n = n / 2
4 r = a * r
7 else:
5 return r
8 r = r * a
9 n = n - 1
10 return r
comment exprimer formellement l’intuition selon laquelle la
deuxième version est plus efficace que la première ?

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 20 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 76


MASSART Thierry Programmation Transparents - Volume 2

Quelques nouveaux algorithmes

Efficacité des algorithmes du calcul de nième nombre de


Fibonacci

Les deux fonctions suivantes calculent le nième nombre de Fibonacci :


1 def fib_rec(n):
2 if n == 0: 1 def fib_iter(n):
3 return 0 2 f = [0, 1]
4 elif n == 1: 3 for i in range(2,n+1):
5 return 1 4 f.append(f[i-1] + f[i-2])
6 else: 5 return f[n]
7 return fib_rec(n-1) + fib_rec(n-2)
pourquoi la version récursive prend beaucoup plus de temps dès
que n > 30 ? (quand elle n’est pas interrompue par un
dépassement du nombre maximum d’appels récursifs !)

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 21 / 126

Calculer le temps CPU des algorithmes

Calculer le temps CPU des


algorithmes

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 22 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 77


MASSART Thierry Programmation Transparents - Volume 2

Calculer le temps CPU des algorithmes

Evaluer le temps CPU d’un algorithme

On ne calcule pas le temps d’exécution réel d’un algorithme (par ex.


avec une montre ou en récupérant dans le script l’heure avant et après
l’exécution d’un morceau de code) ! En effet,
le processeur n’est pas utilisé uniquement pour votre programme :
même si vous quittez toutes les autres applications avant votre test, il y
a quantité de programmes qui tournent en tâches de fond (qui vérifient
si un email est arrivé, si la connexion internet est ouverte, si un CD est
dans le lecteur, mise à jour anti-virus, etc.)

il y a aussi d’autres problèmes techniques difficiles à estimer : y-a-t il un


cache pour les fonctions, comment cela marche-t-il ?
On va plutôt évaluer le temps CPU : temps mis par le processeur pour
exécuter uniquement un (morceau) de code donné (ce temps reste
néanmoins dépendant du processeur, du système d’exploitation, etc. : on ne
peut comparer que des tests réalisés sur une même machine).

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 23 / 126

Calculer le temps CPU des algorithmes

Evaluer le temps CPU d’un algorithme


Si on veut estimer le temps CPU d’un morceau de code Python, il est
recommandé d’utiliser le module timeit.
ce module règle automatiquement la plupart des problèmes
évoqués ci-avant;
néanmoins, une mesure du temps CPU avec timeit reste une
estimation, et le même morceau de code sur les mêmes entrées
peut donner des temps différents. Dans ce cas :
� choisiriez-vous de calculer la moyenne des temps CPU pour avoir
une bonne estimation ?

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 24 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 78


MASSART Thierry Programmation Transparents - Volume 2

Calculer le temps CPU des algorithmes

Evaluer le temps CPU d’un algorithme


Si on veut estimer le temps CPU d’un morceau de code Python, il est
recommandé d’utiliser le module timeit.
ce module règle automatiquement la plupart des problèmes
évoqués ci-avant;
néanmoins, une mesure du temps CPU avec timeit reste une
estimation, et le même morceau de code sur les mêmes entrées
peut donner des temps différents. Dans ce cas :
� choisiriez-vous de calculer la moyenne des temps CPU pour avoir
une bonne estimation ?
� Non ! S’il y a des différences de temps, elles ne sont pas dues à
votre test (le code et les entrées n’ont pas été modifiés) : il faut
prendre le temps minimum.
� En pratique, on va effectuer un grand nombre de fois un morceau
de code (une série de n tests), calculer d’autres séries de n tests :
on gardera alors le temps de la série la plus rapide.

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 24 / 126

Calculer le temps CPU des algorithmes

Utilisation de timeit en ligne de commande

Une façon d’utiliser timeit est de le faire en ligne de commande.

Syntaxe. Pour tester une fonction f qui se trouve dans un module m :

python -m timeit "import m; m.f(...)"

Exemple. La commande suivante va chercher la version itérative de


Fibonacci dans le fichier fibo.py :

python -m timeit "import fibo; fibo.fib_iter(30)"

produit
100000 loops, best of 3: 16 usec per loop

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 25 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 79


MASSART Thierry Programmation Transparents - Volume 2

Calculer le temps CPU des algorithmes

Utilisation de timeit en ligne de commande

$ python -m timeit "import fibo; fibo.fib_iter(30)"


100000 loops, best of 3: 16 usec per loop

le symbole $ est ajouté pour montrer qu’il s’agit d’une commande.


timeit a calculé 100000 fois fib_iter(30), et ce, 3 fois de suite.
le meilleur des 3 temps était � 1600000 µs, ce qui correspond à
16 µs en moyenne pour chaque appel (loop) à fib_iter.
une milliseconde (ms, msec) correspond à un millième de
seconde.
une microseconde (µs, usec) correspond à un millième de
milliseconde.
au total, calculer 100000 fois fib_iter(30) a donc pris (dans le
meilleur des cas) 1.6 sec de temps CPU.

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 26 / 126

Calculer le temps CPU des algorithmes

Utilisation de timeit en ligne de commande


$ python -m timeit "import fibo; fibo.fib_rec(30)"
10 loops, best of 3: 943 msec per loop

le nombre de tests de la série a été réduit de 100000 à 10 par


timeit
timeit a calculé 10 fois fib_rec(30), et ce, 3 fois de suite.
le meilleur des 3 temps était � 9430 ms, ce qui correspond à 943
ms en moyenne pour chaque appel (loop) à fib_rec.
au total, calculer 10 fois fib_rec(30) a donc pris (dans le
meilleur des cas) 9.43 sec de temps CPU.
un appel à fib_iter(30) � 16 µs, un appel à fib_rec(30)
� 943000 µs.
fib_iter(30) semble donc être fois � 60000 plus rapide que
fib_rec(30) !

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 27 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 80


MASSART Thierry Programmation Transparents - Volume 2

Calculer le temps CPU des algorithmes

Utilisation de timeit en ligne de commande

par défaut, le nombre de “loops” est calculé en essayant des


puissances de 10 successives jusqu’à obtenir un temps total d’au
moins 0.2 sec.
on peut spécifier le nombre de loops avec l’option -n
$ python -m timeit -n 5 "import fibo; fibo.fib_rec(30)"
5 loops, best of 3: 940 msec per loop
on peut spécifier le nombre de séries (3 par défaut) avec -r
$ python -m timeit -n 5 -r 5 "import fibo; fibo.fib_rec(30)"
5 loops, best of 5: 944 msec per loop

l’aide complète de l’utilisation en ligne de commande est obtenue


avec l’option -h
$ python -m timeit -h

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 28 / 126

Calculer le temps CPU des algorithmes

Utilisation de timeit dans un script

Le module timeit peut également être utilisé dans un script ou en


mode interactif (mais il faut préciser où il peut importer les fonctions à
tester, même si le temps est calculé dans le script lui-même).
>>> import timeit
>>> t = timeit.Timer(’fibo.fib_iter(30)’,’import fibo’)
>>> t.timeit()
14.887954950332642

on initialise un objet Timer avec les commandes à tester (le premier


argument est le code à tester, le second est le code initial, à exécuter
une seule fois avant les tests)

la méthode timeit(n) effectue n fois (par défaut un million) le code à


tester et retourne un temps (float) exprimé en secondes.

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 29 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 81


MASSART Thierry Programmation Transparents - Volume 2

Calculer le temps CPU des algorithmes

Utilisation de timeit dans un script

>>> t.repeat(3, 100000)


[1.5052459239959717, 1.4943749904632568, 1.4944181442260742]
>>> min(t.repeat())
15.087707996368408

la méthode repeat(r,n) exécute r séries de n tests et retournent les


résultats (exprimés en secondes) sous la forme d’une liste. Par défaut,
r vaut 3 et n vaut un million.

la fonction min retourne le plus petit élément d’une séquence.

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 30 / 126

Calculer le temps CPU des algorithmes

Utilisation de timeit dans un script

Nous créons un module cpu (disponible sur e-learning) :


import timeit

def cpuTime(func_name, mod_name, args, n = 100000, r = 3):


""" lance r series de n tests
sur la fonction func_name (dans le module mod_name)
et avec args comme arguments

func_name, mod_name et args sont des str


n et r sont des entiers (defaut: 100000 and 3)

retourne le temps moyen (en sec)


pour une execution de la fonction dans la meilleure serie
"""
stmt = mod_name + ’.’ + func_name + ’(’ + args + ’)’
setup = ’import ’ + mod_name
t = timeit.Timer(stmt,setup)
res = min(t.repeat(r,n))
return res / n

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 31 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 82


MASSART Thierry Programmation Transparents - Volume 2

Calculer le temps CPU des algorithmes

Comparaison d’algorithmes

Dans le module fibo :


import cpu

if __name__ == ’__main__’:
print "Temps affiches en msec"
print ’n: iter: rec:’
for i in range (5,31, 5):
print ’%4d’ % i,
print ’%.3f’ % (cpu.cpuTime(’fib_iter’, ’fibo’, str(i), 1000) * 1000),
print ’%.3f’ % (cpu.cpuTime(’fib_rec’, ’fibo’, str(i), 10) * 1000)
for i in range (100, 1901, 200):
print ’%4d’ % i,
print ’%.3f ----’ % (cpu.cpuTime(’fib_iter’, ’fibo’, str(i), 1000)
* 1000)

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 32 / 126

Calculer le temps CPU des algorithmes

Comparaison d’algorithmes
Comparaison des deux fonctions pour Fibonacci :
Temps affiches en msec
n: iter: rec:
5 0.003 0.005
10 0.006 0.058
15 0.008 0.656
20 0.011 7.305
25 0.013 82.964
30 0.015 905.731
100 0.050 ----
300 0.148 ----
500 0.283 ----
700 0.421 ----
900 0.568 ----
1100 0.737 ----
1300 0.910 ----
1500 1.123 ----
1700 1.319 ----
1900 1.514 ----

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 33 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 83


MASSART Thierry Programmation Transparents - Volume 2

Calculer le temps CPU des algorithmes

Comparaison d’algorithmes

Comparaison des deux fonctions pour le calcul de l’exposant :


Temps affiches en msec. Calcul de 2^n
n: class: amel:
1000 0.394 0.014
2000 0.950 0.029
3000 1.757 0.046
4000 2.699 0.061
5000 3.702 0.085
6000 4.826 0.071
7000 6.119 0.110
8000 7.561 0.135
9000 9.275 0.163
10000 10.891 0.140

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 34 / 126

Calculer le temps CPU des algorithmes

Comparaison d’algorithmes
Dans le module sort :
import random
import cpu

def test(n):
t1 = range(n)
t2 = range(n,0,-1)
t3 = []
for i in range(n):
t3.append(random.randint(0,n))
print ’%6d ’ % n,
print ’%7.2f’ % (cpu.cpuTime(’selection_sort’, ’sort’, str(t1), 100) * 1000),
print ’%7.2f’ % (cpu.cpuTime(’insertion_sort’, ’sort’, str(t1), 100) * 1000),
print ’%7.2f’ % (cpu.cpuTime(’merge_sort’, ’sort’, str(t1), 100) * 1000),
print ’ %7.2f’ % (cpu.cpuTime(’selection_sort’, ’sort’, str(t2), 100) * 1000),
print ’%7.2f’ % (cpu.cpuTime(’insertion_sort’, ’sort’, str(t2), 100) * 1000),
print ’%7.2f’ % (cpu.cpuTime(’merge_sort’, ’sort’, str(t2), 100) * 1000),
print ’ %7.2f’ % (cpu.cpuTime(’selection_sort’, ’sort’, str(t3), 100) * 1000),
print ’%7.2f’ % (cpu.cpuTime(’insertion_sort’, ’sort’, str(t3), 100) * 1000),
print ’%7.2f’ % (cpu.cpuTime(’merge_sort’, ’sort’, str(t3), 100) * 1000)

if __name__ == ’__main__’:
print "Temps affiches en msec"
print ’n t1: sel ins mer t2: sel ins mer t3: sel ins mer’
for i in range(100, 1001, 100):
test(i)

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 35 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 84


MASSART Thierry Programmation Transparents - Volume 2

Calculer le temps CPU des algorithmes

Comparaison d’algorithmes

Comparaison des trois fonctions de tri (t1 déjà trié, t2 trié par ordre
décroissant, t3 aléatoire) en fonction de la taille n de la séquence :
Temps affiches en msec
n t1: sel ins mer t2: sel ins mer t3: sel ins mer
100 0.95 0.05 0.83 0.96 1.76 0.92 0.93 1.03 1.27
200 3.41 0.10 1.94 3.45 6.92 2.12 3.34 3.53 3.03
300 7.56 0.15 3.24 7.73 15.47 3.45 7.51 7.66 5.12
400 13.37 0.19 4.66 13.60 27.40 4.98 13.24 13.75 7.48
500 20.79 0.24 6.43 21.23 44.01 6.63 20.49 22.13 10.14
600 29.94 0.29 8.04 30.83 61.06 8.57 29.76 33.27 13.08
700 40.68 0.34 9.79 42.10 86.26 10.52 40.88 43.80 16.03
800 53.88 0.39 12.01 55.58 114.96 12.60 53.08 60.03 19.33
900 67.70 0.44 14.14 70.14 145.48 14.76 68.23 76.18 23.07
1000 83.49 0.49 17.24 86.22 180.61 17.67 84.39 95.27 28.64

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 36 / 126

Calculer le temps CPU des algorithmes

Comparaison d’algorithmes
Interprétation des résultats :
liste croissante : le tri par insertion est nettement plus rapide que
les deux autres (à votre avis, pourquoi ?); le tri par fusion est
meilleur que le tri par sélection (l’écart augmente quand n
augmente).
liste décroissante : le tri par insertion est nettement plus lent que
les deux autres; le tri par fusion est meilleur que les deux autres
(même rmq sur l’écart).
liste aléatoire : les tris par insertion et sélection ont des temps du
même ordre de grandeur, le tri par fusion est meilleur que les
deux autres (même rmq sur l’écart).
Cet “écart” est intéressant : ce qui va nous intéresser est la croissance
du temps d’exécution en fonction de n, plutôt que des chiffres. C’est
ce qu’exprime la complexité.

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 37 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 85


MASSART Thierry Programmation Transparents - Volume 2

La notation grand-O et la notion de complexité dans le pire des cas

La notation grand-O et la
notion de complexité dans le
pire des cas

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 38 / 126

La notation grand-O et la notion de complexité dans le pire des cas

Temps d’exécution

Pour analyser les temps d’exécution, nous allons définir une fonction
T (n) qui va représenter le nombre d’unités de temps pris par un
algorithme sur une entrée de taille n, et ce, dans le pire des cas.

Pour simplifier les calculs, on va considérer que les instructions


élémentaires prennent 1 unité de temps : assignation, opération
arithmétique sur des nombres, etc.

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 39 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 86


MASSART Thierry Programmation Transparents - Volume 2

La notation grand-O et la notion de complexité dans le pire des cas

Temps d’exécution
Exemple : soit la fonction suivante, comment exprimer T (n) où n est le
nombre d’éléments de la liste donnée en entrée.
1 def sum_all(liste):
2 res = 0
3 for item in liste:
4 res += item
5 return res

T (n) = 2n + 2 car
l’instruction 2 est exécutée 1 fois
l’instruction 3 est exécutée n fois (assignation de chaque élément)
l’instruction 4 est exécutée n fois
l’instruction 5 est exécutée 1 fois
C’est une approximation (on a pas tenu compte de l’appel à la fonction, du
temps exact de chaque instruction élémentaire, du nombre exact
d’instructions élémentaires cachées dans l’instr. 3, etc.) mais cela a peu
d’importance : ce qui va nous intéresser est la croissance de T (n), pas les
constantes.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 40 / 126

La notation grand-O et la notion de complexité dans le pire des cas

Temps d’exécution
Ce qui nous intéresse c’est la croissance de T (n), pas les constantes.

Imaginons que chaque instruction élémentaire soit réalisée en 1 µs (1


million d’instructions à la seconde) et que deux algorithmes A et B
(pour le même problème) aient des temps TA (n) = 1000n et
TB (n) = 2n2 .

Temps d’exécution (en sec.) :

n A B
10 0.01 0.0002
100 0.1 0.02
1000 1.0 2.0
10000 10.0 200.0
100000 100.0 20000.0

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 41 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 87


MASSART Thierry Programmation Transparents - Volume 2

La notation grand-O et la notion de complexité dans le pire des cas

Temps d’exécution

Dès que n > 500, A est bien meilleur que B : la constante 1000
importe beaucoup moins que le fait que dans un cas on a une fonction
linéaire, dans l’autre, elle est quadratique.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 42 / 126

La notation grand-O et la notion de complexité dans le pire des cas

Temps d’exécution
x3
Comparaison de 1000x + 1000, 10x 2 et 100
− x sur [0, 70]

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 43 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 88


MASSART Thierry Programmation Transparents - Volume 2

La notation grand-O et la notion de complexité dans le pire des cas

Temps d’exécution
x3
Comparaison de 1000x + 1000, 10x 2 et 100
− x sur [600, 2000]

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 44 / 126

La notation grand-O et la notion de complexité dans le pire des cas

Temps d’exécution

Les fonctions de temps suivantes sont classées par croissance. Les


termes fréquemment utilisés pour décrire certains temps sont mis
entre parenthèses.
T (n) = log n (temps logarithmique, la base importe peu)

T (n) = n
T (n) = n (temps linéaire)
T (n) = n log n
T (n) = n2 (temps quadratique)
T (n) = n3 (temps cubique)
T (n) = 2n (temps exponentiel, la base importe peu)
T (n) = n!
g(n)

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 45 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 89


MASSART Thierry Programmation Transparents - Volume 2

La notation grand-O et la notion de complexité dans le pire des cas

Temps d’exécution

Comparaison de log n, n et n sur [0, 50].

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 46 / 126

La notation grand-O et la notion de complexité dans le pire des cas

Temps d’exécution
Comparaison de n, n log n et n2 sur [0, 25].

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 47 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 90


MASSART Thierry Programmation Transparents - Volume 2

La notation grand-O et la notion de complexité dans le pire des cas

Temps d’exécution
Comparaison de n3 et 2n sur [0, 15].

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 48 / 126

La notation grand-O et la notion de complexité dans le pire des cas

Temps d’exécution
Comparaison de 2n et n! sur [1, 20].

>>> for i in range(1,21):


print ’%7d’ % 2**i,
print ’%19d’ % fact(i)

2 1
4 2
8 6
16 24
32 120
64 720
128 5040
256 40320
512 362880
1024 3628800
2048 39916800
4096 479001600
8192 6227020800
16384 87178291200
32768 1307674368000
65536 20922789888000
131072 355687428096000
262144 6402373705728000
524288 121645100408832000
1048576 2432902008176640000

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 49 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 91


MASSART Thierry Programmation Transparents - Volume 2

La notation grand-O et la notion de complexité dans le pire des cas

Taille maximale du problème, pour un temps donné

Imaginons qu’une machine exécute 1 million d’instructions à la


seconde et que nous sommes prêts à attendre 1 heure de temps CPU
pour résoudre un problème. Quelle est la taille maximale du problème
que l’algorithme A (TA (n) = 1000n) peut résoudre ?

1000n ≤ 60 × 60 × 106 = 36 × 108 ,

donc n ≤ 36 × 105 . L’algorithme A peut résoudre un problème de taille


3600000 en 1 heure.

Pour l’algorithme B (TB (n) = 2n2 ) :

2n2 ≤ 36 × 108 ,

donc n ≤ 18 × 108 � 42426.

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 50 / 126

La notation grand-O et la notion de complexité dans le pire des cas

Taille maximale du problème, pour un temps donné

Supposons qu’un algorithme dépense un temps f (n) pour résoudre un


problème de taille n et que la machine exécute 1 million d’instructions
à la seconde.

Taille maximale pour résoudre le problème en un temps donné :

f (n) 1 sec. 1 min. 1 heure 1 jour


ln n
√ 3 × 10434294 8 × 10 26057668
�∞ �∞
n 1012 3.6 × 1015 1.3 × 1019 7.5 × 1021
n 106 6 × 107 3.6 × 109 8.64 × 1010
n ln n 87847 3.9 × 106 1.8 × 108 3.9 × 109
n2 1000 7745 60000 293938

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 51 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 92


MASSART Thierry Programmation Transparents - Volume 2

La notation grand-O et la notion de complexité dans le pire des cas

Taille maximale du problème, pour un temps donné

Supposons qu’un algorithme dépense un temps f (n) pour résoudre un


problème de taille n et que la machine exécute 1 million d’instructions
à la seconde.

Taille maximale pour résoudre le problème en un temps donné :

f (n) 1 sec. 1 min. 1 heure 1 jour 1 mois 1 an 1 siècle


n3 100 391 1532 4420 13886 31595 146652
2n 13 16 31 36 41 44 51
n! 9 11 12 13 15 16 17

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 52 / 126

La notation grand-O et la notion de complexité dans le pire des cas

Notation grand-O

Soit T (n) un temps d’exécution mesuré en fonction de la taille n du


problème. Nous pouvons supposer que T (n) est une fonction telle
que :
n est un entier positif ou nul;
T (n) ≥ 0 ∀n.
On va décrire le taux de croissance de ce type de fonction en utilisant
une notation spécifique, la notation O (prononcer “grand-O ”). On parle
aussi de la complexité (sous-entendu “dans le pire des cas”) de la
fonction. Ceci permettra de classer les fonctions – et donc les temps
d’exécution des algorithmes – selon leur type de croissance,
indépendamment des constantes et autres détails techniques.

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 53 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 93


MASSART Thierry Programmation Transparents - Volume 2

La notation grand-O et la notion de complexité dans le pire des cas

Notation grand-O

Les fonctions de complexité sont des fonctions

N → R∗ = {r ∈ R | r ≥ 0} .

Intuition : Soit deux fonctions de complexité f (n) et g (n), on note

f (n) ∈ O (g (n)) ,

ou
f (n) = O (g (n)) ,

si f ne croît pas plus vite que g.

On dit alors que “f est en grand-O de g”.

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 54 / 126

La notation grand-O et la notion de complexité dans le pire des cas

Notation grand-O

“f est en grand-O de g”

Intuition : f ne croît pas plus vite que g.

Définition formelle :

f (n) ∈ O (g (n)) ⇐⇒ ∃n0 ∈ N, c ∈ R t.q. ∀n ≥ n0 : f (n) ≤ c g (n),

où n0 ≥ 0 et c > 0.

Preuves : prouver qu’une fonction f (n) ∈ O (g (n)) reviendra


(principalement) à déterminer n0 et c.

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 55 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 94


MASSART Thierry Programmation Transparents - Volume 2

La notation grand-O et la notion de complexité dans le pire des cas

Notation grand-O

f (n) ∈ O (g (n)) ⇐⇒ ∃n0 ∈ N, c ∈ R t.q. ∀n ≥ n0 : f (n) ≤ c g (n)

c g(n)
Vision asymptotique :

n0 ≥ 0 permet d’éviter les effets


f(n) de bord : ce n’est pas parce
qu’un algorithme est plus rapide
sur des petites valeurs, qu’il le
sera encore asymptotiquement;

c > 0 exprime le fait que les


constantes ne sont pas (très)
importantes
n0
n

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 56 / 126

La notation grand-O et la notion de complexité dans le pire des cas

Notation grand-O

f (n) ∈ O (g (n)) ⇐⇒ ∃n0 ∈ N, c ∈ R t.q. ∀n ≥ n0 : f (n) ≤ c g (n)

Exemples : f(n) € O(n)

Soit f (n) = 1000n et g (n) = n, alors f (n) ∈ O (g (n)). En effet, si


n0 = 0 et c = 1000, alors

f (n) = 1000n ≤ 1000n = 1000g (n), ∀n ≥ 0.

Soit f (n) = 2n2 et g (n) = n2 , alors f (n) ∈ O (g (n)). En effet, si


n0 = 0 et c = 2, alors f (n) ≤ 2g (n), ∀n ≥ 0.

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 57 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 95


MASSART Thierry Programmation Transparents - Volume 2

La notation grand-O et la notion de complexité dans le pire des cas

Notation grand-O

f (n) ∈ O (g (n)) ⇐⇒ ∃n0 ∈ N, c ∈ R t.q. ∀n ≥ n0 : f (n) ≤ c g (n)

Exemples :
Soit f (n) = (n + 1)2 et g (n) = n2 , alors f (n) ∈ O (g (n)). En effet,
si n0 = 1 et c = 4, alors

f (n) = (n + 1)2 = n2 + 2n + 1 ≤ n2 + 2n2 + n2 = 4n2 = 4g (n), ∀n ≥ 1.

Notez que l’inégalité n’est pas vraie si n = 0, d’où n0 = 1.

Remarque : on aurait pu prendre d’autres valeurs (comme n0 = 3


et c = 2)

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 58 / 126

La notation grand-O et la notion de complexité dans le pire des cas

Notation grand-O

Exemples :
n2 ∈ O (n3 ). Soit n0 = 0 et c = 1 : n2 ≤ n3 , ∀n ≥ 0 est vrai (trivial).
/ O (n2 ). Par l’absurde, supposons que n3 ∈ O (n2 ), alors il
n3 ∈
existe deux constantes n0 et c telles que n3 ≤ c n2 , ∀n ≥ n0 .
Cela signifie que

n3
c≥ = n, ∀n ≥ n0 ,
n2
ce qui est une contradiction avec le fait que c est une constante.

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 59 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 96


MASSART Thierry Programmation Transparents - Volume 2

La notation grand-O et la notion de complexité dans le pire des cas

Simplification des expressions grand-O


La notation grand-O permet de simplifier les fonctions de complexité.

Lemme (les facteurs constants ne sont pas importants)


Pour toute valeur constante d > 0 et toute fonction de complexité f (n),
on a
f (n) ∈ O (d · f (n)) et d · f (n) ∈ O (f (n))

Preuve.
1
Soit c = d
et n0 = 0, on a

f (n) = (c · d )f (n) = c (d · f (n)), ∀n ≥ 0,

et donc f (n) ∈ O (df (n)). Pour le deuxième cas, il suffit de choisir


c = d.
n2
Exemples : 1000n2 et 1000
∈ O (n2 ).

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 60 / 126

La notation grand-O et la notion de complexité dans le pire des cas

Simplification des expressions grand-O

Lemme (les termes d’ordre inférieurs sont négligeables)


Soit une fonction de complexité f (n) polynomiale et dont le coefficient
du terme de plus grand degré est positif, c-à-d,

f (n) := ak nk + ak −1 nk −1 + . . . + a2 n2 + a1 n + a0 ,

où ak > 0. Alors, � �
f (n) ∈ O nk

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 61 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 97


MASSART Thierry Programmation Transparents - Volume 2

La notation grand-O et la notion de complexité dans le pire des cas

Simplification des expressions grand-O

Preuve.
Soit n0 = 1 et c = ∑ki=0, max(ai , 0) (somme des coefficients positifs).
Donc c est bien une constante strictement positive car ak > 0. Alors,

f (n) ≤ cnk , ∀n ≥ 1.

En effet, pour chaque coefficient aj :


si aj ≤ 0, on a certainement aj nj ≤ 0, ∀n ≥ 1.
si aj > 0, on a aj nj ≤ aj nk , ∀n ≥ 1 car j ≤ k .

Exemple : Soit f (n) := 3n5 + 10n4 − 100n3 + n + 1. Alors


f (n) ∈ O (n5 ). En effet, si c = 3 + 10 + 1 + 1 = 15, alors f (n) ≤ 15n5 ,
∀n ≥ 1.

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 62 / 126

La notation grand-O et la notion de complexité dans le pire des cas

Simplification des expressions grand-O


Le deuxième lemme exprime simplement que f (n) croît moins vite que
nk , c-à-d
f (n)
lim k = 0.
n→∞ n

On peut généraliser cela : si g (n) croît moins vite que h(n), alors

g (n) + h(n) ∈ O (h(n)).

(on peut négliger ce qui croît moins vite)

Exemple : toute exponentielle croît plus vite que tout polynôme :

np
lim = 0.
n→∞ an
Donc, par exemple,
2n + n3 ∈ O (2n ).

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 63 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 98


MASSART Thierry Programmation Transparents - Volume 2

La notation grand-O et la notion de complexité dans le pire des cas

Simplification des expressions grand-O

Lemme (Transitivité des expressions grand-O )


Grand-O est une relation transitive, c-à-d que si f ∈ O (g ) et g ∈ O (h),
alors f ∈ O (h).

Preuve.
Par définition,
f (n) ≤ c1 g (n), ∀n ≥ n1 ,
g (n) ≤ c2 h(n), ∀n ≥ n2 .
Soit n0 = max(n1 , n2 ) et c0 = c1 · c2 , alors,

f (n) ≤ c1 g (n) ≤ c1 c2 h(n) = c0 h(n), ∀n ≥ n0 .

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 64 / 126

La notation grand-O et la notion de complexité dans le pire des cas

Simplification des expressions grand-O

Pour exprimer la complexité d’une fonction T (n) qui représente le


temps d’exécution d’un algorithme, nous suivrons les deux règles
suivantes :
Simplification. On exprime T (n) en appliquant les règles de
simplification des expressions grand-O . Ainsi, si T (n) = 2n2 + 3,
on dira que l’algorithme est exécuté en un temps O (n2 ) (ou
temps quadratique).
Proximité. Il est clair que si T (n) = 2n2 + 3, alors T (n) ∈ O (n3 )
ou même O (2n ), mais cela n’exprime pas la croissance de T (n).
On dira donc que T (n) ∈ O (n2 ) (on choisit une fonction g dont la
croissance est la plus faible possible et pour laquelle f ∈ O (g ).)

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 65 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 99


MASSART Thierry Programmation Transparents - Volume 2

Evaluer la complexité des algorithmes

Evaluer la complexité des


algorithmes

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 66 / 126

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes

Nous allons appliquer la notation grand-O pour évaluer la complexité


des algorithmes, en suivant quelques règles qui permettent de
simplifier l’analyse et qui découlent directement des simplifications des
expressions grand-O .

Au lieu d’évaluer un temps d’exécution T (n) précisément, les


simplifications des expressions grand-O vont être appliquées
directement, en fonction des structures utilisées dans le programme à
analyser (boucles, instructions conditionnelles, etc.)

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 67 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 100


MASSART Thierry Programmation Transparents - Volume 2

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes


Une instruction simple est une instruction qui réalise une action en un
temps constant, quelle que soit la taille des entrées. Par exemple, les
instructions suivantes sont des instructions simples : affectation,
opérations arithmétiques sur des entiers ou des réels, opérateur [.]
d’accès à un élément d’une liste ou d’une chaîne, instruction return,
etc.
A l’inverse, toute instruction qui peut provoquer un temps d’exécution
non constant n’est pas considérée comme une instruction simple :
appel d’une fonction ou d’une méthode (dont appels récursifs), boucles,
instructions conditionnelles, certains opérateurs appliqués sur des
objets de taille variable (comme l’opérateur “slice” sur les listes, son
coût en temps peut ne pas être constant, dépendant de la taille de la
tranche), etc.

Règle (Instruction simple)


Par définition, une instruction simple à un coût en temps O (1) (temps
constant).

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 68 / 126

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes

On “additionne” la complexité de morceaux de code en ne gardant que


la complexité la plus grande.

Règle (Sommation)
Soit deux parties A et B d’un programme, exécutées de façon
séquentiellea . Supposons TA (n) = O (fA (n)) et TB (n) = O (fB (n)).
Alors

TA (n) + TB (n) = O (fB (n) + fA (n)) = O (max (fB (n), fA (n)).


a
l’une après l’autre, la partie B n’est pas inclue dans le travail de la partie A et
vice-versa.

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 69 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 101


MASSART Thierry Programmation Transparents - Volume 2

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes

Exemple

def swap(a, b):


temp = b
b = a
a = temp
return (a, b)

Le temps de cet algorithme est en O (1) car sa complexité est


O (1) + O (1) + O (1) + O (1).

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 70 / 126

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes

Règle (Appel de fonction ou méthode)


Le coût d’un appel à une fonction ou une méthode est équivalent au
coût de l’exécution du corps de la fonction.

Remarque : on expliquera par après comment évaluer le coût d’une


fonction récursive, mais la règle ci-dessus reste d’application.

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 71 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 102


MASSART Thierry Programmation Transparents - Volume 2

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes

Exemples

(a, b) = swap(a, b)
(a, b) = swap(a, b)

Le temps de cet algorithme (totalement inutile) est O (1).

def f(n):
res = g(n)
res += h(n)
return res

Imaginons que le temps d’exécution de g(n) soit en O (n) et celui de


h(n) soit en O (n2 ), alors le temps d’exécution de f(n) est en
O (n) + O (n2 ) + O (1) = O (n2 ).

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 72 / 126

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes


Complexité des opérations sur les listes (d’après le site wiki python4 ) :

obtenir la longueur de la liste (n = len(t)) : O (1)


accéder à un élément d’un indice donné (x = t[i]) : O (1)
ajouter un élément en fin de liste (t.append(3)) : O (1)
insérer un élément (pire des cas: au début de la liste)
(t.insert(0,8)) : O (n)

supprimer un élément (pire des cas: au début de la liste)


(t.remove(4)) : O (n)

insérer un élément (meilleur des cas: en fin de liste)


(t.insert(len(t),8)) : O (1)

supprimer un élément (meilleur des cas: en fin de liste) : O (1)

4
http://wiki.python.org/moin/TimeComplexity
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 73 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 103


MASSART Thierry Programmation Transparents - Volume 2

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes

Complexité des opérations sur les listes – suite :


t[a:b]
obtenir une tranche de taille k : O (k )
copier une liste : O (n) (par ex., la méthode extend copie la seconde
liste à la fin de la première, donc O (k ) si la seconde liste à k éléments)

concaténer : O (n1 + n2 )
multiplier (= copier) une liste k fois : O (kn)
rechercher un élément dans la séquence (x in s) : O (n)
trouver le min. ou le max. d’une séquence (x = min(t)) : O (n)
l’appel à range est linéaire en le nombre d’éléments dans la liste
retournée.

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 74 / 126

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes

Exemples

t.append(3)
print 3 in t
t2 = t * 2

Ce morceau de code a un temps linéaire :


O (1) + O (n) + O (2n) = O (n)
t2 = t * len[t]

Cette instruction a un temps quadratique : O (n · n) = O (n2 )

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 75 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 104


MASSART Thierry Programmation Transparents - Volume 2

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes


Règle (Boucles for)
Le temps d’exécution d’une boucle for est équivalent à

complexité de la création de la séquence +


# d’itérations (dans le pire des cas) × complexité du corps de la boucle

Remarques :
la notation grand-O est utilisée dans ce chapitre pour calculer les
temps d’exécution dans le pire des cas. Or, on peut sortir d’une
boucle de différentes façons (break, return, condition d’arrêt),
d’où le pire des cas sur le # d’itérations.
n · O (1) = O (n) et pas O (1) : la règle de sommation ne peut pas
s’appliquer car n n’est pas constant.
pour multiplier une expression grand-O par quelque chose qui
dépend de la taille du problème : f (n) · O (g (n)) = O (f (n) · g (n)).
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 76 / 126

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes

Exemple
1 def sum_all(liste):
2 res = 0
3 for item in liste:
4 res += item
5 return res

Le temps de cet algorithme est O (n), où n est le nombre d’éléments


de la liste.
Preuve.
La liste est déjà créée (car passée en paramètre), il n’y a donc pas de
coût lié à son utilisation. Les instructions 2 et 5 sont en O (1). La
boucle est exécutée n fois. Le temps d’exécution du corps de la
boucle (instr. 4) est en O (1). Donc le temps de cet algorithme est
O (1) + n · O (1) = O (n).

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 77 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 105


MASSART Thierry Programmation Transparents - Volume 2

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes

Exemple
1 def sum_1_to_n(n):
2 res = 0
3 for i in range(1, n + 1):
4 res += i
5 return res

Le temps de cet algorithme est O (n), où n est le nombre d’éléments


de la liste.
Preuve.
La création de la séquence (fonction range) est en O (n). Les
instructions 2 et 5 sont en O (1). La boucle est exécutée n fois. Le
temps d’exécution du corps de la boucle (instr. 4) est en O (1). Donc
le temps de cet algorithme est O (1) + O (n) + n · O (1) = O (n).

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 78 / 126

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes

Règle (Boucles while)


Le temps d’exécution d’une boucle while est équivalent à

# d’itérations (dans le pire des cas) × (complexité de l’évaluation de la


condition + complexité du corps de la boucle)

Remarque :
à chaque itération, la condition est évaluée, donc il faut en tenir
compte si celle-ci a un coût > O (1).

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 79 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 106


MASSART Thierry Programmation Transparents - Volume 2

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes


Exemple
1 def sum_1_to_n(n):
2 t = range(1, n + 1)
3 i = 0
4 res = 0
5 while i < n: n*(O(1)) = O(n)
6 res += t[i]
7 i += 1
8 return res

Le temps de cet algorithme est O (n), où n est le nombre d’éléments


de la liste.
Preuve.
Le temps de l’instr. 2 est en O (n). Les instructions 3, 4 et 8 sont en
O (1). La boucle est exécutée n fois et son corps est en O (1). Le
temps de l’évaluation de la condition est en O (1). Donc le temps de
cet algorithme est O (n) + n · (O (1) + O (1)) = O (n).

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 80 / 126

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes


Exemple
1 def is_include(t1, t2):
2 i = 0
3 n1 = len(t1)
4 while i < len(t1) and t1[i] in t2: n1*( (O(1) + O(n2)) + O(1)) = n1*(O(n2))
5 i += 1
6 return i == n1

Cet algorithme retourne vrai ssi les éléments de t1 sont tous présents
dans t2 . Le temps de cet algorithme est O (n1 · n2 ), où n1 et n2 sont les
tailles de t1 et t2 .

Preuve.
Le temps des instr. 2, 3 et 6 sont en O (1). La boucle est exécutée n1
fois et son corps est en O (1). Le temps de l’évaluation de la condition
est en O (n2 ). Donc le temps de cet algorithme est
O (1) + n1 · O (n2 ) = O (n1 · n2 ).

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 81 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 107


MASSART Thierry Programmation Transparents - Volume 2

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes

Exercice
1 def insertion_sort(t):
2 n = len(t)
3 for i in range(1,n):
4 clef = t[i]
5 j = i - 1
6 while j >= 0 and t[j] > clef:
7 t[j+1] = t[j]
8 j = j - 1
9 t[j+1] = clef

Quelle est la complexité dans le pire des cas du tri par insertion, en
fonction du nombre n d’éléments à trier ?

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 82 / 126

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes

Solution : le tri par insertion est en temps O (n2 )


l’instruction 1 est en O (1)
considérons d’abord la boucle intérieure (while) : le corps de
cette boucle est en O (1) (instr. 7 et 8) et, dans le pire des cas, on
sort de la boucle en ayant décalé tous les éléments, c-à-d quand
le test j ≥ 0 n’est plus vrai. Comme j est initialisé à i − 1, dans le
pire des cas, on exécutera i fois la boucle (le nombre d’éléments
entre l’indice 0 et l’indice i − 1 est i). Le temps de l’évaluation de
la condition est en O (1). Sa complexité est donc O (i ) (dans le
pire des cas i = n − 1 mais gardons cette expression en i pour le
moment car il représente l’indice de la boucle extérieure)

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 83 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 108


MASSART Thierry Programmation Transparents - Volume 2

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes

Solution : le tri par insertion est en temps O (n2 )


considérons maintenant la boucle extérieure (for) : elle est
exécutée exactement n − 1 fois. L’appel à range est en O (n).
Toutes les instructions du corps de la boucle sont en O (1), sauf
la boucle intérieure qui est en O (i ). On peut calculer le temps
total de la boucle comme suit grâce à la formule d’Euler :
� � � �
n−1 n
n2 + n
O (n)+ ∑ O (i ) = O (0 + 1 +. . .+ n − 1 + n) = O ∑i =O
i =0 i =1 2

On en conclut que le temps du tri par insertion est en O (n2 ).

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 84 / 126

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes

Et si la liste est déjà triée ?


alors l’analyse de la boucle intérieure montre qu’elle sera
exécutée en temps constant (le test t [j ] > clef étant faux pour
chaque i).
l’analyse complète donne alors un temps en O (n) dans le
meilleur des cas.
cette analyse de la complexité explique les temps CPU observés.

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 85 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 109


MASSART Thierry Programmation Transparents - Volume 2

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes

Dans le cas d’une instruction conditionnelle, on ne garde que le plus


grand temps d’exécution de toutes les branches.

Règle (Instructions conditionnelles)


Soit une instruction conditionnelle avec k branches et � conditions à
évaluer, et soit Tk (n) le temps d’exécution de la k ième branche. Si
Ti (n) ∈ O (fi (n)) pour i = 1, . . . , k , et si l’évaluation de la condition j
est en O (gj (n)), pour j = 1, . . . , � alors
� �
T (n) ∈ O max(fi (n)) + max(gj (n)) ,
i j

où T (n) est le temps d’exécution de l’instruction conditionnelle


complète.

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 86 / 126

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes

Exemple : attention aux opérations et appels de fonctions / méthodes


cachés dans les conditions

if x in t:
print ’x est dans t’
O(max(O(n), O(1), O() + max(O(1), O(1)))
else:
print ’x pas dans t’

les deux branches sont en O (1)


le test du if est en O (n) (où n = len(t))
la complexité est donc O (n) + max(O (1), O (1)) = O (n)

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 87 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 110


MASSART Thierry Programmation Transparents - Volume 2

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes

Exemple : le temps du tri par sélection est en O (n2 ) où n est le


nombre d’éléments de la liste à trier (et il n’y a pas de cas meilleur que
d’autres).
1 def selection_sort(t):
2 n = len(t)
3 for i in range(n-1):
4 small = i
5 for j in range(i+1,n):
6 if t[j] < t[small]:
7 small = j
8 (t[i], t[small]) = (t[small], t[i])

Preuve.
Au tableau.

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 88 / 126

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes récursifs

Analyser le temps d’exécution d’une fonction récursive demande plus


de travail :
on associe à chaque appel de f un temps T (n) inconnu qui définit
le temps de f en fonction de n (taille ou valeur des arguments).
on établit ensuite une définition récursive qui associe T (n) à un
appel de f avec des arguments plus petits (ou à une autre
fonction que f , en fonction du code à analyser) :
� Base. La taille des arguments est suffisamment réduite pour
qu’aucun appel récursif ne soit fait par f .
� Réc. La taille des arguments est suffisamment grande pour que
des appels récursifs apparaissent. On suppose que tout appel
récursif ultérieur effectué par f sera effectué avec des arguments
plus petits (grâce à une preuve d’arrêt par ex.).

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 89 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 111


MASSART Thierry Programmation Transparents - Volume 2

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes récursifs


La relation de récurrence définissant T (n) s’établit grâce à l’examen du code
de f , selon les actions suivantes :
pour chaque appel récursif à f , on utilise T (k ) comme temps
d’exécution de l’appel, où k est la taille appropriée des arguments.
on évalue le temps du corps de f comme précédemment, mais en
gardant les termes T (k ) sous la forme d’inconnues. On doit analyse f
deux fois : (1) en supposant que la taille n est suffisamment petite pour
tomber dans un cas de base; (2) en supposant que n a une taille plus
grande. On obtient alors deux expressions pour le temps d’exécution
de f .
Dans les expressions obtenues, on remplace les termes grand-O
comme O (h(n)) par c · h(n) où c est une constante particulière.
Si a est une valeur de base pour la taille entrée, on initialise T (a) avec
l’expression résultant de l’étape précédente. On initialise aussi T (n)
avec l’expression obtenue pour le cas récursif.
Le temps d’exécution complet est déterminé par la résolution de cette
relation de récurrence.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 90 / 126

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes récursifs


Exemple
1 def sum_rec(n):
2 if n == 1: O(max(O(1), O(1)) + max(O(1), O(T(n-1))
3 return 1
4 else:
5 return sum_rec(n-1) + n

les appels récursifs sont réalisés avec des arguments plus petits
(n − 1).
pour la base de la définition induction de T (n), on choisit n = 1.
Dans ce cas, la complexité de T (1) est en O (1) (instr. 2 et 3).
si n > 1, seules les instr. 2 et 4 et 5 sont exécutées. Les
instructions 2 et 4 sont en O (1) et le temps d’exécution de l’instr.
5 est T (n − 1).
on peut donc définir T (n) récursivement comme suit :

T (1) = O (1),
T (n) = T (n − 1) + O (1), ∀n > 1.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 91 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 112


MASSART Thierry Programmation Transparents - Volume 2

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes récursifs

on remplace les expressions O (h(n)) par c · h(n) (une constante


pour chaque expression) :

T (1) = a,
T (n) = T (n − 1) + b, ∀n > 1.

pour résoudre cette récurrence, on essaye d’exprimer T (n) en


fonction de n et des constantes. Pour les premières valeurs on a :

T (1) = a,
T (2) = T (1) + b = a + b,
T (3) = T (2) + b = a + 2b,
T (4) = T (3) + b = a + 3b.

à ce stade, on devine que T (n) = a + (n − 1)b, ∀n ≥ 1.


O(T(n)) = O(n)
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 92 / 126

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes récursifs

une preuve par récurrence simple permet de prouver que si


T (n) = a + (n − 1)b, ∀n ≥ 1, alors

T (1) = a,
T (n) = T (n − 1) + b, ∀n > 1.

on conclut que T (n) ∈ O (n) puisque a et b sont des constantes


(> 0).
Intuition : la complexité d’un algorithme récursif est la complexité
d’une étape multipliée par le nombre d’étapes (la difficulté réside dans
le calcul de ce nombre d’étapes).

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 93 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 113


MASSART Thierry Programmation Transparents - Volume 2

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes récursifs


Tri par fusion.
Avant d’analyser le tri par fusion, et maintenant que l’on sait qu’ajouter
ou supprimer un élément en début de liste est en O (n) et en fin de
liste en O (1), on remarque que les instructions des cas récursifs de la
fonction merge peuvent être améliorées.
1 def merge(t1, t2):
2 if len(t1) == 0:
3 return t2
4 elif len(t2) == 0:
5 return t1
6 elif t1[0] < t2[0]:
7 return [t1[0]] + merge(t1[1:], t2)
8 else:
9 return [t2[0]] + merge(t1, t2[1:])

Dans l’instr. 7, le coût du “slice” est O (len(t1 ) − 1) et la concaténation


est en O (len(t1 ) + len(t2 )), ce qui donne du O (n) sans même compter
l’appel récursif.

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 94 / 126

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes récursifs

On modifie donc le code pour travailler plutôt en fin des listes.


1 def merge(t1, t2):
2 if len(t1) == 0:
3 return t2
4 elif len(t2) == 0:
5 return t1
6 elif t1[-1] > t2[-1]:
7 last = t1.pop()
8 new = merge(t1, t2)
9 new.append(last)
10 return new
11 else:
12 last = t2.pop()
13 new = merge(t1, t2)
14 new.append(last)
15 return new

A part les appels récursifs, les autres instructions sont donc


maintenant en O (1).

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 95 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 114


MASSART Thierry Programmation Transparents - Volume 2

Evaluer la complexité des algorithmes

Comparaison d’algorithmes

Cette modification améliore encore le temps CPU du tri par fusion :


Temps affiches en msec
n t1: sel ins mer t2: sel ins mer t3: sel ins mer
100 0.96 0.05 0.71 0.97 1.79 0.64 0.94 0.95 0.89
200 3.40 0.10 1.52 3.46 6.93 1.37 3.36 3.44 1.97
300 7.64 0.15 2.37 8.02 15.46 2.13 7.52 7.43 3.15
400 13.41 0.20 3.26 13.80 27.59 3.02 13.37 14.08 4.38
500 21.77 0.30 4.55 21.85 42.71 3.95 20.82 22.43 5.72
600 30.39 0.30 5.21 31.13 62.16 4.64 30.34 32.18 7.19
700 41.54 0.35 6.06 42.85 87.28 5.46 41.03 45.52 8.48
800 53.88 0.40 7.10 56.05 118.52 6.33 54.20 57.36 9.92
900 68.56 0.45 7.88 71.28 142.15 7.40 68.54 71.84 11.37
1000 84.56 0.50 8.77 87.87 177.36 8.46 85.01 90.58 12.05

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 96 / 126

Evaluer la complexité des algorithmes

Evaluer la complexité des algorithmes récursifs


Tri par fusion.
1 def merge_sort(t):
17 def merge(t1, t2):
2 n = len(t)
18 if len(t1) == 0:
3 if n > 1:
19 return t2
4 (t1, t2) = split(t)
20 elif len(t2) == 0:
5 t1 = merge_sort(t1)
21 return t1
6 t2 = merge_sort(t2)
22 elif t1[-1] > t2[-1]:
7 return merge(t1, t2)
23 last = t1.pop()
8 else:
24 new = merge(t1, t2)
9 return t
25 new.append(last)
10
26 return new
11 def split(t):
27 else:
12 """ precondition: len(t) >= 2 """
28 last = t2.pop()
13 mid = len(t) / 2
29 new = merge(t1, t2)
14 t1 = t[:mid]
30 new.append(last)
15 t2 = t[mid:]
31 return new
16 return (t1, t2)

La complexité dans le pire des cas du tri par fusion (version améliorée)
est en O (n log2 n), en fonction du nombre n d’éléments à trier.

Preuve.
Au tableau.

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 97 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 115


MASSART Thierry Programmation Transparents - Volume 2

Complexité des opérations sur les dictionnaires

Complexité des opérations sur


les dictionnaires

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 98 / 126

Complexité des opérations sur les dictionnaires

Complexité des opérations sur les dictionnaires

Les dictionnaires ont une structure (sous-jacente) en Python qui leur


permet des performances très intéressantes pour accéder à un
élément en moyenne (mais pas toujours dans le pire des cas). Ils
utilisent ce qu’on appelle une table de hachage.

En pratique, la complexité en moyenne sera souvent atteinte.

Moyenne Pire des cas


Copie O (n) O (n)
Accès à un élément (via la clef) O (1) O (n) print(d[k])
Mettre à jour un élément (via la clef) O (1) O (n)
Supprimer un élément (via la clef) O (1) O (n)
Obtenir une liste (de clefs ou de valeurs O (n) O (n)
Le nombre des paires dans le dictionnaire est n.
Source : http://wiki.python.org/moin/TimeComplexity

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 99 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 116


MASSART Thierry Programmation Transparents - Volume 2

Complexité des opérations sur les dictionnaires

Complexité des opérations sur les dictionnaires

Pour tester cette complexité en moyenne, nous allons réaliser le test


suivant :
lire le fichier words.txt pour créer une liste t et un dictionnaire d
contenant les 113809 mots;
utiliser le module cpu (cf. Chap. 9) pour évaluer le temps
d’exécution de la recherche d’un mot :
� avec une recherche linéaire dans la liste (complexités moyenne et
pire des cas en O (n));
� avec une recherche dichotomique dans la liste, puisque les mots
sont triés (complexités en moyenne et pire des cas en O (log2 n));
� avec accès direct dans un dictionnaire (complexité en moyenne
en O (1), dans le pire des cas en O (n)).

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 100 / 126

Complexité des opérations sur les dictionnaires

Complexité des opérations sur les dictionnaires

Création des données :


file = open(’words.txt’)

t = []
d = {}
for line in file:
word = line.strip()
t.append(word)
d[word] = ’’

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 101 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 117


MASSART Thierry Programmation Transparents - Volume 2

Complexité des opérations sur les dictionnaires

Complexité des opérations sur les dictionnaires

Recherche linéaire :
def linear_search(t, w):
for word in t:
if word == w:
return True
return False

Recherche dichotomique : voir Travaux Pratiques.

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 102 / 126

Complexité des opérations sur les dictionnaires

Complexité des opérations sur les dictionnaires

Recherche dans le dictionnaire :


def dico_search(d, w):
return w in d

Pour obtenir un mot aléatoire :


import random

def random_word(t):
index = random.randint(0,len(t)-1)
return t[index]

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 103 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 118


MASSART Thierry Programmation Transparents - Volume 2

Complexité des opérations sur les dictionnaires

Complexité des opérations sur les dictionnaires

Résultats obtenus en faisant appel au module cpu : on fait deux types


de tests (mots existants ou non).

Temps affiches en usec

Tests sur mots aleatoires (existants):


linear dicho dict
5205.32 14.29 4.40

Tests sur mots non presents:


linear dicho dict
11799.67 10.55 0.43

Remarque : dans les tests sur les mots aléatoires, le coût de la


création d’un mot aléatoire est inclus dans les temps ci-dessus, mais il
est le même pour les 3 méthodes.

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 104 / 126

Comparaison de différents algorithmes

Comparaison de différents
algorithmes

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 105 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 119


MASSART Thierry Programmation Transparents - Volume 2

Comparaison de différents algorithmes

Comparaison de différents algorithmes


Le tri par fusion est donc bien plus rapide que les tris par sélection et
insertion.

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 106 / 126

Comparaison de différents algorithmes

Comparaison de différents algorithmes

Calcul de an .
1 def expo(a, n):
2 r = 1
3 while n > 0:
1 def expo(a, n):
4 if n % 2 == 0:
2 r = 1
5 a = a * a
3 for i in range(n):
6 n = n / 2
4 r = a * r
7 else:
5 return r
8 r = r * a
9 n = n - 1
10 return r

La complexité du calcul de an est en O (n) (version simple) et en


O (log2 n) (version améliorée).
Preuve.
Au tableau.

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 107 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 120


MASSART Thierry Programmation Transparents - Volume 2

Comparaison de différents algorithmes

Comparaison de différents algorithmes

Calcul de Fibonacci.
1 def fib_rec(n):
2 if n == 0: 1 def fib_iter(n):
3 return 0 2 f = [0, 1]
4 elif n == 1: 3 for i in range(2,n+1):
5 return 1 4 f.append(f[i-1] + f[i-2])
6 else: 5 return f[n]
7 return fib_rec(n-1) + fib_rec(n-2)

La complexité du calcul de fn est en√ O (ϕn ) (version récursive) et en


O (n) (version itérative), où ϕ = 1+2 5 � 1.618 est le nombre d’or.
Idée de preuve.
Au tableau (pas une preuve formelle : explique l’intuition).

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 108 / 126

Comparaison de différents algorithmes

Comparaison de différents algorithmes


La complexité du calcul récursif de Fibonacci est donc exponentielle !

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 109 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 121


MASSART Thierry Programmation Transparents - Volume 2

Comparaisons de la comlexité d’algorithmes sur des dictionnaires

Comparaisons de la comlexité
d’algorithmes sur des
dictionnaires

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 110 / 126

Comparaisons de la comlexité d’algorithmes sur des dictionnaires

Trouver la (les) clef(s) d’une valeur donnée

def get_keys(d, value):


t = []
for k in d:
if d[k] == value: creation+ #iter*complex_interieur
t.append(k) O(n) + n*( O(n) + O(1)) = O(n) + n*O(n) = O(n²)
return t

>>> d = histogram(’evenement’)
>>> d
{’m’: 1, ’n’: 2, ’e’: 4, ’t’: 1, ’v’: 1}
>>> get_keys(d,4)
[’e’]
>>> get_keys(d,1)
[’m’, ’t’, ’v’]

Complexité : pour rappel, append est en O (1). On a donc un temps en


O (n) en moyenne et en O (n2 ) dans le pire des cas (dû à d[k]).

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 111 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 122


MASSART Thierry Programmation Transparents - Volume 2

Comparaisons de la comlexité d’algorithmes sur des dictionnaires

Inverser les clefs et les valeurs d’un dictionnaire

def invert_dict(d):
inv = {}
for k in d:
val = d[k]
if val not in inv:
inv[val] = [k]
else:
inv[val].append(k)
return inv

>>> d = histogram(’evenement’)
>>> inv_d = invert_dict(d)
>>> inv_d
{1: [’m’, ’t’, ’v’], 2: [’n’], 4: [’e’]}

Complexité : O (n) en moyenne et O (n3 ) dans le pire des cas.

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 112 / 126

Comparaisons de la comlexité d’algorithmes sur des dictionnaires

Retour sur Fibonacci amélioré

known = {0 : 0, 1 : 1}

def fib_dict(n):
if n in known:
return known[n]
res = fib_dict(n-1) + fib_dict(n-2)
known[n] = res
return res

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 113 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 123


MASSART Thierry Programmation Transparents - Volume 2

Comparaisons de la comlexité d’algorithmes sur des dictionnaires

Comparaison des temps CPU des 3 méthodes


Temps affiches en msec
n: iter: rec: dict:
5 0.003 0.014 0.000
10 0.006 0.062 0.000
15 0.008 0.665 0.000
20 0.011 7.370 0.000
25 0.013 80.641 0.000
30 0.015 901.055 0.000
100 0.050 ---- 0.000
300 0.148 ---- 0.000
500 0.297 ---- 0.000
700 0.373 ---- 0.000
900 0.496 ---- 0.000
1100 0.628 ---- 0.000
1300 0.773 ---- 0.000
1500 0.930 ---- 0.000
1700 1.150 ---- 0.000
1900 1.410 ---- 0.000

Comment expliquer cela ? Revenons d’abord sur la notion de variable


globale.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 114 / 126

Comparaisons de la comlexité d’algorithmes sur des dictionnaires

Variables globales

Quand on définit une variable en dehors de toute fonction, elle est


créée une fois pour toute et est utilisable dans toutes les fonctions du
module (ou dans d’autres modules grâce à la notation point, per ex.
math.pi.

Une variable définie dans une fonction ou un paramètre d’une fonction


est une variable locale : sa portée est limitée à la fonction dans lequel
elle est définie.

Par contre, la portée d’une variable définie en dehors de toute fonction


est globale, d’où le nom de variable globale.

Remarque : on peut toujours se passer de variables globales, et en


général on essaye de les éviter car il peut y avoir des effets de bord,
un manque de clarté (voir ci-après).

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 115 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 124


MASSART Thierry Programmation Transparents - Volume 2

Comparaisons de la comlexité d’algorithmes sur des dictionnaires

Variables globales

Exemple : a est globale, b et c sont locales à la fonction f.


>>> a = 3
>>> def f(b):
c = 2
print ’a:’, a, ’b:’, b, ’c:’, c

>>> f(4)
a: 3 b: 4 c: 2
>>> print a
3
>>> print b
NameError: name ’b’ is not defined
>>> print c
NameError: name ’c’ is not defined

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 116 / 126

Comparaisons de la comlexité d’algorithmes sur des dictionnaires

Variables globales
Que se passe-t-il si on réassigne une variable dans une fonction et
dont le nom est le nom d’une variable globale ?
>>> a = 3
>>> def g():
a = 7
print a

>>> g()
7
>>> a
3

la variable globale n’est pas modifiée


en réalité, on a recréé (assignation = création ou mise à jour) une
variable locale du même nom
on a déjà vu ce phénomène avec les paramètres (qui sont des variables
locales)
si on ne crée pas de variable locale : on utilise la variable globale,
sinon, la variable locale du même nom “cache” la variable globale.

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 117 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 125


MASSART Thierry Programmation Transparents - Volume 2

Comparaisons de la comlexité d’algorithmes sur des dictionnaires

Variables globales
Que se passe-t-il si on applique une méthode sur une variable globale
qui est mutable ?
>>> x = [1, 2, 3]
>>> def h():
x.append(4)

>>> h()
>>> x
[1, 2, 3, 4]
>>> def h2():
x = []

>>> h2()
>>> x
[1, 2, 3, 4]

la valeur de la variable globale est modifiée dans h (on ne crée pas de


variable locale puisqu’il n’y a pas d’assignation).

dans h2 par contre, on crée un variable locale.

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 118 / 126

Comparaisons de la comlexité d’algorithmes sur des dictionnaires

Variables globales

Comprenez-vous cette erreur ? Comment la corriger ?


>>> count = 0
>>> def f():
count += 1

>>> f()
UnboundLocalError: local variable ’count’ referenced before assignment

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 119 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 126


MASSART Thierry Programmation Transparents - Volume 2

Comparaisons de la comlexité d’algorithmes sur des dictionnaires

Variables globales
Les fonctions globals et locals peuvent être utilisées pour connaître
les variables globales ou locales disponibles à un endroit du code
(elles retournent un dictionnaire).

Soit le fichier scope.py :


def f(b):
a = 4
c = 3
print ’Dans f: var. globales: ’, globals(), ’\n’
print ’Dans f: var. locales: ’, locals(), ’\n’

def g():
if ’a’ in globals(): # clefs sous forme de str
print ’Dans g: a =’, a

a = 1
f(2)
g()

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 120 / 126

Comparaisons de la comlexité d’algorithmes sur des dictionnaires

Variables globales

La commande python scope.py produit :

Dans f: var. globales: {’a’: 1, ’g’: <function g at 0x77370>,


’f’: <function f at 0x773b0>, ’__builtins__’: <module ’__builtin__’ (built-in)>,
’__file__’: ’scope.py’, ’__package__’: None,
’__name__’: ’__main__’, ’__doc__’: None}

Dans f: var. locales: {’a’: 4, ’c’: 3, ’b’: 2}

Dans g: a = 1

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 121 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 127


MASSART Thierry Programmation Transparents - Volume 2

Comparaisons de la comlexité d’algorithmes sur des dictionnaires

Quelques algorithmes sur des dictionnaires


Analysons le comportement de la version “dictionnaire” de Fibonacci :
known = {0 : 0, 1 : 1}

def fib_dict(n):
if n in known:
return known[n]
res = fib_dict(n-1) + fib_dict(n-2)
known[n] = res
return res

la première fois que l’on fait appel à cette fonction, toutes les valeurs de
la séquence jusqu’à n sont stockées dans known.
cela signifie que si j’exécute la même fonction à nouveau avec un
paramètre m ≤ n, le seul travail à réaliser est d’aller chercher la valeur
dans le dictionnaire (O (1) en moyenne).
si j’exécute la même fonction à nouveau avec un paramètre m > n, le
seul travail à réaliser est de calculer les m − n nouveaux nombres de la
séquence (O (m − n) en moyenne).

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 122 / 126

Comparaisons de la comlexité d’algorithmes sur des dictionnaires

Quelques algorithmes sur des dictionnaires


Temps affiches en msec
n: iter: rec: dict:
5 0.003 0.014 0.000
10 0.006 0.062 0.000
15 0.008 0.665 0.000
20 0.011 7.370 0.000
25 0.013 80.641 0.000
30 0.015 901.055 0.000
100 0.050 ---- 0.000
300 0.148 ---- 0.000
500 0.297 ---- 0.000
700 0.373 ---- 0.000
900 0.496 ---- 0.000
1100 0.628 ---- 0.000
1300 0.773 ---- 0.000
1500 0.930 ---- 0.000
1700 1.150 ---- 0.000
1900 1.410 ---- 0.000

pour chaque valeur de n dans ces tests, 3 séries de 1000 appels sont
réalisées pour calculer le temps moyen, dans la meilleure série;
cela explique que nous avons un temps inférieur à 0 µs !
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 123 / 126
il faut donc interpréter ces résultats avec prudence : la version avec
INFO-F-101_B dictionnairePUB
“triche” en conservant
Cours-Librairie, en mémoire
av. P. Héger 42, B-1000 un “cache” (qui peut être
Bruxelles 128
grand et saturer la mémoire).
MASSART Thierry Programmation Transparents - Volume 2

Comparaisons de la comlexité d’algorithmes sur des dictionnaires

Longs entiers
Si vous calculez le 50ième nombre de Fibonacci, vous obtenez ceci :
>>> fib_dict(50)
12586269025L

Le L signifie qu’il s’agit d’un entier de type “long”.


>>> type(10), type(10L)
(<type ’int’>, <type ’long’>)
>>> 1000 * 1000
1000000
>>> 1000000 * 1000000
1000000000000L

un entier de type int a une taille limitée5 .


dès qu’un nombre entier dépasse cette limite, Python le transforme en
long.
un entier de type long a une taille arbitrairement grande.
quand les entiers deviennent très grands, les opérations
mathématiques consomment plus d’espace et de temps.
5
Plus grand int sur une machine “32bits” avec Python 2.6 :
31
2 − 1 = 2147483647 (peut varier en fonction de la machine / version Python)
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 124 / 126

Glossaire

Glossaire

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 125 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 129


MASSART Thierry Programmation Transparents - Volume 2

Glossaire

Glossaire

temps CPU : temps mis par le processeur pour exécuter un programme, le temps CPU
varie d’une machine à l’autre et est difficile à estimer précisément.
instruction simple : instruction qui est exécutée en temps constant.
notation grand-O : notation qui permet d’exprimer la croissance d’une fonction, de
manière simplifiée.
complexité : la complexité d’un algorithme est la croissance, dans le pire des cas, de son
temps d’exécution. Elle est exprimée grâce à la notation grand-O en fonction de la taille
des entrées.

Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 126 / 126

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 130


MASSART Thierry Programmation Transparents - Volume 2

Chapitre 12. Fichiers et exceptions

Contenu du chapitre

1 Lecture et écriture de fichiers

2 Noms de fichiers et chemins

3 Sauvegarder des objets

4 Exceptions

5 Gérer les exceptions

6 Glossaire

Programmation (Chap. 12) Fichiers et exceptions 1 / 50

Lecture et écriture de fichiers

Lecture et écriture de fichiers

Programmation (Chap. 12) Fichiers et exceptions 2 / 50

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 131


MASSART Thierry Programmation Transparents - Volume 2

Lecture et écriture de fichiers

Persistence
Les programmes réalisés jusqu’à présent s’exécutent pour un court
laps de temps et produisent des sorties à l’écran. Quand ils se
terminent, leurs données disparaissent.

D’autres programmes sont persistants : ils tournent longtemps (ou tout


le temps); gardent une partie de leurs données de manière
permanente (sur un disque dur par ex.); et s’ils sont interrompus et
relancés, ils reviennent là où ils en étaient.

Exemples : systèmes d’exploitation, serveurs web, programmes de


traitement de texte, etc.

Un moyen simple de conserver des données (objet de ce chapitre) :


lire et écrire dans des fichiers “textes” ou sauvegarder l’état du
programme dans une base de données simple (modules pickle et
shelve).

Programmation (Chap. 12) Fichiers et exceptions 3 / 50

Lecture et écriture de fichiers

Lecture dans un fichier texte

Un fichier texte est une séquence de caractères stockée sur un media


permanent comme un disque dur, un CD-ROM ou une clef USB.

Nous avons vu comment lire un fichier ligne par ligne grâce à la


méthode readline ou une boucle for.

Script readLines.py :
import sys

filename = sys.argv[1]
f = open(filename)

firstLine = f.readline()
print ’First line:’, firstLine.strip()

for i, line in enumerate(f):


print ’Line’, i, ’:’, line.strip()

Programmation (Chap. 12) Fichiers et exceptions 4 / 50

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 132


MASSART Thierry Programmation Transparents - Volume 2

Lecture et écriture de fichiers

Lecture dans un fichier texte

La lecture est séquentielle, c-à-d que chaque ligne est lue l’une après
l’autre et que chaque appel à readline ou chaque itération de la
boucle for avance la position courante de la lecture d’une ligne dans
le fichier.

Fichier example.txt :
Un
Deux
Trois

Résultat de python readLines.py example.txt :


First line: Un
Line 0 : Deux
Line 1 : Trois

Programmation (Chap. 12) Fichiers et exceptions 5 / 50

Lecture et écriture de fichiers

Lecture dans un fichier texte

Alternativement, on peut lire l’entièreté d’un fichier avec la méthode


read.
Script readAll.py :
import sys

filename = sys.argv[1]
f = open(filename)

content = f.read()
print ’content:’, repr(content)

Remarque : la fonction repr prend n’importe quel objet en paramètre


et affiche une représentation de celui-ci sous forme de chaîne. Dans
le cas des chaînes de caractères, cela permet de voir les caractères
spéciaux (comme \n).

Programmation (Chap. 12) Fichiers et exceptions 6 / 50

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 133


MASSART Thierry Programmation Transparents - Volume 2

Lecture et écriture de fichiers

Lecture dans un fichier texte

Résultat de python readAll.py example.txt :


content: ’Un\nDeux\nTrois’

Toutes les méthodes des chaînes (comme split) sont alors


disponibles pour travailler sur le contenu, mais si le fichier est très
grand, il vaut mieux travailler ligne par ligne.

Remarque : il existe également la méthode readlines (notez le “s”)


qui retourne une liste dont les éléments sont les lignes du fichier
(chaînes).

Programmation (Chap. 12) Fichiers et exceptions 7 / 50

Lecture et écriture de fichiers

Lecture dans un fichier texte

Pour réouvrir un fichier (par ex., pour changer le mode “lecture” en


“écriture” ou pour revenir au début du fichier), il faut d’abord le fermer
avec close, puis l’ouvrir à nouveau avec open.
>>> f = open(’example.txt’)
>>> content = f.read()
>>> content2 = f.read()
>>> content
’Un\nDeux\nTrois’
>>> content2
’’
>>> f.close()
>>> f = open(’example.txt’)
>>> content2 = f.read()
>>> content2
’Un\nDeux\nTrois’

Programmation (Chap. 12) Fichiers et exceptions 8 / 50

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 134


MASSART Thierry Programmation Transparents - Volume 2

Lecture et écriture de fichiers

Ecriture dans un fichier texte

Pour ouvrir un fichier en mode “écriture”, on doit spécifier le mode :


soit w (write) qui crée un nouveau fichier et l’ouvre en écriture;
soit a (append) qui ouvre un fichier existant et permet l’écriture à
la fin de celui-ci.
Attention : si le fichier existe déjà et qu’on l’ouvre en mode w, l’ancien
fichier est écrasé et son contenu supprimé !

On utilise la méthode write pour écrire dans le fichier. Celle-ci


n’ajoute pas de caractère de fin de ligne. Au besoin, il faut les spécifier
avec \n.

Programmation (Chap. 12) Fichiers et exceptions 9 / 50

Lecture et écriture de fichiers

Ecriture dans un fichier texte

Script write.py :
import sys
filename = sys.argv[1]
f = open(filename, ’w’)
f.write(’Line 1\n’)
f.write(’Line 2\n’)
f.close()
f = open(filename)
content = f.read()
print content
f.close()
f = open(filename, ’a’)
f.write(’New text\n’)
f.close()
f = open(filename)
content = f.read()
print content
f.close()

Programmation (Chap. 12) Fichiers et exceptions 10 / 50

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 135


MASSART Thierry Programmation Transparents - Volume 2

Lecture et écriture de fichiers

Ecriture dans un fichier texte

Résultat de python write.py test.txt :


Line 1
Line 2

Line 1
Line 2
New text

Le fichier test.txt est créé et contient les trois lignes de texte.

Programmation (Chap. 12) Fichiers et exceptions 11 / 50

Lecture et écriture de fichiers

Opérateur de format

L’argument de write doit être une chaîne de caractères. Il faut donc


convertir les autres types de valeurs avant de les écrire dans un fichier.

Un moyen simple de la faire est d’utiliser str. On peut également


utiliser l’opérateur de format %
>>> f = open(’test.txt’, ’w’)
>>> x = 52
>>> f.write(x)
TypeError: argument 1 must be string or read-only character buffer, not int
>>> f.write(str(x))
>>> a = 12
>>> euros = 32.56
>>> s = ’%d %.2f’ % (a, euros)
>>> s
’12 32.56’
>>> f.write(s)
>>> f.close()

Programmation (Chap. 12) Fichiers et exceptions 12 / 50

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 136


MASSART Thierry Programmation Transparents - Volume 2

Lecture et écriture de fichiers

Opérateur de format

%d Entier (base 10)


%i Entier (base 10)
%o Entier (octal, base 8)
%x Entier (hexadecimal, base 16, en minuscules)
%X Entier (hexadecimal, base 16, en majuscules)
%e Réel, format scientifique (base 10, minuscule)
%E Réel, format scientifique (base 10, majuscule)
%f Réel (base 10)
%g Réel (base 10) : format scientifique ou décimal en fonction de
l’exposant, n’affiche pas les zéros non significatifs
%r Chaîne (convertit tout objet avec repr)
%s Chaîne (convertit tout objet avec str)
%% Pas d’argument converti, retourne le caractère %

D’autres formats existent :


voir docs.python.org/lib/typesseq-strings.html

Programmation (Chap. 12) Fichiers et exceptions 13 / 50

Lecture et écriture de fichiers

Opérateur de format

Exemples :
>>> t = (2,34,56)
>>> print ’Time is %dh%dmin%dsec’ % t
Time is 2h34min56sec
>>> a = 12
>>> print ’a is %d (decimal) or %o (octal) or %X (hexadec)’ % ((a,) * 3)
a is 12 (decimal) or 14 (octal) or C (hexadec)
>>> x = 10**9
>>> y = math.pi
>>> print ’%e %E %f %g’ % ((x,) *4)
1.000000e+09 1.000000E+09 1000000000.000000 1e+09
>>> t = range(4)
>>> print ’List: %r’ % t
List: [0, 1, 2, 3]
>>> x = 0.1
>>> print ’x: %r (i.e. %s)’ % (x, x)
x: 0.10000000000000001 (i.e. 0.1)
>>> print ’ratio = %g%%’ % (2.0 / 3)
ratio = 0.666667%

Programmation (Chap. 12) Fichiers et exceptions 14 / 50

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 137


MASSART Thierry Programmation Transparents - Volume 2

Lecture et écriture de fichiers

Opérateur de format

Pour les entiers ou les chaînes, on peut spécifier le nombre (minimum)


de caractères utilisés pour réaliser un alignement (avec des espaces
ajoutés automatiquement avant ce qu’il faut afficher).
%xd (ou %xs) : x est un nombre qui signifie : utiliser x caractères
(minimum) pour afficher le nombre (la chaîne).

Pour les réels, on peut également spécifier la précision affichée.


%x.yf : x et y sont des nombres signifient : afficher y chiffres
après la virgules, et utiliser x caractères (minimum) pour afficher
le nombre.

Programmation (Chap. 12) Fichiers et exceptions 15 / 50

Lecture et écriture de fichiers

Opérateur de format
Exemple :
import math

def f(n):
return math.sqrt(math.sqrt(2**n))

print ’%3s %12s’ % (’n’,’f(n)’)


for n in range(10,111,10):
print ’%3d %12.2f’ % (n, f(n))

n f(n)
10 5.66
20 32.00
30 181.02
40 1024.00
50 5792.62
60 32768.00
70 185363.80
80 1048576.00
90 5931641.60
100 33554432.00
110 189812531.25

Programmation (Chap. 12) Fichiers et exceptions 16 / 50

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 138


MASSART Thierry Programmation Transparents - Volume 2

Lecture et écriture de fichiers

Opérateur de format
On peut également utiliser des “flags”.

0 (zéro) Le nombre est affiché avec des zéros le précédant


’ ’ (espace) Un espace est ajouté avant un nombre positif
+ Un caractère de signe (+ ou −) est ajouté avant le nombre
- La valeur est ajustée à gauche

import random
def f():
return random.randint(-20, 20) Time is 06:04:17
14
print ’Time is %02d:%02d:%02d’ % (6, 4, 17) 7
for i in range(3): -9
print ’% 3d’ % f() ***
print ’***’ -13
for i in range(3): +20
print ’%+3d’ % f() -3
print ’***’ ***
for i in range(3): -16*
print ’%- 3d*’ % f() -2 *
9 *

Programmation (Chap. 12) Fichiers et exceptions 17 / 50

Noms de fichiers et chemins

Noms de fichiers et chemins

Programmation (Chap. 12) Fichiers et exceptions 18 / 50

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 139


MASSART Thierry Programmation Transparents - Volume 2

Noms de fichiers et chemins

Noms de fichiers et chemins

Les fichiers sont organisés en répertoires (ou dossiers). Chaque


programme possède son “répertoire courant” (par défaut, le répertoire
dans lequel on lance la commande python sur un script, ou un
répertoire fixé dans la configuration pour IDLE).

Le module os (os = operating system) possède une fonction getcwd


(cwd = current working directory) qui retourne le nom du répertoire
courant.
>>> import os
>>> print os.getcwd()
/Users/hadmelot/Documents

Programmation (Chap. 12) Fichiers et exceptions 19 / 50

Noms de fichiers et chemins

Noms de fichiers et chemins

Une chaîne comme /home/username/filename.txt qui identifie un


fichier est appelé un chemin.
un chemin absolu commence à la racine (répertoire le plus haut,
symbolisé par “/”) du système. Exemple :
/home/username/filename.txt1 ;
un chemin relatif commence depuis le répertoire courant (il ne
commence donc pas par /, il peut être symbolisé par “.”). On
peut remonter un répertoire avec les symboles “../”. Exemples :
../data/file.txt; ./file.txt.

Syntaxe Linux ou Mac, sous windows, on remplace “/” par “\”. L’attribut os.sep
1

permet d’obtenir le séparateur du système d’exploitation sur lequel tourne le script.


Programmation (Chap. 12) Fichiers et exceptions 20 / 50

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 140


MASSART Thierry Programmation Transparents - Volume 2

Noms de fichiers et chemins

Noms de fichiers et chemins


Dans une instruction comme open(’test.txt’) le chemin est donc
relatif au répertoire courant. Pour obtenir le chemin absolu d’un fichier,
on peut utiliser os.path.abspath (path est un sous-module du module
os).

Quelques autres fonctions des modules os et os.path :


os.path.exists vérifie qu’un fichier (ou un répertoire) existe;
os.path.isdir vérifie s’il s’agit d’un répertoire;
os.path.isfile vérifie s’il s’agit d’un fichier;
os.listdir retourne une liste de fichiers et de répertoires
contenus dans le répertoire passé en paramètre;
os.path.join prend les noms d’un répertoire et d’un fichier en
paramètre et les joint en un chemin complet (en utilisant le
séparateur spécifique au système d’exploitation).

Programmation (Chap. 12) Fichiers et exceptions 21 / 50

Noms de fichiers et chemins

Noms de fichiers et chemins

Exemples
>>> os.path.abspath(’memo.txt’)
’/Users/hadmelot/Documents/memo.txt’
>>> os.path.exists(’memo.txt’)
True
>>> os.path.isdir(’memo.txt’)
False
>>> os.path.isdir(’music’)
True
>>> os.listdir(os.getcwd())
[’memo.txt’, ’music’, ’photos’]

Programmation (Chap. 12) Fichiers et exceptions 22 / 50

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 141


MASSART Thierry Programmation Transparents - Volume 2

Noms de fichiers et chemins

Noms de fichiers et chemins


Exemple
Le script suivant permet d’afficher les fichiers d’un répertoire donné en
argument de la ligne de commande, et affiche (récursivement) le
contenu des sous-répertoires.
import os
import sys

def walk(dir):
for name in os.listdir(dir):
path = os.path.join(dir, name)

if os.path.isfile(path):
print path
else:
walk(path)

walk(sys.argv[1])

Remarque : le module os possède une fonction walk qui fait presque


la même chose que cette fonction.
Programmation (Chap. 12) Fichiers et exceptions 23 / 50

Sauvegarder des objets

Sauvegarder des objets

Programmation (Chap. 12) Fichiers et exceptions 24 / 50

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 142


MASSART Thierry Programmation Transparents - Volume 2

Sauvegarder des objets

Pickling
Dans ce qu’on a vu précédemment, il faut convertir les données en
chaînes de caractères pour les écrire dans un fichier. Ce n’est pas
toujours pratique pour sauver, puis récupérer, des objets plus
complexes que des chaînes ou des entiers (par ex. listes,
dictionnaires, etc.).

Le module pickle permet de sérialiser un objet, c-à-d, de le sauver,


puis de le récupérer en l’état, très facilement.

La fonction pickle.dumps prend un objet en paramètre et retourne un


représentation de l’objet sous forme de chaîne. La chaîne obtenue
n’est pas destinée à être compréhensible ou lue par l’humain. Par
contre, la fonction pickle.loads prend une chaîne de ce type en
paramètre et reconstitue un objet identique à celui donné à
pickle.dumps.

Programmation (Chap. 12) Fichiers et exceptions 25 / 50

Sauvegarder des objets

Pickling

>>> t = [1, 2, 3]
>>> pickle.dumps(t)
’(lp0\nI1\naI2\naI3\na.’
>>> pickle.dumps({’a’:3, ’b’:8, ’c’:9})
"(dp0\nS’a’\np1\nI3\nsS’c’\np2\nI9\nsS’b’\np3\nI8\ns."
>>> pickle.dumps(’hello’)
"S’hello’\np0\n."
>>> pickle.dumps(12)
’I12\n.’
>>> save_t = pickle.dumps(t)
>>> t2 = pickle.loads(save_t)
>>> t2
[1, 2, 3]
>>> t == t2
True
>>> t is t2
False

Programmation (Chap. 12) Fichiers et exceptions 26 / 50

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 143


MASSART Thierry Programmation Transparents - Volume 2

Sauvegarder des objets

Pickling

Les fonctions dump et load (sans “s”) prennent un fichier comme


argument supplémentaire pour sauvegarder / récupérer un objet
depuis le fichier.
>>> f = open(’data.txt’, ’w’)
>>> d = {’a’:3, ’b’:8, ’c’:9}
>>> pickle.dump(d, f)
>>> f.close()
>>> # on peut fermer IDLE et relancer une session
>>> # d est sauvegarde dans data.txt
>>> f = open(’data.txt’)
>>> d = pickle.load(f)
>>> d
{’a’: 3, ’c’: 9, ’b’: 8}

Programmation (Chap. 12) Fichiers et exceptions 27 / 50

Sauvegarder des objets

Shelve
Le pickling est un moyen simple de sauvegarder un objet (ou plusieurs
objets séquentiellement dans le même fichier).

Un autre moyen, plus souple, est d’utiliser le module shelve qui fournit
une solution de persistance de type “base de données”.

Concrètement,
la base de donnée est écrite (automatiquement) dans un fichier;
la fonction shelve.open, crée un fichier de sauvegarde s’il
n’existe pas, et retourne un objet de type shelve;
un shelve fonctionne (presque) comme un dictionnaire. La
plupart des opérations et méthodes des dictionnaires lui sont
applicables;
toute modification appliquée au shelve est (automatiquement)
sauvegardée;
la fonction shelve.close permet de fermer le shelve, comme
pour un fichier.
Programmation (Chap. 12) Fichiers et exceptions 28 / 50

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 144


MASSART Thierry Programmation Transparents - Volume 2

Sauvegarder des objets

Shelve
>>> import shelve
>>> t = range(4)
>>> p = (1, 2, 3)
>>> i = 12
>>> s = ’hello’
>>> db = shelve.open(’data.db’)
>>> db[’liste’] = t
>>> db[’point’] = p
>>> db[’entier’] = i
>>> db[’chaine’] = s
>>> print db
{’liste’: [0, 1, 2, 3], ’chaine’: ’hello’, ’entier’: 12, ’point’: (1, 2, 3)}
>>> db.close()
>>> # on peut fermer IDLE et relancer une session
>>> # la base de donnee est sauvegardee a chaque modification
>>> db = shelve.open(’data.db’)
>>> print db[’liste’]
[0, 1, 2, 3]
>>> db[’chaine’] = ’bonjour’
>>> print db
{’liste’: [0, 1, 2, 3], ’chaine’: ’bonjour’, ’entier’: 12, ’point’: (1, 2, 3)}

Programmation (Chap. 12) Fichiers et exceptions 29 / 50

Exceptions

Exceptions

Programmation (Chap. 12) Fichiers et exceptions 30 / 50

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 145


MASSART Thierry Programmation Transparents - Volume 2

Exceptions

Exceptions

Les exceptions sont fréquentes, en particulier quand on manipule des


fichiers.
>>> 3 / 0
ZeroDivisionError: integer division or modulo by zero
>>> t = range(4)
>>> t[4]
IndexError: list index out of range
>>> s = ’2’ + 2
TypeError: cannot concatenate ’str’ and ’int’ objects
>>> open(’xyz.txt’)
IOError: [Errno 2] No such file or directory: ’xyz.txt’
>>> open(’/etc/passwd’, ’w’)
IOError: [Errno 13] Permission denied: ’/etc/passwd’
>>> open(’music’)
IOError: [Errno 21] Is a directory: ’music’

Programmation (Chap. 12) Fichiers et exceptions 31 / 50

Exceptions

Lancer une exception


Une exception indique que quelque chose d’exceptionnel s’est produit.
Vous pouvez-vous même lancer des exceptions.
def square_root(a, eps = 10**-9):
if not isinstance(a,int) and not isinstance(a,float):
raise TypeError(’a must be an integer or a float’)
if a < 0.0:
raise ValueError(’a must be >= 0’)
x = 0.0
approx = 1.0
while (abs(approx - x) > eps):
x = approx
approx = (x + (a / x)) / 2.0
return approx
>>> square_root(4)
2.0
>>> square_root(-2)
ValueError: a must be >= 0
>>> square_root(’hello’)
TypeError: a must be an integer or a float

Programmation (Chap. 12) Fichiers et exceptions 32 / 50

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 146


MASSART Thierry Programmation Transparents - Volume 2

Exceptions

Lancer une exception

Syntaxe : raise ExceptionType(’message’)

Les types d’exceptions les plus fréquentes que vous pouvez lancer :

ValueError Erreur de valeur (par ex., ne respecte pas les pré-


conditions)
TypeError Erreur de type pour un paramètre
IndexError Indice hors bornes
IOError Erreur d’entrée / sortie (par ex. fichiers)

Voir aussi : docs.python.org/library/exceptions.html et


docs.python.org/tutorial/errors.html

Programmation (Chap. 12) Fichiers et exceptions 33 / 50

Gérer les exceptions

Gérer les exceptions

Programmation (Chap. 12) Fichiers et exceptions 34 / 50

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 147


MASSART Thierry Programmation Transparents - Volume 2

Gérer les exceptions

Gérer les exceptions

Jusqu’à présent, quand une exception se produit, le script s’interrompt


brutalement. Comment gérer les exceptions pour éviter cette sortie
brutale et effectuer du code spécifique quand une exception se
produit ?

Solution 1 : utiliser des instructions conditionnelles pour éviter les


exceptions.

Exemple : avant d’ouvrir un fichier, vérifier qu’il existe, que ce n’est


pas un répertoire, etc. avec des fonctions comme os.path.exists ou
os.path.isdir.

Programmation (Chap. 12) Fichiers et exceptions 35 / 50

Gérer les exceptions

Gérer les exceptions


Solution 1 : utiliser des instructions conditionnelles pour éviter les
exceptions.

Inconvénients :
code difficile à lire et à écrire car nombreux cas possibles;
les tests peuvent être coûteux en temps de calcul;
nécessite de penser aux cas exceptionnels, et de les gérer “a
priori”, plutôt que de se concentrer sur le code principal;
le nombre d’exceptions possibles peut-être très important. Par
exemple, pour les fichiers, l’exception suivante suggère qu’il y a
au moins 21 types d’erreurs possibles !
>>> open(’music’)
IOError: [Errno 21] Is a directory: ’music’

comment être sûr de ne pas oublier un cas exceptionnel ?

Programmation (Chap. 12) Fichiers et exceptions 36 / 50

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 148


MASSART Thierry Programmation Transparents - Volume 2

Gérer les exceptions

Gérer les exceptions

Il vaudrait donc mieux pouvoir écrire le code principal et demander à


l’interpréteteur d’essayer (try) de l’exécuter, et gérer les problèmes
seulement s’ils se produisent.

C’est exactement ce que propose la syntaxe try – except.

Solution 2 (de loin préférée) : utiliser une instruction try – except.

Programmation (Chap. 12) Fichiers et exceptions 37 / 50

Gérer les exceptions

Gérer les exceptions

La forme la plus simple d’une instruction try – except est la suivante.

Syntaxe :

try:
# code principal
except:
# code specifique en cas d’exception

Comportement :
Le code principal est exécuté. Dès que celui-ci produit une exception,
il est interrompu et le code spécifique est exécuté. Si aucune
exception ne s’est produite, le code spécifique n’est pas exécuté.

On dit qu’on rattrape (catch) une exception (dans une clause except).

Programmation (Chap. 12) Fichiers et exceptions 38 / 50

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 149


MASSART Thierry Programmation Transparents - Volume 2

Gérer les exceptions

Gérer les exceptions

Exemple :
try:
print ’Avant’
raise ValueError(’Test’)
print ’Apres’
except:
print ’Gestion exception’
print ’Fin’

Résultat :
Avant
Gestion exception
Fin

Programmation (Chap. 12) Fichiers et exceptions 39 / 50

Gérer les exceptions

Gérer les exceptions


Exemple :
try:
i = int(raw_input(’Entier : ’))
print ’Nous pouvons travailler avec’, i
except:
print ’Je ne peux travailler qu\’avec un entier’
print ’Fin du programme’

Si on entre un entier :
Entier : 2
Nous pouvons travailler avec 2
Fin du programme

Sinon :
Entier : hello
Je ne peux travailler qu’avec un entier
Fin du programme

Programmation (Chap. 12) Fichiers et exceptions 40 / 50

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 150


MASSART Thierry Programmation Transparents - Volume 2

Gérer les exceptions

Gérer les exceptions

Il est possible de spécifier dans la clause except le type d’exception


que l’on veut rattraper.

Exemple :
while True:
try:
x = int(raw_input("Please enter a number: "))
break
except ValueError:
print "Oops! That was no valid integer. Try again..."

print ’Now I\’m sure that’, x, ’is a valid integer.’

Remarque : dans cet exemple, si une autre exception qu’une


ValueError se produit, elle n’est pas rattrapée et le comportement
sera celui habituel : le programme s’arrête brutalement.

Programmation (Chap. 12) Fichiers et exceptions 41 / 50

Gérer les exceptions

Gérer les exceptions


On peut spécifier plusieurs clauses except, pour gérer différents types
d’exceptions.

Exemple :
try:
f = open(’myfile.txt’)
s = f.readline()
i = int(s.strip())
except IOError:
print ’Cannot open myfile.txt’
except ValueError:
print ’Could not convert’, s.strip(), ’to an integer.’
except:
print ’Unexpected error.’
raise

Remarque : le dernier cas permet de relancer les exceptions qui n’ont


pas été spécifiquement gérées (par ex. si ce code est encapsulé dans
une fonction, l’appelant pourra alors gérer ces exceptions).

Programmation (Chap. 12) Fichiers et exceptions 42 / 50

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 151


MASSART Thierry Programmation Transparents - Volume 2

Gérer les exceptions

Gérer les exceptions

On peut spécifier un tuple d’exceptions à gérer dans une même clause


except.

Exemple :
try:
f = open(’myfile.txt’)
s = f.readline()
i = int(s.strip())
except (IOError, ValueError):
print ’Cannot open file or cannot convert integer’

Programmation (Chap. 12) Fichiers et exceptions 43 / 50

Gérer les exceptions

Gérer les exceptions


On peut placer un else, après les clauses except. Il sera exécuté si
aucune exception n’est produite. C’est utile dans le cas où il faut
exécuter du code seulement si l’instruction try n’a pas provoqué
d’exceptions.

Exemple :
import sys

for arg in sys.argv[1:]:


try:
f = open(arg, ’r’)
except IOError:
print ’cannot open’, arg
else:
print arg, ’has’, len(f.readlines()), ’lines’
f.close()

Remarque : l’utilisation d’une clause else est ici meilleure que d’ajouter du
code dans le try car cela évite d’attraper accidentellement une exception qui
serait provoquée par une autre instruction que open.
Programmation (Chap. 12) Fichiers et exceptions 44 / 50

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 152


MASSART Thierry Programmation Transparents - Volume 2

Gérer les exceptions

Gérer les exceptions

Pour obtenir des informations sur une exception, on peut la faire suivre
par le mot-clef as et le nom d’une variable. Cette variable possède
comme valeur un objet de type exception. En affichant celle-ci, on
obtient le message associé à l’exception.

Exemple :
try:
f = open(’myfile.txt’)
except IOError as e:
print ’Cannot open myfile.txt:’, e
else:
s = f.readline().strip()
print s

Programmation (Chap. 12) Fichiers et exceptions 45 / 50

Gérer les exceptions

Gérer les exceptions


Enfin, on peut ajouter une clause finally qui sera exécutée dans tous
les cas (qu’il y ait une exception ou pas). Cela permet de réaliser des
tâches de “clean-up” comme fermer un fichier qu’on savait ouvert
avant le try, ou sauvegarder les données avant que le programme
s’arrête suite à une exception, etc. La clause finally est exécutée
juste avant de sortir du try – except, qu’une exception soit provoquée
ou non.

Exemple :
try:
raise KeyboardInterrupt
finally:
print ’Goodbye, world!’
Goodbye, world!
KeyboardInterrupt

Remarques : la commande “Ctrl-C” permet d’interrompre l’exécution d’un


programme. En Python, une telle commande provoque un
KeyboardInterrupt.
Programmation (Chap. 12) Fichiers et exceptions 46 / 50

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 153


MASSART Thierry Programmation Transparents - Volume 2

Gérer les exceptions

Gérer les exceptions

Exemple :
import time

print ’This is a malicious script!’


print ’Try to find the exit before the bomb exploses!’

try:
for i in range(10):
print 10-i,
if i % 2 == 0:
print ’tic’
else:
print ’tac’
time.sleep(1)
except KeyboardInterrupt:
print ’Well done! You find the exit!’
else:
print ’BOOOM! You lose!’
finally:
print ’Goodbye!’

Programmation (Chap. 12) Fichiers et exceptions 47 / 50

Gérer les exceptions

Gérer les exceptions

Exercice : Quels sont les messages que la fonction suivante va


afficher (et dans quel ordre) si
x = 2 et y = 1;
x = 2 et y = 0;
x = ’2’ et y = ’1’ ?
def divide(x, y):
try:
result = x / y
except ZeroDivisionError:
print "division by zero!"
else:
print "result is", result
finally:
print "executing finally clause"

Programmation (Chap. 12) Fichiers et exceptions 48 / 50

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 154


MASSART Thierry Programmation Transparents - Volume 2

Glossaire

Glossaire

Programmation (Chap. 12) Fichiers et exceptions 49 / 50

Glossaire

Glossaire

fichier texte : séquence de caractères stockée dans un support permanent (disque dur,
CD-ROM, etc.).
répertoire : (dossier) un répertoire possède son propre nom et contient une collection de
fichiers ou d’autres répertoires.
chemin : chaîne de caractères qui identifie un fichier.
chemin absolu : chemin qui commence au répertoire du plus haut niveau du système.
chemin relatif : chemin qui commence depuis le répertoire courant.
lancer (une exception) : on lance une exception quand quelque chose d’exceptionnel se
produit, via l’instruction raise.
rattraper (une exception) : on rattrape une exception dans une clause except, pour y
exécuter du code spécifique.

Programmation (Chap. 12) Fichiers et exceptions 50 / 50

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 155


MASSART Thierry Programmation Transparents - Volume 2

Chapitre 13. Introduction aux objets

Contenu du chapitre

1 Classes et objets

2 Fonctions pures

3 Méthodes

4 Programmation orientée objets

5 Glossaire

Programmation (Chap. 13) Introduction aux objets 1 / 48

Classes et objets

Classes et objets

Programmation (Chap. 13) Introduction aux objets 2 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 156


MASSART Thierry Programmation Transparents - Volume 2

Classes et objets

La notion de classe

Nous avons déjà rencontré de nombreux objets (en Python tout est
objet), comme des objets de types entier, chaîne ou liste.

Chaque objet possède donc un type. En fonction de celui-ci, une série


de méthodes, d’attributs et d’opérateurs sont disponibles.

Une classe est un modèle décrivant un type d’objets : ses attributs,


ses méthodes, etc.

On distingue donc les deux notions :


objet : instance (valeur) d’un certain type;
classe : modèle décrivant un type et ses propriétés.

Programmation (Chap. 13) Introduction aux objets 3 / 48

Classes et objets

La notion de classe

On peut définir ses propres types en donnant une définition de classe.


On peut donc voir une définition de classe comme la définition d’un
nouveau type.

Définissons un nouveau type (classe) pour représenter des points


dans le plan.
class Point(object):
""" represente un point dans le plan """

le mot-clef class indique le début d’une définition de classe;


on donne ensuite le nom de la classe (par convention, avec une
majuscule);
entre parenthèses, on précise qu’un Point est une sorte d’
object, qui est un type prédéfini.

Programmation (Chap. 13) Introduction aux objets 4 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 157


MASSART Thierry Programmation Transparents - Volume 2

Classes et objets

La notion de classe

Pour l’instant notre modèle (classe) est très peu précis. On a juste
donné un nom et un docstring.
>>> print Point
<class ’__main__.Point’>
>>> help(Point)
Help on class Point in module __main__:

class Point(__builtin__.object)
| represente un point dans le plan
|
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)

Programmation (Chap. 13) Introduction aux objets 5 / 48

Classes et objets

La notion d’objets

Cependant, on peut déjà créer des objets de la classe Point. Pour ce


faire, on appelle Point comme si cela était une fonction.
>>> p = Point()
>>> print p
<__main__.Point object at 0xe73bd0>

la valeur de retour est une référence vers un objet de type Point,


que l’on assigne à la variable p;
créer un objet est une instanciation, et l’objet est une instance de
la classe Point;
quand on affiche une instance, Python nous dit à quelle classe
elle appartient et où elle est stockée en mémoire (sous la forme
d’une adresse représentée par un nombre hexadécimal).

Programmation (Chap. 13) Introduction aux objets 6 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 158


MASSART Thierry Programmation Transparents - Volume 2

Classes et objets

La notion d’objets

>>> p = Point()
>>> print p
<__main__.Point object at 0xe73bd0>

p 0xe73bd0

Point

Programmation (Chap. 13) Introduction aux objets 7 / 48

Classes et objets

Attributs
Une des propriétés des objets sont ses attributs. Un attribut est une
valeur.

Par exemple, pour représenter un point dans le plan, on a besoin de


deux attributs (coordonnées x et y).

Pour assigner un attribut à un objet, on peut utiliser la notation point.


>>> p.x = 2.0
>>> p.y = 4.0

Point

x 2.0
y 4.0

Programmation (Chap. 13) Introduction aux objets 8 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 159


MASSART Thierry Programmation Transparents - Volume 2

Classes et objets

Attributs

On accède aux attributs d’un objet également via la notation point.

p.x peut être lu comme “aller dans l’objet référé par p et récupérer la
valeur de son attribut x”
>>> print p.x
2.0
>>> y = p.y
>>> print y
4.0
>>> print ’(%g, %g)’ % (p.x, p.y)
(2, 4)
>>> dist_from_orig = math.sqrt(p.x**2 + p.y**2)
>>> print dist_from_orig
4.472135955

Programmation (Chap. 13) Introduction aux objets 9 / 48

Classes et objets

Attributs

On peut passer un objet en argument d’une fonction. Le paramètre est


assigné avec la référence de l’objet, il s’agit donc d’un alias et les
attributs de l’objet peuvent être modifiés (les objets sont mutables1 ).
def print_point(pt):
print ’(%g, %g)’ % (pt.x, pt.y)

def double_point(pt): Point


pt.x *= 2 p
pt.y *= 2 pt x 2.0
>>> print_point(p) y 4.0
(2, 4)
>>> double_point(p)
>>> print_point(p)
(4, 8)

1
Par défaut, les objets créés par le programmeur sont mutables (ce qu’on peut
empêcher : ne sera pas vu à ce cours).
Programmation (Chap. 13) Introduction aux objets 10 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 160


MASSART Thierry Programmation Transparents - Volume 2

Classes et objets

Attributs

Un attribut peut être une référence vers un autre objet.


class Rectangle(object):
""" represente un rectangle dans le plan
attributs: width, height, corner
(coin inferieur gauche, Point)
""" Rectangle
>>> box = Rectangle() width 100.0 Point
>>> box.width = 100.0 height 200.0 x 0.0
>>> box.height = 200.0
corner y 0.0
>>> box.corner = Point()
>>> box.corner.x = 0.0
>>> box.corner.y = 0.0

Programmation (Chap. 13) Introduction aux objets 11 / 48

Classes et objets

Objets comme valeurs de retour

Une fonction peut créer un objet et retourner la référence vers celui-ci.


def find_center(r):
c = Point()
c.x = box.corner.x + box.width / 2.0
c.y = box.corner.y + box.height / 2.0
return c
>>> center = find_center(box)
>>> print_point(center)
(50, 100)

Programmation (Chap. 13) Introduction aux objets 12 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 161


MASSART Thierry Programmation Transparents - Volume 2

Classes et objets

Copie d’objets
Un objet passé en argument peut-être modifié par une fonction. Cela
peut provoquer des problèmes dans certains cas (cfr. Chap. 7).

On peut copier un objet pour éviter l’aliasing grâce au module copy.


>>> p1 = Point()
>>> p1.x = 3.0
>>> p1.y = 4.0
>>> p2 = p1
>>> p2.y = 7.0
>>> print_point(p1)
(3, 7)
>>> p1 is p2
True
>>> import copy
>>> p2 = copy.copy(p1)
>>> p2.y = 9.0
>>> print_point(p1)
(3, 7)
>>> print_point(p2)
(3, 9)
>>> p1 is p2
False
Programmation (Chap. 13) Introduction aux objets 13 / 48

Classes et objets

Copie d’objets

Soit :
>>> p1 = Point()
>>> p1.x = 3.0
>>> p1.y = 4.0
>>> p2 = copy.copy(p1)

Que va retourner p1 == p2 ?

Programmation (Chap. 13) Introduction aux objets 14 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 162


MASSART Thierry Programmation Transparents - Volume 2

Classes et objets

Copie d’objets

Soit :
>>> p1 = Point()
>>> p1.x = 3.0
>>> p1.y = 4.0
>>> p2 = copy.copy(p1)

Que va retourner p1 == p2 ?

False !

Pour les objets, le comportement par défaut de == est le même que


l’opérateur is. Mais ce comportement pourra être modifié (dans la
définition de classe).

Programmation (Chap. 13) Introduction aux objets 14 / 48

Classes et objets

Copie d’objets

Soit :
>>> box2 = copy.copy(box)
>>> box2 is box
False

Que va retourner box2.corner is box.corner ?

Programmation (Chap. 13) Introduction aux objets 15 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 163


MASSART Thierry Programmation Transparents - Volume 2

Classes et objets

Copie d’objets

Soit :
>>> box2 = copy.copy(box)
>>> box2 is box
False

Que va retourner box2.corner is box.corner ?

True !

La fonction copy.copy réalise une “shallow copy”, c-à-d qu’elle copie


les références contenues dans les attributs mutables.
Rectangle Rectangle
box2
box Point
width 100.0 width 100.0
height 200.0 x 0.0 height 200.0
corner y 0.0 corner

Programmation (Chap. 13) Introduction aux objets 15 / 48

Classes et objets

Copie d’objets

La fonction copy.deepcopy réalise une “deep copy”, c-à-d qu’elle copie


“réellement” les objets référencés dans les attributs mutables (et ceux
référencés par ceux-ci etc.).
>>> box3 = copy.deepcopy(box)
>>> box3 is box
False
>>> box3.corner is box.corner
False

Les objets référencés par box et box3 sont maintenant complètement


séparés (et indépendants).

Programmation (Chap. 13) Introduction aux objets 16 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 164


MASSART Thierry Programmation Transparents - Volume 2

Fonctions pures

Fonctions pures

Programmation (Chap. 13) Introduction aux objets 17 / 48

Fonctions pures

Fonctions pures

Pour rappel, un effet de bord d’une fonction est quelque chose qu’elle
produit et qui est visible en dehors de celle-ci. Par ex. :
afficher une valeur;
demander une valeur à l’utilisateur;
écrire dans un fichier;
modifier un objet (mutable) passé en argument.
Retourner une valeur (return) n’est pas considéré comme un effet de
bord (intuition : boite noire).

Une fonction qui n’a pas d’effet de bord est parfois appelée une
fonction pure. En général, on essaye toujours d’écrire des fonctions
pures (sauf si le but de la fonction est explicitement d’appliquer un
effet de bord bien précis, par ex.: print_point).

Programmation (Chap. 13) Introduction aux objets 18 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 165


MASSART Thierry Programmation Transparents - Volume 2

Fonctions pures

Fonctions pures

Exemple : on va définir une classe pour des objets représentant un


temps (heure).
class Time(object):
""" represente un temps (heure).
attributs: hour, minute, second
"""

def print_time(t):
print ’%02d:%02d:%02d’ % (t.hour, t.minute, t.second)

Problème : écrire une fonction pure qui permet d’additionner deux


temps.

Programmation (Chap. 13) Introduction aux objets 19 / 48

Fonctions pures

Fonctions pures

Premier prototype (ne tient pas compte du fait que 60 sec = 1min,
etc.) :
def add_time(t1, t2):
res = Time()
res.hour = t1.hour + t2.hour
res.minute = t1.minute + t2.minute
res.second = t1.second + t2.second
return res

C’est une fonction pure car elle ne modifie pas les deux temps passés
en argument. Elle retourne un nouvel objet qui représente le temps
total.

Programmation (Chap. 13) Introduction aux objets 20 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 166


MASSART Thierry Programmation Transparents - Volume 2

Fonctions pures

Fonctions pures

>>> start = Time()


>>> start.hour = 9
>>> start.minute = 45
>>> start.second = 0
>>> duration = Time()
>>> duration.hour = 1
>>> duration.minute = 35
>>> duration.second = 0
>>> done = add_time(start, duration)
>>> print_time(start)
09:45:00
>>> print_time(duration)
01:35:00
>>> print_time(done)
10:80:00

Programmation (Chap. 13) Introduction aux objets 21 / 48

Fonctions pures

Fonctions pures
Deuxième prototype (tient compte des conversions mais code long) :
def add_time(t1, t2):
res = Time()
res.hour = t1.hour + t2.hour
res.minute = t1.minute + t2.minute
res.second = t1.second + t2.second

if res.second >= 60:


res.second -= 60
res.minute += 1

if res.minute >= 60:


res.minute -= 60
res.hour += 1

return res
>>> done = add_time(start, duration)
>>> print_time(done)
11:20:00

Programmation (Chap. 13) Introduction aux objets 22 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 167


MASSART Thierry Programmation Transparents - Volume 2

Fonctions pures

Fonctions pures

Pour améliorer le code de la fonction add_time, nous allons écrire des


fonctions de conversions.
def time_to_sec(time):
mins = time.hour * 60 + time.minute
secs = mins * 60 + time.second
return secs

def sec_to_time(secs):
time = Time()
mins, time.second = divmod(secs, 60)
time.hour, time.minute = divmod(mins, 60)
return time

def add_time(t1, t2):


secs = time_to_sec(t1) + time_to_sec(t2)
return sec_to_time(secs)

Programmation (Chap. 13) Introduction aux objets 23 / 48

Méthodes

Méthodes

Programmation (Chap. 13) Introduction aux objets 24 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 168


MASSART Thierry Programmation Transparents - Volume 2

Méthodes

Méthodes

Les attributs d’un objet modélisent son état, ses particularités dans la
famille des objets du même type.
Exemples :
un point a comme coordonnées (2, 3); un autre (4, 5), mais ce
sont deux points dans le plan.
un rectangle à une largeur de 10, une hauteur de 20, et un coin
inférieur droit en (3, 7). Un autre sera plus grand, ou plus à
droite, mais il reste un rectangle.
un temps dans la journée correspond à la manière dont nous
décrivons un temps donné, c-à-d, une heure, une minute et une
seconde.
Mais un objet possède d’autres propriétés que ses attributs : les
méthodes associées à son type (sa classe).

Programmation (Chap. 13) Introduction aux objets 25 / 48

Méthodes

Méthodes

Une méthode est une fonction qui est définie pour un type donné et
qui s’applique sur les objets de ce type.

Motivation : la plupart des fonctions définies dans les sections


précédentes (print_point, add_time, etc.) ont été définies pour un
type d’objets particulier. Le principe des méthodes est de définir
explicitement ce type de fonctions dans la définition de la classe
elle-même.

Intuition : une fonction est une boite noire qui utilise ses entrées pour
produire ses sorties. Une méthode est une action appliquée sur un
objet (et peut avoir besoin d’autres entrées que l’objet sur lequel elle
est appliquée, et peut produire également des sorties).

Programmation (Chap. 13) Introduction aux objets 26 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 169


MASSART Thierry Programmation Transparents - Volume 2

Méthodes

Méthodes

Pour afficher l’heure, nous avions défini une fonction :


class Time(object):
""" represente un temps (heure).
attributs: hour, minute, second
"""

def print_time(t):
print ’%02d:%02d:%02d’ % (t.hour, t.minute, t.second)

Cela nécessite de passer un objet Time en argument.


>>> print_time(start)
09:45:00

Programmation (Chap. 13) Introduction aux objets 27 / 48

Méthodes

Méthodes

Comme la fonction print_time est destinée à être appliquée


uniquement sur des objets de type Time, nous voudrions plutôt la
transformer en une méthode display spécifique à ces objets. La
syntaxe, plus intuitive, deviendrait :
>>> start.display()
09:45:00

Programmation (Chap. 13) Introduction aux objets 28 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 170


MASSART Thierry Programmation Transparents - Volume 2

Méthodes

Méthodes
Définir une méthode revient simplement à donner sa définition à l’intérieur de
la définition de la classe. Pour accéder à l’objet sur lequel la méthode sera
invoquée, on utilise la première variable nommée self (convention).
class Time(object):
""" represente un temps (heure).
attributs: hour, minute, second
"""
def display(self):
print ’%02d:%02d:%02d’ % (self.hour, self.minute, self.second)
>>> start.display()
09:45:00

une méthode destinée à être invoquée sur un objet possède toujours


cet objet comme premier paramètre;
quand on invoque la méthode sur un objet, il ne faut pas donner
l’argument (self);
ce type de méthodes est appelé une méthode d’instance;
∃ d’autres types de méthodes (de classes et statiques) qui ne seront
pas vues dans ce cours.
Programmation (Chap. 13) Introduction aux objets 29 / 48

Méthodes

Méthodes

Intuition

“Hey print_time, here’s an object for you to print.”


>>> print_time(start)
09:45:00

Programmation procédurale : les fonctions sont les agents actifs.

“Hey start, please display yourself.”


>>> start.display()
09:45:00

Programmation orientée objets : les objets sont les agents actifs.

Programmation (Chap. 13) Introduction aux objets 30 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 171


MASSART Thierry Programmation Transparents - Volume 2

Méthodes

Méthodes
Comment adapter la fonction add_time pour en faire une méthode ?

La somme de deux temps implique deux objets : l’un sera l’objet sur
lequel la méthode sera invoquée, l’autre sera donné en paramètre.

Le comportement sera un peu différent : add_time est une fonction


pure, ici nous allons écrire une méthode add qui va modifier l’objet sur
lequel elle est invoquée (rappel : ce comportement fonction pure /
méthode modifiante est classique).

Pour cela nous allons d’abord réécrire la fonction time_to_sec en une


méthode to_sec.

De la même manière, le fonction sec_to_time sera transformée en


une méthode update qui prend un nombre de secondes en arguments
et met à jour l’objet Time en transformant ce nombre en temps.

Programmation (Chap. 13) Introduction aux objets 31 / 48

Méthodes

Méthodes

class Time(object):
# ...
def to_sec(self):
mins = self.hour * 60 + self.minute
secs = mins * 60 + self.second
return secs

def update(self, secs):


mins, self.second = divmod(secs, 60)
self.hour, self.minute = divmod(mins, 60)

def add(self, other):


secs = self.to_sec() + other.to_sec()
self.update(secs)

Programmation (Chap. 13) Introduction aux objets 32 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 172


MASSART Thierry Programmation Transparents - Volume 2

Méthodes

Méthodes

>>> start = Time()


>>> start.hour = 9
>>> start.minute = 45
>>> start.second = 0
>>> duration = Time()
>>> duration.hour = 1
>>> duration.minute = 35
>>> duration.second = 0
>>> start.display()
09:45:00
>>> duration.display()
01:35:00
>>> duration.to_sec()
5700
>>> duration.update(5760)
>>> duration.display()
01:36:00
>>> start.add(duration)
>>> start.display()
11:21:00

Programmation (Chap. 13) Introduction aux objets 33 / 48

Méthodes

La méthode init

Jusqu’à présent, nous assignons les valeurs des attributs “à la main”.


La méthode init (__init__) est une méthode spéciale qui est invoquée
automatiquement quand un objet est créé. Cela permet d’ajouter des
arguments (par défaut) quand on crée un objet.
class Time(object):
# ...
def __init__(self, hour=0, minute=0, second=0):
self.hour = hour
self.minute = minute
self.second = second
# ...

Programmation (Chap. 13) Introduction aux objets 34 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 173


MASSART Thierry Programmation Transparents - Volume 2

Méthodes

La méthode init

>>> t1 = Time()
>>> t2 = Time(3)
>>> t3 = Time(4, 12)
>>> t4 = Time(18, 35, 17)
>>> t1.display()
00:00:00
>>> t2.display()
03:00:00
>>> t3.display()
04:12:00
>>> t4.display()
18:35:17

Programmation (Chap. 13) Introduction aux objets 35 / 48

Méthodes

La méthode str

La méthode str (__str__) est une méthode spéciale qui est censée
retournée une représentation de l’objet sous forme de chaîne de
caractères. Lors d’une instruction print, Python appelle
automatiquement cette méthode.
class Time(object):
# ...
def __str__(self):
s = ’%02d:%02d:%02d’ % (self.hour, self.minute, self.second)
return s
# ...

Programmation (Chap. 13) Introduction aux objets 36 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 174


MASSART Thierry Programmation Transparents - Volume 2

Méthodes

La méthode str

>>> t = Time(8,30)
>>> print t
08:30:00
>>> str(t)
’08:30:00’

la méthode display devient inutile.


__init__ et __str__ devraient toujours être présentes dans vos
définitions de classes (en général, on commence par ces
méthodes).

Programmation (Chap. 13) Introduction aux objets 37 / 48

Méthodes

Surcharge d’opérateur

On peut également définir des opérateurs sur les objets. Par exemple,
l’opérateur + peut être défini en écrivant une méthode spéciale
__add__ pour la classe Time.

On parle de surcharge des opérateurs : adapter le comportement d’un


opérateur à un type défini par le programmeur.
class Time(object):
# ...
def __add__(self, other):
res = Time()
secs = self.to_sec() + other.to_sec()
res.update(secs)
return res
# ...

Programmation (Chap. 13) Introduction aux objets 38 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 175


MASSART Thierry Programmation Transparents - Volume 2

Méthodes

Surcharge d’opérateur

>>> t1 = Time(9)
>>> t2 = Time(1,30)
>>> t3 = t1 + t2
>>> print t3
10:30:00

Pour chaque opérateur présent en Python, il y a une méthode spéciale


qui correspond et qui permet de surcharger l’opérateur !

Voir: docs.python.org/ref/specialnames.html

Programmation (Chap. 13) Introduction aux objets 39 / 48

Méthodes

Surcharge d’opérateur

Problème : pour le moment l’opérateur + opère sur deux objets Time.


Nous voudrions pouvoir également pouvoir ajouter un nombre entier
(secondes) à ces objets.
class Time(object):
# ...
def __add__(self, other):
res = Time()
secs = self.to_sec()
if isinstance(other, Time):
secs += other.to_sec()
elif isinstance(other, int):
secs += other
else:
raise NotImplemented(’op + only for Time and int’)
res.update(secs)
return res
# ...

Programmation (Chap. 13) Introduction aux objets 40 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 176


MASSART Thierry Programmation Transparents - Volume 2

Méthodes

Surcharge d’opérateur

>>> t1 = Time(9)
>>> t2 = t1 + 600
>>> print t2
09:10:00
>>> t3 = t1 + t2
>>> print t3
18:10:00

Programmation (Chap. 13) Introduction aux objets 41 / 48

Méthodes

Surcharge d’opérateur

Problème : malheureusement, cette addition n’est pas commutative.


Si l’entier est le premier opérateur, on obtient une erreur.
>>> 1200 + t3
TypeError: unsupported operand type(s) for +: ’int’ and ’Time’

Le problème vient du fait qu’au lieu de demander à un objet Time


d’ajouter un entier, on fait le contraire, et on ne peut pas modifier les
objets de type entiers pour qu’ils connaissent les objets Time.

Solution : écrire la méthode spéciale __radd__ (right-side add). Cette


méthode est automatiquement invoquée quand un objet Time est le
membre droit d’un opérateur + (et que la méthode __add__ n’existe
pas ou retourne NotImplemented sur le membre gauche).

Programmation (Chap. 13) Introduction aux objets 42 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 177


MASSART Thierry Programmation Transparents - Volume 2

Méthodes

Surcharge d’opérateur

class Time(object):
# ...
def __radd__(self, other):
return self.__add__(other)
# ...
>>> t1 = Time(8)
>>> t2 = 1200 + t1
>>> print t2
08:20:00

Remarque : les méthodes spéciales peuvent être également invoquée


“à la main”, comme n’importe quelle autre méthode.

Programmation (Chap. 13) Introduction aux objets 43 / 48

Méthodes

Polymorphisme

On a déjà vu qu’une fonction peut s’appliquer sur différents types


d’objets, pour peu que le corps de la fonction applique des opérateurs
disponibles pour ce type d’objet. On appelle cette caractéristique le
polymorphisme.

Cela signifie par exemple que l’on peut utiliser la fonction sum sur nos
objets Time !
>>> sum(range(10))
45
>>> t = [Time(1,30), Time(2), Time(3,45,30)]
>>> total_time = sum(t)
>>> print total_time
07:15:30

Programmation (Chap. 13) Introduction aux objets 44 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 178


MASSART Thierry Programmation Transparents - Volume 2

Programmation orientée objets

Programmation orientée objets

Programmation (Chap. 13) Introduction aux objets 45 / 48

Programmation orientée objets

Programmation orientée objets

Des langages comme Python, C++ ou Java permettent de définir des


classes, d’instancier des objets et de les manipuler.

Cette partie de la syntaxe n’est pas nécessaire (on pourrait tout faire
rien qu’avec des fonctions), mais elle permet une programmation
intuitive, concise et réutilisable.

Nous n’avons couvert qu’une toute petite partie des concepts orientés
objets. Il existe une série de techniques très intéressantes et
permettant d’accentuer encore les avantages précités.

Voir : cours “Langages et Algorithmique et Programmation I et II”.

Programmation (Chap. 13) Introduction aux objets 46 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 179


MASSART Thierry Programmation Transparents - Volume 2

Glossaire

Glossaire

Programmation (Chap. 13) Introduction aux objets 47 / 48

Glossaire

Glossaire

classe : une définition de classe permet de définir un nouveau type d’objets.


instance : un objet (qui appartient à une classe).
attribut : une valeur (nommée) associée à un objet.
shallow copy : copie du contenu d’un objet, incluant les références vers d’autres objets
imbriqués (mais pas leur copie).
deep copy : copie du contenu d’un objet ainsi que des objets qui lui sont imbriqués.
fonction pure : une fonction qui ne produit pas d’effet de bord (et donc ne modifie pas les
objets passés en arguments).
méthode d’instance : fonction définie à l’intérieur d’une définition de classe, et qui
s’applique sur les instances (objets) de cette classe.

Programmation (Chap. 13) Introduction aux objets 48 / 48

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 180


MASSART Thierry Programmation Transparents - Volume 2

Chapitre 14. Introduction aux Structures de Données

Contenu du chapitre

1 Structure de Données

2 Listes chaînées

3 Piles

4 Files

5 Listes

Programmation (Chap. 14) Introduction aux Structures de Données 1 / 56

Structure de Données

Structure de Données

Programmation (Chap. 14) Introduction aux Structures de Données 2 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 181


MASSART Thierry Programmation Transparents - Volume 2

Structure de Données

Structure de Données
Une structure de données permet :
de stocker des données;
de réaliser des opérations sur celles-ci.

Exemples : les listes, les tuples, les dictionnaires sont des structures
de données. Les méthodes et opérateurs applicables sur celles-ci
permettent de réaliser des opérations sur celles-ci (rechercher un
élément, ajouter un élément, obtenir le nombre d’éléments, etc.)

Dans ce cours nous allons voir 3 structures de données simples :


listes, piles et files.

On parle également de Type de Données Abstrait (TDA) car on sait ce


que cette structure peut faire et stocker, mais on ne sait pas comment
les données sont organisées concrètement en interne (il peut y avoir
une multitude de manières d’obtenir le même comportement).
Programmation (Chap. 14) Introduction aux Structures de Données 3 / 56

Structure de Données

Liste

Une liste est un TDA permettant les opérations suivantes :


insérer un élément à un indice donné;
obtenir un élément à un indice donné;
obtenir le nombre d’éléments dans la liste;
afficher le contenu de la liste;
etc.

Exemple : les listes en Python

Programmation (Chap. 14) Introduction aux Structures de Données 4 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 182


MASSART Thierry Programmation Transparents - Volume 2

Structure de Données

Pile

Une pile est un TDA permettant les opérations suivantes :


insérer un élément en haut de la pile;
obtenir (et retirer) l’élément du haut de la pile;
obtenir le nombre d’éléments dans la pile;
afficher le contenu de la pile;
etc.

Ce type de structure représente une pile car le dernier élément inséré


est le premier élément retiré (LIFO (Last In First Out)).

Fréquent en informatique : par ex. pile des fonctions appelées (f


appelle g qui appelle h : h sera la première fonction terminée, puis on
retourne en g, etc.).

Programmation (Chap. 14) Introduction aux Structures de Données 5 / 56

Structure de Données

File

Une file est un TDA permettant les opérations suivantes :


insérer un élément à la fin de la file;
obtenir (et retirer) l’élément du début de la file;
obtenir le nombre d’éléments dans la file;
afficher le contenu de la file;
etc.

Ce type de structure représente une file car le premier élément inséré


est le premier élément retiré (FIFO (First In First Out)).

Fréquent en informatique : files de processus à accomplir, de fichiers


à traiter, etc.

Programmation (Chap. 14) Introduction aux Structures de Données 6 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 183


MASSART Thierry Programmation Transparents - Volume 2

Structure de Données

Implémentation
Les opérations sur une structure de données sont la partie visible de
celles-ci. C’est ça qui intéresse l’utilisateur.

D’un point de vue “caché” (implémentation concrète), pour un TDA, il


peut y avoir beaucoup de possibilités :
choix de l’organisation interne des données (tuples, listes,
ensemble de variables, objets, etc.)
choix des algorithmes permettant d’effectuer les opérations
(dépend de la manière dont sont organisées les données).

Exemple : Pour créer une pile, on peut définir une classe Pile dont un des
attributs est une liste data (qui contient les données de la pile). Pour simuler
le fonctionnement de la pile, cette classe n’a qu’une méthode pour insérer un
élément : en le plaçant à la fin de data. De la même manière, elle n’a qu’une
méthode pour obtenir et retirer un élément : en choisissant le dernier élément
de data.

Programmation (Chap. 14) Introduction aux Structures de Données 7 / 56

Structure de Données

Implémentation

Dans ce chapitre, nous supposons que les listes, dictionnaires ou


autres structures de données prédéfinies en Python n’existent pas1 .

Nous allons créer des classes permettant d’implémenter des listes,


des piles et des files, en essayant d’avoir une bonne complexité pour
chacune des opérations autorisées par chaque TDA.

D’un point de vue interne, nous allons organiser les données en


utilisant la notion de listes chaînées.

1
Nous pourrons cependant utiliser des tuples au besoin, mais pas pour stocker les
données de manière interne.
Programmation (Chap. 14) Introduction aux Structures de Données 8 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 184


MASSART Thierry Programmation Transparents - Volume 2

Listes chaînées

Listes chaînées

Programmation (Chap. 14) Introduction aux Structures de Données 9 / 56

Listes chaînées

Objets à taille variable ?

Nos 3 structures de données doivent permettre de stocker un nombre


indéfini d’objets et doivent être mutables.

Problème : comment déterminer les attributs des objets de type liste,


pile ou file, si nous ne connaissons pas le nombre d’éléments à
l’avance (et si les listes, dictionnaires, etc. n’existent pas en Python) ?

Programmation (Chap. 14) Introduction aux Structures de Données 10 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 185


MASSART Thierry Programmation Transparents - Volume 2

Listes chaînées

Objets à taille variable ?

Ce qu’on ne peut pas faire : définir une classe ayant un nombre


indéfini d’attributs2 .

Ce qui est autorisé : un attribut peut être une référence vers un autre
objet, de n’importe quel type.

Exemple : un Rectangle a un attribut Point

Rectangle

width 100.0 Point


height 200.0 x 0.0
corner y 0.0

2
ce n’est pas tout à fait vrai car les attributs d’un objet sont stockés dans un attribut
spécial __dict__ de type dictionnaire, mais c’est spécifique à Python.
Programmation (Chap. 14) Introduction aux Structures de Données 11 / 56

Listes chaînées

Objets à taille variable ?


Ce qui est autorisé : un attribut peut être une référence vers un autre
objet, de n’importe quel type, et donc y compris un autre objet de la
même classe.

Exemple : définissons une classe Node dont les objets auront deux attributs :
data : une donnée à stocker
next : une référence vers un autre Node s’il y a un élément suivant, ou
None sinon.
class Node(object):
""" represente un maillon d’une liste chainee
attributs: data (donnee) et next_ (maillon suivant)
"""
def __init__(self, data = 0, next_ = None):
self.data = data
self.next = next_
p = Node()
p Node

data 0
next None
Programmation (Chap. 14) Introduction aux Structures de Données 12 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 186


MASSART Thierry Programmation Transparents - Volume 2

Listes chaînées

Liste chaînées
Une liste chaînée est constituée de maillons. Chaque maillon (ou
noeud) possède deux attributs : une donnée et une référence vers
l’élément suivant.

Notation : dans nos schémas, on utilise * pour représenter None

Elements dans la liste : 0

0 *

p Node

data 0
next None

p = Node()

Programmation (Chap. 14) Introduction aux Structures de Données 13 / 56

Listes chaînées

Liste chaînées
Une liste chaînée est constituée de maillons. Chaque maillon (ou
noeud) possède deux attributs : une donnée et une référence vers
l’élément suivant.

Notation : dans nos schémas, on utilise * pour représenter None

Elements dans la liste : 0, 1

0 1 *

p Node q Node

data 0 data 1
next next None

q = Node(1)
p.next = q

Programmation (Chap. 14) Introduction aux Structures de Données 14 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 187


MASSART Thierry Programmation Transparents - Volume 2

Listes chaînées

Liste chaînées
Une liste chaînée est constituée de maillons. Chaque maillon (ou
noeud) possède deux attributs : une donnée et une référence vers
l’élément suivant.

Notation : dans nos schémas, on utilise * pour représenter None

Elements dans la liste : 0, 1, 2

2 0 1 *

r Node p Node q Node

data 2 data 0 data 1


next next next None

r = Node(2, p)

Programmation (Chap. 14) Introduction aux Structures de Données 15 / 56

Listes chaînées

Listes chaînées

Pour afficher le contenu d’une liste chaînée, on peut écrire une


fonction qui prend en paramètre un Node, affiche sa donnée, et
continue à afficher les données tant que l’élément suivant n’est pas
None. On utilise une variable p pour référencer le maillon courant.

def print_list(t):
p = t
while p != None:
print p.data, ’->’,
p = p.next
print ’*’
>>> print_list(p)
0 -> 1 -> *
>>> print_list(q)
1 -> *
>>> print_list(r)
2 -> 0 -> 1 -> *

Programmation (Chap. 14) Introduction aux Structures de Données 16 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 188


MASSART Thierry Programmation Transparents - Volume 2

Listes chaînées

Listes chaînées

Travailler avec des listes chaînées revient donc à utiliser 5 opérations


de base :
création d’un élément : créer un nouveau maillon via Node(...)
mise à jour d’une donnée : assigner une référence data via une
affectation (par ex. p.data = 4 ou en passant un premier
paramètre à Node(data, next))
mise à jour de la structure : assigner une référence next via une
affectation (par ex. p.next = q ou en passant un second
paramètre à Node(data, next))
référencer un élément : assigner une référence vers un via une
affectation (par ex. first = r)

Programmation (Chap. 14) Introduction aux Structures de Données 17 / 56

Listes chaînées

Listes chaînées

Chacune de ces quatre opérations peut-être vue comme une


modification du schéma de la liste.

création d’un élément : dessiner un nouveau carré représentant


un maillon;
mise à jour d’une donnée : mettre une nouvelle valeur dans un
carré;
mise à jour de la structure : (re) dessiner une flêche d’un carré
vers un autre (ou vers *);
référencer un élément : dessiner une flêche vers un carré.

Programmation (Chap. 14) Introduction aux Structures de Données 18 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 189


MASSART Thierry Programmation Transparents - Volume 2

Listes chaînées

Listes chaînées p p q

0 * 0 1 *
Exemple : pour passer de à :
on crée le nouveau maillon, on lui affecte les valeurs 1 (data) et
None (next), et on assigne la référence vers ce nouveau maillon à
q:
q = Node(1)
on met à jour la “flêche” (= l’attribut next) du maillon référencé
par p :
p.next = q
Cette dernière instruction peut être lue comme : mettre à jour la flêche
de p et la faire pointer vers ce que pointe q.

D’un point de vue machine, la valeur de q est une adresse en mémoire


(l’adresse de l’endroit où est stocké le second maillon). Nous voulons que p
connaisse l’adresse en mémoire de l’élément suivant et nous la stockons
donc dans p.next.
Programmation (Chap. 14) Introduction aux Structures de Données 19 / 56

Listes chaînées

Listes chaînées
Exemple :
def print_list(t):
p = t
while p != None:
print p.data, ’->’,
p = p.next
print ’*’
>>> print_list(r)
2 -> 0 -> 1 -> *

Dans cette fonction, p est une “flêche” vers, d’abord, le maillon passé
en argument et référencé par t, puis est mis à jour pour pointer vers
l’élément suivant et ainsi de suite.
p

2 0 1 *
p

2 0 1 *
p

2 0 1 *

Programmation (Chap. 14) Introduction aux Structures de Données 20 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 190


MASSART Thierry Programmation Transparents - Volume 2

Listes chaînées

Listes chaînées
Exercice : comment insérer 3 entre 2 et 0 ? On suppose qu’on a
seulement une référence first vers le premier élément.

first

2 0 1 *

Programmation (Chap. 14) Introduction aux Structures de Données 21 / 56

Listes chaînées

Listes chaînées
Exercice : comment insérer 3 entre 2 et 0 ? On suppose qu’on a
seulement une référence first vers le premier élément.

Solution :
first

2 0 1 *

p
3 *

1 création nouveau maillon référencé par p : p = Node(3)

Programmation (Chap. 14) Introduction aux Structures de Données 21 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 191


MASSART Thierry Programmation Transparents - Volume 2

Listes chaînées

Listes chaînées
Exercice : comment insérer 3 entre 2 et 0 ? On suppose qu’on a
seulement une référence first vers le premier élément.

Solution :
first

2 0 1 *

p
3

1 création nouveau maillon référencé par p : p = Node(3)


2 mise à jour du suivant de p : p.next = first.next

Programmation (Chap. 14) Introduction aux Structures de Données 21 / 56

Listes chaînées

Listes chaînées
Exercice : comment insérer 3 entre 2 et 0 ? On suppose qu’on a
seulement une référence first vers le premier élément.

Solution :
first

2 0 1 *

p
3

1 création nouveau maillon référencé par p : p = Node(3)


2 mise à jour du suivant de p : p.next = first.next
3 mise à jour du suivant de first : first.next = p
L’ordre est important ! (voyez-vous pourquoi ?)

Programmation (Chap. 14) Introduction aux Structures de Données 21 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 192


MASSART Thierry Programmation Transparents - Volume 2

Listes chaînées

Listes chaînées

Exercice : comment supprimer l’élément 0 de la liste ?

first

2 0 1 *

Programmation (Chap. 14) Introduction aux Structures de Données 22 / 56

Listes chaînées

Listes chaînées

Exercice : comment supprimer l’élément 0 de la liste ?

Solution :
first

2 0 1 *

1 mise à jour du suivant de first :


first.next = first.next.next
Cela suffit ! Le maillon “0” est déconnecté et inaccessible
puisqu’aucune référence ne pointe plus vers lui.

Programmation (Chap. 14) Introduction aux Structures de Données 22 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 193


MASSART Thierry Programmation Transparents - Volume 2

Listes chaînées

Listes chaînées

Les listes chaînées permettent donc de créer des structures de


données de tailles variables.

Nous allons les utiliser pour implémenter les piles, les files et les listes.
La classe de base représentant un maillon d’une chaîne sera la
suivante (dans un module node.py disponible sur e-learning).

Remarque : La méthode spéciale __str__ est appelée


automatiquement via la fonction str ou une instruction print; la
méthode spéciale __repr__ est appelée automatiquement via la
fonction repr ou quand on donne une variable simple dans la console
interactive.

Programmation (Chap. 14) Introduction aux Structures de Données 23 / 56

Listes chaînées

Listes chaînées

class Node(object):
""" represente un maillon d’une liste chainee
attributs: data (donnee) et next (maillon suivant)
"""
def __init__(self, data = 0, next_ = None):
self.data = data
self.next = next_

def __str__(self):
return str(self.data)

def has_next(self):
""" retourne vrai ssi ce maillon a un maillon suivant """
return self.next != None

Programmation (Chap. 14) Introduction aux Structures de Données 24 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 194


MASSART Thierry Programmation Transparents - Volume 2

Listes chaînées

Listes chaînées
(suite)
def __repr__(self):
res = ’’
p = self
while p != None:
res += str(p) + ’ -> ’
p = p.next
res += ’*’
return res

>>> p = Node(3, Node(4))


>>> p
3 -> 4 -> *
>>> print p
3
>>> print p.next
4
>>> p.has_next()
True
>>> p.next.has_next()
False

Programmation (Chap. 14) Introduction aux Structures de Données 25 / 56

Piles

Piles

Programmation (Chap. 14) Introduction aux Structures de Données 26 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 195


MASSART Thierry Programmation Transparents - Volume 2

Piles

Piles
Nous désirons les opérations (méthodes) suivantes, avec la meilleure
complexité possible, et en utilisant une liste chaînée en interne :
is_empty() : retourne vrai ssi la pile est vide;
count() : retourne le nombre d’éléments dans la pile;
head() : retourne l’élément en haut de la pile (sans le retirer);
pop() : retourne l’élément en haut de la pile et le retire de celle-ci;
push(x) : insère x en haut de la pile.
display() : affiche le contenu de la pile.
clear() : vide la pile.
__init__() : initialise la pile (vide par défaut) ou avec un nombre
d’éléments variables.
Pour rendre nos piles plus facilement utilisables, nous allons
également associer certaines des méthodes spéciales à nos
méthodes (par ex. __str__ et display).
Programmation (Chap. 14) Introduction aux Structures de Données 27 / 56

Piles

Piles
Attributs : une référence vers le premier maillon d’une liste chaînée et
le nombre n d’éléments dans la pile (pour éviter de traverser la pile
pour compter le nombre d’éléments).

Idée : le premier élément de la liste chaînée sera l’élément en “haut”


de la pile, ce qui permettra d’y travailler en O (1), sans devoir parcourir
la liste jusqu’à la fin.
from node import Node

class Pile(object):
""" represente une pile (LIFO) """
def __init__(self):
self.top = None

Pour le moment, l’attribut top ne référencie aucun Node : la pile est


vide. On améliorera __init__ par après (ajouter un nombre variable
d’éléments).

Programmation (Chap. 14) Introduction aux Structures de Données 28 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 196


MASSART Thierry Programmation Transparents - Volume 2

Piles

Piles

Méthode push : Il faut insérer un élément au début de la liste chaînée.


class Pile(object): top *
# ...
def push(self, x):
top *
p = Node(x)
p.next = self.top p
2
self.top = p
self.n += 1
...
>>> stack = Pile()
>>> stack.push(2) top
>>> stack.push(4) 4 2 *
>>> stack.push(3)
>>> stack.top p
3 -> 4 -> 2 -> *
3
>>> print stack.n
3

Programmation (Chap. 14) Introduction aux Structures de Données 29 / 56

Piles

Piles

Méthodes is_empty et count. La méthode spéciale __len__ sera


appelée automatiquement via la fonction len.
>>> stack = Pile()
class Pile(object):
>>> stack.is_empty()
#...
True
def is_empty(self):
>>> stack.push(2)
return self.top == None
>>> stack.is_empty()
False
def count(self):
>>> stack.count()
return self.n
1
>>> stack.push(3)
def __len__(self):
>>> len(stack)
return self.count()
2

Programmation (Chap. 14) Introduction aux Structures de Données 30 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 197


MASSART Thierry Programmation Transparents - Volume 2

Piles

Piles

Méthodes d’affichage.
class Pile(object):
# ...
>>> stack = Pile()
def __str__(self):
>>> stack.push(1)
res = ’’
>>> stack.push(2)
p = self.top
>>> stack.push(3)
while p != None:
>>> print stack
if not p is self.top:
3
res += ’\n’
2
res += str(p)
1
p = p.next
>>> stack.display()
return res
3
2
def display(self):
1
print self
>>> stack
3 -> 2 -> 1 -> *
def __repr__(self):
return repr(self.top)

Programmation (Chap. 14) Introduction aux Structures de Données 31 / 56

Piles

Piles

Avant d’implémenter top et pop, nous aimerions pouvoir exprimer le


fait que ces méthodes doivent être appelées sur des piles non vides.
Pour ce faire, nous allons définir un nouveau type d’exception.

Pour créer un nouveau type d’exception la syntaxe est simple : les


exceptions sont des objets de type Exception. On peut le faire de la
manière suivante (on inclut ceci dans le module node.py pour que ce
soit également utilisable pour les files et les listes).

Il n’y a pas besoin de définir des attributs ou des méthodes pour une
exception.
class EmptyError(Exception):
pass

Programmation (Chap. 14) Introduction aux Structures de Données 32 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 198


MASSART Thierry Programmation Transparents - Volume 2

Piles

Piles

Méthodes head et pop.


#... >>> stack.push(1)
from node import EmptyError >>> stack.push(2)
>>> stack.push(3)
class Pile(object): >>> stack.head()
#... 3
def head(self): >>> res = stack.pop()
if self.is_empty(): >>> print stack
raise EmptyError(’Stack is empty’) 2
return self.top.data 1
>>> res
def pop(self): 3
if self.is_empty(): >>> stack.pop()
raise EmptyError(’Stack is empty’) 2
res = self.top.data >>> stack.pop()
self.top = self.top.next 1
self.n -= 1 >>> stack.pop()
return res node.EmptyError: Stack is empty

Programmation (Chap. 14) Introduction aux Structures de Données 33 / 56

Piles

Piles

Méthodes clear et __init__ (revisitée).


>>> stack = Pile(1, 2, 3)
class Pile(object):
>>> stack
def __init__(self, *args):
3 -> 2 -> 1 -> *
self.top = None
>>> stack.pop()
self.n = 0
3
for i in args:
>>> len(stack)
self.push(i)
2
>>> stack.clear()
def clear(self):
>>> len(stack)
self.top = None
0
self.n = 0
>>> stack.is_empty()
# ...
True

Programmation (Chap. 14) Introduction aux Structures de Données 34 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 199


MASSART Thierry Programmation Transparents - Volume 2

Piles

Piles

Le code complet de la classe Pile est disponible sur e-learning.

Complexité

les méthodes d’affichage sont en O (n) où n est le nombre


d’éléments de la pile;
la méthode __init__ est en O (k ) où k est le nombre
d’arguments passés;
toutes les autres méthodes sont en O (1) grâce à l’attribut n et
l’insertion / suppression au début de la liste chaînée.

On ne pourrait pas faire mieux !

Programmation (Chap. 14) Introduction aux Structures de Données 35 / 56

Piles

Piles
Application : le script suivant montre une utilisation des piles pour
montrer la pile des fonctions appelées lors d’appels récursifs.
from pile import Pile

f_stack = Pile()

def somme(n):
msg = ’call somme(’ + str(n) + ’)’
f_stack.push(msg)
if n <= 1:
res = n
print ’Basis reached. Current stack:’
print f_stack
print ’-----------------------------’
else:
res = n + somme(n-1)
done = f_stack.pop()
print done + ’ is finished’
return res

somme(4)

Programmation (Chap. 14) Introduction aux Structures de Données 36 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 200


MASSART Thierry Programmation Transparents - Volume 2

Piles

Piles

Application : output

Basis reached. Current stack:


call somme(1)
call somme(2)
call somme(3)
call somme(4)
-----------------------------
call somme(1) is finished
call somme(2) is finished
call somme(3) is finished
call somme(4) is finished

Programmation (Chap. 14) Introduction aux Structures de Données 37 / 56

Files

Files

Programmation (Chap. 14) Introduction aux Structures de Données 38 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 201


MASSART Thierry Programmation Transparents - Volume 2

Files

Files

Nous désirons les opérations (méthodes) suivantes, avec la meilleure


complexité possible, et en utilisant une liste chaînée en interne :
is_empty() : retourne vrai ssi la file est vide;
count() : retourne le nombre d’éléments dans la file;
head() : retourne l’élément au début de la file (sans le retirer);
remove() : retourne l’élément au début de la file et le retire de
celle-ci;
insert(x) : insère x en fin de file.
display() : affiche le contenu de la file.
clear() : vide la file.
__init__() : initialise la file (vide par défaut) ou avec un nombre
d’éléments variables.

Programmation (Chap. 14) Introduction aux Structures de Données 39 / 56

Files

Files

Attributs : une référence vers le premier maillon de la liste chaînée,


une référence vers le dernier maillon de la liste chaînée et le nombre n
d’éléments dans la file.

Idée : le premier élément de la liste chaînée sera l’élément au début


de la file (first), ce qui permettra d’y travailler en O (1). On conserve
également une référence vers le dernier élément de la liste chaînée
pour pouvoir insérer un nouvel élément en O (1) (last).

Programmation (Chap. 14) Introduction aux Structures de Données 40 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 202


MASSART Thierry Programmation Transparents - Volume 2

Files

Files

Module file.py
from node import Node
from node import EmptyError

class File(object):
""" represente une file (FIFO) """
def __init__(self, *args):
self.first = None
self.last = None
self.n = 0
for i in args:
self.insert(i)

def clear(self):
self.first = None
self.last = None
self.n = 0

Programmation (Chap. 14) Introduction aux Structures de Données 41 / 56

Files

Files

(suite)
def insert(self, x):
p = Node(x)
if self.is_empty():
self.first = p
self.last = p
else:
self.last.next = p
self.last = p
self.n += 1

def head(self):
if self.is_empty():
raise EmptyError(’Queue is empty’)
return self.first.data

Programmation (Chap. 14) Introduction aux Structures de Données 42 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 203


MASSART Thierry Programmation Transparents - Volume 2

Files

Files

(suite)
def remove(self):
if self.is_empty():
raise EmptyError(’Queue is empty’)
res = self.first.data
self.first = self.first.next
self.n -= 1
return res

def is_empty(self):
return self.first == None

def count(self):
return self.n

def __len__(self):
return self.count()

Programmation (Chap. 14) Introduction aux Structures de Données 43 / 56

Files

Files

(suite)
def __str__(self):
res = ’’
p = self.first
while p != None:
if not p is self.first:
res += ’ < ’
res += str(p)
p = p.next
return res

def __repr__(self):
return repr(self.first)

def display(self):
print self

Programmation (Chap. 14) Introduction aux Structures de Données 44 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 204


MASSART Thierry Programmation Transparents - Volume 2

Files

Files

>>> queue = File(1, 2, 3)


>>> print queue
1 < 2 < 3
>>> queue
1 -> 2 -> 3 -> *
>>> len(queue)
3
>>> queue.insert(4)
>>> queue.head()
1
>>> queue.remove()
1
>>> queue.remove()
2
>>> queue.clear()
>>> queue.remove()
node.EmptyError: Queue is empty

Programmation (Chap. 14) Introduction aux Structures de Données 45 / 56

Files

Files

Le code complet de la classe File est disponible sur e-learning.

Complexité

les méthodes d’affichage sont en O (n) où n est le nombre


d’éléments de la pile;
la méthode __init__ est en O (k ) où k est le nombre
d’arguments passés;
toutes les autres méthodes sont en O (1) grâce à l’attribut n et les
deux références permettant de faire l’insertion et la suppression
en O (1).

Ici aussi, on ne pourrait pas faire mieux.

Programmation (Chap. 14) Introduction aux Structures de Données 46 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 205


MASSART Thierry Programmation Transparents - Volume 2

Files

Files

Application : le script suivant montre une utilisation des files pour


simuler la file d’attente d’un bureau administratif. Chaque minute, il y a
1 chance sur 5 qu’un nouveau client arrive (ce qui fait en moyenne 12
clients par heure). Le responsable du guichet prend entre 5 et 15
minutes pour servir un client. Le bureau est ouvert de 8 à 12h et de
13h à 16h. A l’heure de fermeture, les clients encore présents dans la
file quittent le bureau sans avoir été servis.

Comment simuler cette situation pour estimer le nombre de clients


mécontents ?

Nous utilisons la classe Time définie au Chapitre 13, pour laquelle on a


ajouté une surcharge de l’opérateur <=.

Programmation (Chap. 14) Introduction aux Structures de Données 47 / 56

Files

Files
from random import randint
from random import choice
from time import Time
from file import File

class Someone(object):
def __init__(self):
self.name = self.random_firstname() + ’ ’ + self.random_lastname()

def random_firstname(self):
firstnames = (’Bill’, ’Joe’, ’Jack’, ’Georges’, ’Alan’, ’Lisa’,
’Sarah’, ’Kate’, ’Charley’)
return choice(firstnames)

def random_lastname(self):
lastnames = (’Black’, ’Baroud’, ’Cormen’, ’Rivest’, ’Aho’,
’Ullman’, ’Turing’, ’Escher’)
return choice(lastnames)

def __str__(self):
return self.name

Programmation (Chap. 14) Introduction aux Structures de Données 48 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 206


MASSART Thierry Programmation Transparents - Volume 2

Files

Files

(suite)
class Simulation(object):
def __init__(self, start, end):
self.serving = False
self.minUntilDone = 0
self.wait_queue = File()
self.clock = start
self.end = end

def has_new_client(self):
return randint(1, 5) == 1

def random_servetime(self):
return randint(5,15)

def is_finished(self):
return self.clock >= self.end

Programmation (Chap. 14) Introduction aux Structures de Données 49 / 56

Files

Files
(suite, toujours dans la classe Simulation)
def simulate_one_minute(self):
if self.has_new_client():
new = Someone()
print self.clock, ’:’, new, ’just arrived’
self.wait_queue.insert(new)
if self.serving:
if self.minUntilDone == 0:
self.serving = False
old = self.wait_queue.remove()
print self.clock, ’:’, old ,’is happy and leaving office’
else:
self.minUntilDone -= 1
else:
if not self.wait_queue.is_empty():
self.serving = True
self.minUntilDone = self.random_servetime()
print self.clock, ’:’, self.wait_queue.head() ,
print ’is currently served’
self.clock = self.clock + 60

Programmation (Chap. 14) Introduction aux Structures de Données 50 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 207


MASSART Thierry Programmation Transparents - Volume 2

Files

Files

(suite, toujours dans la classe Simulation)


def open_office(self):
print ’It is’, self.clock, ’the office is open!’

def close_office(self):
print ’It is’, self.clock, ’the office is closed!’
print ’There are’, len(self.wait_queue),’people in the queue that are’,
print ’in very bad mood.’
self.wait_queue.clear()
self.serving = False

Programmation (Chap. 14) Introduction aux Structures de Données 51 / 56

Files

Files

(suite, code principal)


sim = Simulation(Time(8), Time(12))
sim.open_office()
while not sim.is_finished():
sim.simulate_one_minute()
sim.close_office()

sim = Simulation(Time(13), Time(16))


sim.open_office()
while not sim.is_finished():
sim.simulate_one_minute()
sim.close_office()

Programmation (Chap. 14) Introduction aux Structures de Données 52 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 208


MASSART Thierry Programmation Transparents - Volume 2

Files

Files
Application : output
It is 08:00:00 the office is open!
08:00:00 : Bill Black just arrived
08:00:00 : Bill Black is currently served
08:02:00 : Georges Escher just arrived
08:08:00 : Alan Cormen just arrived
08:09:00 : Georges Ullman just arrived
08:10:00 : Joe Ullman just arrived
08:10:00 : Bill Black is happy and leaving office
08:10:00 : Georges Escher is currently served
...
It is 12:00:00 the office is closed!
There are 27 people in the queue that are in very bad mood.
It is 13:00:00 the office is open!
13:03:00 : Joe Cormen just arrived
13:03:00 : Joe Cormen is currently served
13:08:00 : Joe Cormen is happy and leaving office
13:10:00 : Sarah Ullman just arrived
13:10:00 : Sarah Ullman is currently served
13:13:00 : Georges Cormen just arrived
13:15:00 : Kate Cormen just arrived
13:24:00 : Sarah Ullman is happy and leaving office
13:24:00 : Georges Cormen is currently served
13:26:00 : Alan Rivest just arrived
13:36:00 : Charley Rivest just arrived
13:38:00 : Georges Cormen is happy and leaving office
13:38:00 : Kate Cormen is currently served
...
It is 16:00:00 the office is closed!
There are 16 people in the queue that are in very bad mood.

Programmation (Chap. 14) Introduction aux Structures de Données 53 / 56

Listes

Listes

Programmation (Chap. 14) Introduction aux Structures de Données 54 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 209


MASSART Thierry Programmation Transparents - Volume 2

Listes

Listes
Nous désirons les opérations (méthodes) suivantes, et en utilisant une
liste chaînée en interne :
is_empty() : retourne vrai ssi la liste est vide;
count() : retourne le nombre d’éléments dans la liste;
remove(i) : retourne et supprime l’élément qui se trouve à
l’indice i;
insert(x [, i]) : insère x à la fin de la liste si i n’est pas
présent, ou entre les élements d’indice i et i + 1 si i est précisé;
display() : affiche le contenu de la liste.
clear() : vide la liste.
__init__() : initialise la liste (vide par défaut) ou avec un nombre
d’éléments variables.
+ : concatène deux listes (surcharge de __add(self, other)__)
[i] : retourne l’élément d’indice i (surcharge de
__getitem__(self, index))
Programmation (Chap. 14) Introduction aux Structures de Données 55 / 56

Listes

Listes

A vous de jouer !

Préparez une classe Liste qui implémente les opérations


demandées:
en essayant d’avoir une bonne complexité (mais accès à un
indice donné ne sera pas en O (1));
en lançant des exceptions EmptyError ou IndexError quand
nécessaire.

A préparer pour le prochain TP.

Programmation (Chap. 14) Introduction aux Structures de Données 56 / 56

INFO-F-101_B PUB Cours-Librairie, av. P. Héger 42, B-1000 Bruxelles 210

Vous aimerez peut-être aussi