Académique Documents
Professionnel Documents
Culture Documents
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. »
Plus d’informations ?
www.fsc.be
A la recherche de produits FSC ?
www.jecherchedufsc.be
Chapitre 8. Tuples
Contenu du chapitre
3 Listes et tuples
4 Dictionnaires et tuples
5 Comparaison de tuples
6 Glossaire
1
L’utilisation des parenthèses est facultative, mais est souvent utilisée pour plus de
clarté.
Programmation (Chap. 8) Tuples 3 / 29
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).
2
Dans le cas des dictionnaires, retourne un tuple des clefs.
Programmation (Chap. 8) Tuples 6 / 29
Assignation de tuples
Assignation de tuples
Assignation de tuples
max(...)
max(iterable[, key=func]) -> value
max(a, b, c, ...[, key=func]) -> value
>>> 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])
>>> 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)
divmod(...)
divmod(x, y) -> (div, mod)
Listes et tuples
Listes et tuples
Listes et tuples
Listes et tuples
Listes et tuples
Listes et tuples
0 a
1 b
2 c
Listes et tuples
Listes et tuples
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]:
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
Dictionnaires et tuples
Dictionnaires et tuples
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é).
Dictionnaires et tuples
Dictionnaires et tuples
a 0
c 2
b 1
d 3
Dictionnaires et tuples
Dictionnaires et tuples
Comparaison de tuples
Comparaison de tuples
Comparaison de tuples
Comparaison de tuples
Glossaire
Glossaire
Glossaire
Glossaire
Chapitre 9. Récursivité
Contenu du chapitre
1 Concept de récursivité
2 Glossaire
Concept de récursivité
Concept de récursivité
Concept de récursivité
Rappel du principe
Concept de récursivité
Exemple
n(n + 1)
S (n) = ∀n ≥ 1 (1)
2
1×2
1= ,
2
ce qui est exact.
Concept de récursivité
(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
Concept de récursivité
On a donc bien
n(n + 1)
S (n) = ∀n ≥ 1.
2
Concept de récursivité
Récursivité
Problème
Comment calculer la somme des n premiers nombres entiers ?
55
Concept de récursivité
Récursivité
55
Concept de récursivité
Récursivité
Intuition : le programmeur paresseux
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).
Concept de récursivité
Récursivité
Problème
Ecrire une fonction qui calcule la factorielle d’un nombre entier.
Concept de récursivité
Récursivité
Problème
Ecrire une fonction qui calcule la factorielle d’un nombre entier.
Concept de récursivité
Récursivité
En Python :
24
Plus simplement :
Concept de récursivité
Récursivité
Problème
Ecrire une fonction qui calcule an , si n ≥ 1.
Concept de récursivité
Récursivité
Problème
Ecrire une fonction qui calcule an , si n ≥ 1.
Concept de récursivité
Récursivité
En Python :
27 27
Concept de récursivité
Concept de récursivité
Récursivité
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 !
Concept de récursivité
Concept de récursivité
Concept de récursivité
Concept de récursivité
Concept de récursivité
Concept de récursivité
Concept de récursivité
Concept de récursivité
Concept de récursivité
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
Concept de récursivité
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).
Concept de récursivité
Concept de récursivité
Concept de récursivité
Concept de récursivité
Concept de récursivité
>>> post_count(3)
3
2
1
Boum
Concept de récursivité
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” ?
Concept de récursivité
Etape de recurrence:
pre_count(n - 1)
print 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
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.
F0 = 0,
F1 = 1,
Fn = Fn−1 + Fn−2 , ∀n ≥ 2.
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)
Concept de récursivité
Fibonacci amélioré
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
Concept de récursivité
Récursion infinie
def recurse():
recurse()
Concept de récursivité
Récursion infinie
Concept de récursivité
Récursion infinie
>>> sys.setrecursionlimit(10000)
>>> factorial(1.5)
...
RuntimeError: maximum recursion depth exceeded
Concept de récursivité
>>> factorial(’fred’)
Factorial is only defined for integers.
None
>>> factorial(-2)
Factorial is only defined for positive integers.
None
Glossaire
Glossaire
Glossaire
Glossaire
Contenu du chapitre
1 Quelques algorithmes
Quelques algorithmes
Quelques algorithmes
Quelques algorithmes
Quelques algorithmes
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
Quelques algorithmes
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
Quelques algorithmes
Quelques algorithmes
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
Quelques algorithmes
Quelques algorithmes
Quelques algorithmes
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
Quelques algorithmes
Quelques algorithmes
Problème
Soit une séquence de n entiers (n ≥ 0), comment trier (par ordre
croissant) les éléments de la séquence ?
Quelques algorithmes
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
Quelques algorithmes
i small i small
Quelques algorithmes
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]
Quelques algorithmes
Problème
Prouver que la division euclidienne par soustraction s’arrête quand
a ≥ 0 et b > 0.
Preuve.
Au tableau.
Problème
Prouver que le calcul de l’exposant s’arrête quand n ≥ 0.
Preuve.
Au tableau.
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.
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 !
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.
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.
Invariant de boucle
Invariant de boucle
Invariant de boucle
a = b × q + r, (1)
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
Invariant de boucle
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])
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])
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])
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.
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
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.
Base. Avant d’entrer dans la boucle, res = 0 qui est bien égal à
1
∑−
j =0 liste[j ]. Donc S (0) est vraie.
k −1 k
resnew = resold + liste[k ] = ∑ liste[j ] + liste[k ] = ∑ liste[j ].
j =0 j =0
n−1 n
∑ i +n = ∑ i.
i =1 i =1
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.
9 Glossaire
Référence
1
Central Processing Unit.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 4 / 126
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).
Quelques nouveaux
algorithmes
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
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
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]
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
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
3 7 2 6 5 1 4
3 7 2 6 5 1 4
3 7 2 6 1 5 4
3 7 2 6 5 1 4
2 3 6 7 1 4 5
Merge Merge
3 7 2 6 1 5 4
3 7 2 6 5 1 4
2 3 6 7 1 4 5
1 2 3 4 5 6 7
Merge
2 3 6 7 1 4 5
1 2 3 4 5 6 7
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
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
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 ?
produit
100000 loops, best of 3: 16 usec per loop
Comparaison d’algorithmes
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)
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 ----
Comparaison d’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)
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
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é.
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.
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
Temps d’exécution
Ce qui nous intéresse c’est la croissance de T (n), pas les constantes.
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
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
Temps d’exécution
x3
Comparaison de 1000x + 1000, 10x 2 et 100
− x sur [0, 70]
Temps d’exécution
x3
Comparaison de 1000x + 1000, 10x 2 et 100
− x sur [600, 2000]
Temps d’exécution
Temps d’exécution
√
Comparaison de log n, n et n sur [0, 50].
Temps d’exécution
Comparaison de n, n log n et n2 sur [0, 25].
Temps d’exécution
Comparaison de n3 et 2n sur [0, 15].
Temps d’exécution
Comparaison de 2n et n! sur [1, 20].
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
2n2 ≤ 36 × 108 ,
√
donc n ≤ 18 × 108 � 42426.
Notation grand-O
Notation grand-O
N → R∗ = {r ∈ R | r ≥ 0} .
f (n) ∈ O (g (n)) ,
ou
f (n) = O (g (n)) ,
Notation grand-O
“f est en grand-O de g”
Définition formelle :
où n0 ≥ 0 et c > 0.
Notation grand-O
c g(n)
Vision asymptotique :
Notation grand-O
Notation grand-O
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
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.
Preuve.
1
Soit c = d
et n0 = 0, on a
f (n) := ak nk + ak −1 nk −1 + . . . + a2 n2 + a1 n + a0 ,
où ak > 0. Alors, � �
f (n) ∈ O nk
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.
On peut généraliser cela : si g (n) croît moins vite que h(n), alors
np
lim = 0.
n→∞ an
Donc, par exemple,
2n + n3 ∈ O (2n ).
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,
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
Exemple
Exemples
(a, b) = swap(a, b)
(a, b) = swap(a, b)
def f(n):
res = g(n)
res += h(n)
return res
4
http://wiki.python.org/moin/TimeComplexity
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 73 / 126
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.
Exemples
t.append(3)
print 3 in t
t2 = t * 2
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
Exemple
1 def sum_all(liste):
2 res = 0
3 for item in liste:
4 res += item
5 return res
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
Remarque :
à chaque itération, la condition est évaluée, donc il faut en tenir
compte si celle-ci a un coût > O (1).
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 ).
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 ?
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’
Preuve.
Au tableau.
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
T (1) = a,
T (n) = T (n − 1) + b, ∀n > 1.
T (1) = a,
T (2) = T (1) + b = a + b,
T (3) = T (2) + b = a + 2b,
T (4) = T (3) + b = a + 3b.
T (1) = a,
T (n) = T (n − 1) + b, ∀n > 1.
Comparaison d’algorithmes
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 100 / 126
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
Recherche linéaire :
def linear_search(t, w):
for word in t:
if word == w:
return True
return False
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 102 / 126
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
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 104 / 126
Comparaison de différents
algorithmes
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 105 / 126
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 106 / 126
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
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 107 / 126
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)
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 108 / 126
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 109 / 126
Comparaisons de la comlexité
d’algorithmes sur des
dictionnaires
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 110 / 126
>>> 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’]
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 111 / 126
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’]}
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 112 / 126
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
Variables globales
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 115 / 126
Variables globales
>>> 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
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
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 117 / 126
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]
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 118 / 126
Variables globales
>>> f()
UnboundLocalError: local variable ’count’ referenced before assignment
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 119 / 126
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).
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
Variables globales
Dans g: a = 1
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 121 / 126
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
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
Longs entiers
Si vous calculez le 50ième nombre de Fibonacci, vous obtenez ceci :
>>> fib_dict(50)
12586269025L
Glossaire
Glossaire
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 125 / 126
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
Contenu du chapitre
4 Exceptions
6 Glossaire
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.
Script readLines.py :
import sys
filename = sys.argv[1]
f = open(filename)
firstLine = f.readline()
print ’First line:’, firstLine.strip()
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
filename = sys.argv[1]
f = open(filename)
content = f.read()
print ’content:’, repr(content)
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()
Line 1
Line 2
New text
Opérateur de format
Opérateur de format
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%
Opérateur de format
Opérateur de format
Exemple :
import math
def f(n):
return math.sqrt(math.sqrt(2**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
Opérateur de format
On peut également utiliser des “flags”.
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 *
Syntaxe Linux ou Mac, sous windows, on remplace “/” par “\”. L’attribut os.sep
1
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’]
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])
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.).
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
Pickling
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
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)}
Exceptions
Exceptions
Exceptions
Exceptions
Exceptions
Exceptions
Les types d’exceptions les plus fréquentes que vous pouvez lancer :
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’
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).
Exemple :
try:
print ’Avant’
raise ValueError(’Test’)
print ’Apres’
except:
print ’Gestion exception’
print ’Fin’
Résultat :
Avant
Gestion exception
Fin
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
Exemple :
while True:
try:
x = int(raw_input("Please enter a number: "))
break
except ValueError:
print "Oops! That was no valid integer. Try again..."
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
Exemple :
try:
f = open(’myfile.txt’)
s = f.readline()
i = int(s.strip())
except (IOError, ValueError):
print ’Cannot open file or cannot convert integer’
Exemple :
import sys
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
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
Exemple :
try:
raise KeyboardInterrupt
finally:
print ’Goodbye, world!’
Goodbye, world!
KeyboardInterrupt
Exemple :
import time
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!’
Glossaire
Glossaire
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.
Contenu du chapitre
1 Classes et objets
2 Fonctions pures
3 Méthodes
5 Glossaire
Classes et objets
Classes et objets
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.
Classes et objets
La notion de classe
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)
Classes et objets
La notion d’objets
Classes et objets
La notion d’objets
>>> p = Point()
>>> print p
<__main__.Point object at 0xe73bd0>
p 0xe73bd0
Point
Classes et objets
Attributs
Une des propriétés des objets sont ses attributs. Un attribut est une
valeur.
Point
x 2.0
y 4.0
Classes et objets
Attributs
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
Classes et objets
Attributs
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
Classes et objets
Attributs
Classes et objets
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).
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 ?
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 !
Classes et objets
Copie d’objets
Soit :
>>> box2 = copy.copy(box)
>>> box2 is box
False
Classes et objets
Copie d’objets
Soit :
>>> box2 = copy.copy(box)
>>> box2 is box
False
True !
Classes et objets
Copie d’objets
Fonctions pures
Fonctions pures
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).
Fonctions pures
Fonctions pures
def print_time(t):
print ’%02d:%02d:%02d’ % (t.hour, t.minute, t.second)
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.
Fonctions pures
Fonctions pures
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
return res
>>> done = add_time(start, duration)
>>> print_time(done)
11:20:00
Fonctions pures
Fonctions pures
def sec_to_time(secs):
time = Time()
mins, time.second = divmod(secs, 60)
time.hour, time.minute = divmod(mins, 60)
return time
Méthodes
Méthodes
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).
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.
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).
Méthodes
Méthodes
def print_time(t):
print ’%02d:%02d:%02d’ % (t.hour, t.minute, t.second)
Méthodes
Méthodes
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
Méthodes
Méthodes
Intuition
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.
Méthodes
Méthodes
class Time(object):
# ...
def to_sec(self):
mins = self.hour * 60 + self.minute
secs = mins * 60 + self.second
return secs
Méthodes
Méthodes
Méthodes
La méthode init
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
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
# ...
Méthodes
La méthode str
>>> t = Time(8,30)
>>> print t
08:30:00
>>> str(t)
’08:30:00’
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.
Méthodes
Surcharge d’opérateur
>>> t1 = Time(9)
>>> t2 = Time(1,30)
>>> t3 = t1 + t2
>>> print t3
10:30:00
Voir: docs.python.org/ref/specialnames.html
Méthodes
Surcharge d’opérateur
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
Méthodes
Surcharge d’opérateur
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
Méthodes
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
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.
Glossaire
Glossaire
Glossaire
Glossaire
Contenu du chapitre
1 Structure de Données
2 Listes chaînées
3 Piles
4 Files
5 Listes
Structure de Données
Structure de Données
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.)
Structure de Données
Liste
Structure de Données
Pile
Structure de Données
File
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.
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.
Structure de Données
Implémentation
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
Listes chaînées
Listes chaînées
Listes chaînées
Listes chaînées
Ce qui est autorisé : un attribut peut être une référence vers un autre
objet, de n’importe quel type.
Rectangle
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
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
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.
0 *
p Node
data 0
next None
p = Node()
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.
0 1 *
p Node q Node
data 0 data 1
next next None
q = Node(1)
p.next = q
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.
2 0 1 *
r = Node(2, p)
Listes chaînées
Listes chaînées
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 -> *
Listes chaînées
Listes chaînées
Listes chaînées
Listes chaînées
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.
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 *
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 *
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 *
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
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
Listes chaînées
Listes chaînées
first
2 0 1 *
Listes chaînées
Listes chaînées
Solution :
first
2 0 1 *
Listes chaînées
Listes chaînées
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).
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
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
Piles
Piles
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).
class Pile(object):
""" represente une pile (LIFO) """
def __init__(self):
self.top = None
Piles
Piles
Piles
Piles
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)
Piles
Piles
Il n’y a pas besoin de définir des attributs ou des méthodes pour une
exception.
class EmptyError(Exception):
pass
Piles
Piles
Piles
Piles
Piles
Piles
Complexité
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)
Piles
Piles
Application : output
Files
Files
Files
Files
Files
Files
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
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
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()
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
Files
Files
Files
Files
Complexité
Files
Files
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
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
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
Files
Files
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
Files
Files
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.
Listes
Listes
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 !