• Aucun résultat trouvé

Informatique MP Arago

N/A
N/A
Protected

Academic year: 2022

Partager "Informatique MP Arago"

Copied!
102
0
0

Texte intégral

(1)

Informatique MP Arago

Document de compléments de cours et d’exercices avec des corrections

1

Ph. Langlois

version partielle du 23 janvier 2015

1. Excepté le devoir maison, très peu de corrections de questions qui ne demandent pas de programmes

(2)

2

(3)

Table des matières

I Rappels de Python 7

1 Sur les listes python 9

1.1 Définir des listes en compréhension . . . . 9 1.2 Principales méthodes sur les listes . . . . 9

2 Tracer et mesurer des performances 11

2.1 Tracer avec Turtle . . . . 11 2.2 Tracer avec matplotlib . . . . 16 2.3 Mesurer le temps d’exécution d’un algorithme . . . . 20

II Exercices 27

3 Remise en jambe et récursivité 29

3.1 Premières récursions . . . . 29 3.2 C’est vraiment parti ! . . . . 32

4 Les piles 35

4.1 Exercices de base . . . . 35 4.2 Premières applications . . . . 41 4.3 Applications plus conséquentes . . . . 46

5 Trier 61

5.1 Les tris vus en cours . . . . 61 5.2 Autres algorithmes de tri et compléments . . . . 85

6 Couleurs, images et traitements 87

6.1 Principes . . . . 87 6.2 On commence en couleurs ! . . . . 88 6.3 Des images et du traitement . . . . 89

III Devoir maison 91

7 Novembre 2014 93

(4)

4

(5)

Bibliographe utilisée et conseillée

[1] T. Audibert and A. Oussalah.

Informatique. Programmation et calcul scientifique en Python et Scilab.

Ellipses, 2014.

[2] B. Cordeau and L. Pointal. Une introduction à Python. http://perso.limsi.fr/pointal/

_media/python:cours:courspython3.pdf, 2014. version 1.618.

[3] T. Cormen.

Algorithmes. Notions de base. Dunod, 2013.

[4] T. Cormen, C. Leiserson, and R. Rivest.

Introduction à l’algorithmique. Dunod, seconde edition,

2002.

[5] P. Damphousse.

Petite introduction à l’algorithmique. À la découverte des mathématiques du pas à pas.

Ellipses, 2005.

[6] G. Dowek, J.-P. Archambault, E. Baccelli, C. Cimelli, A. Cohen, C. Eisenbeis, T. Viéville, and B. Wack.

Informatique et Sciences du Numérique - Spécialité ISN en Terminale S. Eyrolles, 2012.

[7] E. Le Nagard.

Informatique. Initiation à l’algorithmique en Scilab et Python. Pearson, 2013.

[8] B. Wack, S. Conchon, J. Courant, M. de Falco, G. Dowek, J.-C. Filliâtre, and S. Gonnord.

Informa- tique pour tous en classes préparatoires aux grandes écoles. Eyrolles, 2014.

(6)

6

(7)

Première partie

Rappels de Python

(8)
(9)

Chapitre 1

Sur les listes python

1.1 Définir des listes en compréhension

Principe :

[fonction(x) for x in uneListe if condition-de-fitrage]

# les carrés des éléments liste = [1, 2, 3, 4, 5, 6, 7]

les_carres = [x ** 2 for x in liste]

# Afficher les nombres pairs

print [x for x in liste if x % 2 == 0]

# Plus simple que filter, également :)

# Affiche les carrés pairs (combinaison des deux) print [x ** 2 for x in liste if x ** 2 % 2 == 0]

# ou print [x for x in [a ** 2 for a in liste] if x % 2 == 0]

1.2 Principales méthodes sur les listes

L.append(x) : Ajoute l’élément x à la fin de la liste L

L1.extend(L2) : Étend la liste L1 en y ajoutant tous les éléments de la liste L2 L.insert(i, x) : Insère l’élément x dans la liste L à la position i

L.remove(x) : Supprime de la liste L le premier élément dont la valeur est x.

L.pop([i]) : Enlève de la liste L l’élément situé à la position i

Si aucune position n’est indiqué, L.pop() enlève et retourne le dernier élément de la liste L.index(x) : Retourne la position du premier élément de la liste L ayant la valeur x.

L.sort() : Trie les éléments de la liste, en place.

L.reverse() : Inverse l’ordre des éléments de la liste, en place.

(10)

10

(11)

Chapitre 2

Tracer et mesurer des performances

2.1 Tracer avec Turtle

turtle est un module qui permet de tracer des figures géométriques à l’aide de mouvements élé- mentaires effectués par une . . .tortue. La tortue est représentée par un icone

a

qui fixe sa direction de déplacement. Le tracé est créé par ses déplacements,

— en levant (up()) ou non (down) un crayon,

— déplacements contrôlés par des changements de direction : right(angle) et left(angle) ,

— et des distances vers l’avant : forward(distance) ou l’arrière : backward(distance) .

— goto(x,y) permet de se déplacer au point

(x, y)

sans modifier la direction de la tête.

— reset() initialise la fenêtre graphique et mainloop() permet de la conserver à l’écran une fois le tracé terminé.

D’autres options (couleur et taille du trait, vitesse de déplacement, . . .) se comprennent à l’aide de l’exemple suivant.

import t u r t l e as t

" " " T u r t l e : mouvements e l e m e n t a i r e s " " "

def c a r r e ( c , c o l o r s t r i n g ) :

" " " d e s s i n e un c a r r e de c o t e c e t l e r e m p l i t de c o l o r " " "

t . f i l l c o l o r ( c o l o r s t r i n g ) t . b e g i n _ f i l l ( )

f o r i i n range( 4 ) : t . forward ( c ) t . r i g h t ( 9 0 ) t . e n d _ f i l l ( )

# parametres deplacement un = 30

deux = 2∗un

# i n i t : t a i l l e e c r a n en p i x e l s , c l e a r ecran , r e s e t des v a r i a b l e s t . s c r e e n s i z e ( 1 0 0 , 1 0 0 )

t . c l e a r ( ) t . r e s e t ( )

# l o c a l i s e r ( 0 , 0 ) t . goto ( 0 , 0 )

t . dot ( ) t . up ( )

# un c a r r e t . goto ( 1 0 , 1 0 )

(12)

c a r r e ( deux , " b l a c k ")

# on se d ep lac e sans l a i s s e r de t r a c e t . up ( )

t . goto ( un , un ) t . down ( )

# un c a r r e plus p e t i t c a r r e ( un , " white ")

# e t un a u t r e c a r r e en c o u l e u r l i e au p r e c e d e n t t . goto ( 5∗un , 5∗un )

t . p e n c o l o r (" red ") t . p e n s i z e ( 2 )

t . h i d e t u r t l e ( ) # pour a l l e r plus v i t e c a r r e ( un , " blue ")

# pour que l e t r a c e r e s t e a l e c r a n t . mainloop ( )

Plein d’autres détails sont bien sûr accessibles en ligne à : https://docs.python.org/3/library/

Utiliser turtle pour réaliser les tracés suivants.

Exercice 1.

(P) Le flocon de Von Koch.

Le flocon de Von Koch (1906) est une courbe fractale (continue partout et dérivable nulle part) obte- nue comme la limite de la transformation suivante appliquée récursivement à un segment de départ.

1. le segment est découpée en 3 parties égales,

2. le segment intermédiaire est remplacé par deux segments formant un triangle équilatéral avec le segment supprimé,

3. et ainsi de suite sur les 4 segments ainsi obtenus.

Voici les cinq premières courbes obtenues à partir d’un segment droit (à gauche) ou d’un triangle équilatéral : le flocon de Von Koch (à droite).

1. Un segment.

(a) segment-vk : écrire la suite de commandes turtle qui trace la

n-ième transformation

d’un segment donné.

(b) segment-vk-n : compléter segment-vk pour obtenir le tracé ci-dessus.

2. Le flocon ! On effectue la même transformation sur chacun des cotés d’un triangle équilatéral.

(a) flocon : écrire la suite de commandes turtle qui trace la

n-ième transformation d’un

triangle équilatéral donné.

12

(13)

(b) flocon-n : compléter flocon pour obtenir le tracé ci-dessus.

import t u r t l e as t

" " " Flocon de Von Koch " " "

def segment ( d , n , c o l o r =" b l a c k ") :

" " " r e c u r s i o n sur n pour decouper l e segment de longueur d e t t r a c e r d ( longueur du segment ) p i x e l s s i n==0 " " "

t . p e n c o l o r ( c o l o r ) i f n==0:

t . forward ( d ) e l s e:

dsur3 = d//3

segment ( dsur3 , n−1) t . l e f t ( 6 0 )

segment ( dsur3 , n−1) t . r i g h t ( 1 2 0 )

segment ( dsur3 , n−1) t . l e f t ( 6 0 )

segment ( dsur3 , n−1) def f l o c o n ( d , n , c o l o r =" b l a c k ") :

segment ( d , n , c o l o r ) t . r i g h t ( 6 0 )

segment ( d , n , c o l o r ) t . r i g h t ( 6 0 )

segment ( d , n , c o l o r ) t . r i g h t ( 6 0 )

# i n i t : t a i l l e e c r a n en p i x e l s , c l e a r ecran , r e s e t des v a r i a b l e s t . s c r e e n s i z e ( 1 0 0 , 1 0 0 )

t . c l e a r ( ) t . r e s e t ( )

t . h i d e t u r t l e ( ) # pour a l l e r plus v i t e

# a n g l e s en deg t . degrees ( )

# t a i l l e d = 400

# n b i t e r = in pu t ( " nombre d ’ i t e r a t i o n s = " ) n b i t e r = 3

# e x t r e m i t e de depart a = (−200 , 0 )

t . up ( ) t . goto ( a ) t . down ( )

# t r a c e

f l o c o n ( d , n b i t e r )

# pour que l e t r a c e r e s t e a l e c r a n t . mainloop ( )

import t u r t l e as t

(14)

" " " n f l o c o n s de Von Koch " " "

def segment ( d , n , c o l o r =" b l a c k ") :

" " " r e c u r s i o n sur n pour decouper l e segment de longueur d e t t r a c e r d ( longueur du segment ) p i x e l s s i n==0 " " "

t . p e n c o l o r ( c o l o r ) i f n==0:

t . forward ( d ) e l s e:

dsur3 = d//3

segment ( dsur3 , n−1) t . l e f t ( 6 0 )

segment ( dsur3 , n−1) t . r i g h t ( 1 2 0 )

segment ( dsur3 , n−1) t . l e f t ( 6 0 )

segment ( dsur3 , n−1)

# i n i t : t a i l l e e c r a n en p i x e l s , c l e a r ecran , r e s e t des v a r i a b l e s

# t . s c r e e n s i z e ( 1 0 0 0 , 5 0 0 ) t . c l e a r ( )

t . r e s e t ( )

t . h i d e t u r t l e ( ) # pour a l l e r plus v i t e

# a n g l e s en deg t . degrees ( )

# t a i l l e d = 243

# n b i t e r = in pu t ( " nombre d ’ i t e r a t i o n s = " ) n b i t e r = 4

f o r n i n range( n b i t e r ) :

# e x t r e m i t e s de depart s u c c e s s i v e s a = (−d , d − n∗d//3)

t . up ( ) t . goto ( a ) t . down ( )

# t r a c e

segment ( d , n )

# pour que l e t r a c e r e s t e a l e c r a n t . mainloop ( )

import t u r t l e as t

" " " Flocon de Von Koch " " "

def segment ( d , n , c o l o r =" b l a c k ") :

" " " r e c u r s i o n sur n pour decouper l e segment de longueur d e t t r a c e r d ( longueur du segment ) p i x e l s s i n==0 " " "

t . p e n c o l o r ( c o l o r ) i f n==0:

t . forward ( d ) e l s e:

dsur3 = d//3

segment ( dsur3 , n−1)

14

(15)

t . l e f t ( 6 0 )

segment ( dsur3 , n−1) t . r i g h t ( 1 2 0 )

segment ( dsur3 , n−1) t . l e f t ( 6 0 )

segment ( dsur3 , n−1) def f l o c o n ( d , n , c o l o r =" b l a c k ") :

segment ( d , n , c o l o r ) t . r i g h t ( 1 2 0 )

segment ( d , n , c o l o r ) t . r i g h t ( 1 2 0 )

segment ( d , n , c o l o r ) t . r i g h t ( 1 2 0 )

# i n i t : t a i l l e e c r a n en p i x e l s , c l e a r ecran , r e s e t des v a r i a b l e s t . s c r e e n s i z e ( 1 0 0 , 1 0 0 )

t . c l e a r ( ) t . r e s e t ( )

t . h i d e t u r t l e ( ) # pour a l l e r plus v i t e

# a n g l e s en deg t . degrees ( )

# t a i l l e d = 84

# n b i t e r = in pu t ( " nombre d ’ i t e r a t i o n s = " ) n b i t e r = 2

# e x t r e m i t e de depart a = (−200 , 2 0 0 ) t . up ( )

t . goto ( a ) t . down ( )

# t r a c e

f l o c o n ( d , n b i t e r )

# pour que l e t r a c e r e s t e a l e c r a n t . mainloop ( )

import t u r t l e as t import cmath as cm

" " " n f l o c o n s de Von Koch " " "

def segment ( d , n , c o l o r =" b l a c k ") :

" " " r e c u r s i o n sur n pour decouper l e segment de longueur d e t t r a c e r d ( longueur du segment ) p i x e l s s i n==0 " " "

t . p e n c o l o r ( c o l o r ) i f n==0:

t . forward ( d ) e l s e:

dsur3 = d//3

segment ( dsur3 , n−1) t . l e f t ( 6 0 )

segment ( dsur3 , n−1) t . r i g h t ( 1 2 0 )

(16)

t . l e f t ( 6 0 )

segment ( dsur3 , n−1) def f l o c o n ( d , n , c o l o r =" b l a c k ") :

segment ( d , n , c o l o r ) t . r i g h t ( 1 2 0 )

segment ( d , n , c o l o r ) t . r i g h t ( 1 2 0 )

segment ( d , n , c o l o r ) t . r i g h t ( 1 2 0 )

# i n i t : t a i l l e e c r a n en p i x e l s , c l e a r ecran , r e s e t des v a r i a b l e s

# t . s c r e e n s i z e ( 1 0 0 0 , 5 0 0 ) t . c l e a r ( )

t . r e s e t ( )

t . h i d e t u r t l e ( ) # pour a l l e r plus v i t e

# a n g l e s en deg t . degrees ( )

# t a i l l e d = 81

# n b i t e r = in pu t ( " nombre d ’ i t e r a t i o n s = " ) n b i t e r = 6

f o r n i n range( n b i t e r ) :

# n t r a c e s c o t e a c o t e r e p a r t i s sur 3 l i g n e s l i g n e , c o l = divmod( n , 3 )

# e x t r e m i t e s de depart s u c c e s s i v e s

d e c a l _ x = −250 # pour p r o f i t e r de l a l a r g e u r de l a f e n e t r e a = ( d e c a l _ x + 1 . 2∗d∗c o l , 2∗d − 1 . 2∗d∗l i g n e )

t . up ( ) t . goto ( a ) t . down ( )

# t r a c e f l o c o n ( d , n )

# pour que l e t r a c e r e s t e a l e c r a n t . mainloop ( )

Le module turle est aussi utilisé pour tracer les labyrinthes parfaits et les chemins de sortie de l’exercice 19.

2.2 Tracer avec matplotlib

matplotlib est le module adapté à tous les types de tracés scientifiques : courbes, histogrammes, nuages de points, . . .Ses options sont très nombreuses, de même que la documentation en ligne ou

“livrée avec”. L’utilisation basique consiste à tracer des courbes

x−y

à partir de listes de points, d’un fichier de valeurs ou d’une expression, de choisir les bons axes, les bonnes échelles, d’indiquer les noms des courbes et des axes, de visualiser les tracés à l’écran et de les sauvegarder dans des fichiers (pdf ou png). Il peut être utile de tracer plusieurs “sous-figures” au sein d’une même figure. numpy est le compagnon naturel de matplotlib — pylab est la réunion de deux.

Une première prise en main est très bien décrite par :

http://www.labri.fr/perso/nrougier/teaching/matplotlib/

16

(17)

ou le chapitre 3 du

user guide

(qui comporte plus de 2500 pages).

Exercice 2.

(P) Principales complexités.

Cet exercice permet d’utiliser les commandes de base pour :

— afficher de courbes : plot,figure,title,legend,

— gérer les axes : xticks,xlim,loglog,semilogx ,

— gérer les affichages : show,savefig,close.

1. Obtenir le tracé suivant des principales classes de complexités :

log2n, n, nlog2n, n2, n3,2n

pour les premières puissances de 2 (par exemple de 2 à 128). Des échelles logarithmiques sont adap- tées.

2 4 8 16 32 64 128

1 4 16 64 256 1024 4096

16384 Principales fonctions de complexite 2^n n^3

n^2 nlog2(n) n log2(n)

# c o m p l e x i t e s . py

" " " Trace des p r i n c i p a l e s c l a s s e s de c o m p l e x i t e s avec m a t p l o t l i b " " "

import numpy as np

import m a t p l o t l i b . pyplot as p l t

from m a t p l o t l i b . backends . backend_pdf import PdfPages

# premieres p u i s s a n c e s de 2 n = [ 2∗ ∗i f o r i i n range( 1 , 8 ) ]

# l e s p r i n c i p a l e s f o n c t i o n s de c o m p l e x i t e s

# # a l ’ a i d e de lambda f o n c t i o n s

# # iden = lambda x : x

# # l o g 2 = lambda x : np . l o g 2 ( x )

# # nlog2n = lambda x : x∗l o g 2 ( x )

# #n2= lambda x : np . power ( x , 2 )

# #n3 = lambda x : np . power ( x , 3 )

# # f s = [ log2 , iden , nlog2n , n2 , n3 ]

(18)

# #y = { } # y e s t un d i c t i o n n a i r e

# # f o r f i n f s :

# # y [ s t r ( f ) ] = f ( n )

# t r a c e

p l t . f i g u r e ( f i g s i z e = ( 8 , 6 ) , dpi = 8 0 )

p l t . t i t l e (" P r i n c i p a l e s f o n c t i o n s de c o m p l e x i t e ")

# axes en l o g 2

p l t . l o g l o g ( basex =2 , basey =2)

# v a l e u r s a f f i c h e e s sur l axe des x l a b e l _ x = [s t r( v a l ) f o r v a l i n n ] p l t . x t i c k s ( n , l a b e l _ x )

# v a l e u r s a f f i c h e e s sur l axe des x

y_val = [ np . power ( 2 , j ) f o r j i n range( 0 , 1 5 , 2 ) ] l a b e l _ y = [s t r( v a l ) f o r v a l i n y_val ]

p l t . y t i c k s ( y_val , l a b e l _ y )

# on l i m i t e l e s v a l e u r s de y t r o p grandes p l t . ylim ( 1 . 0 , np . power ( 2 , 1 4 ) )

#

p l t . p l o t ( n , np . power ( 2 . 0 , n ) , l a b e l =" 2^n ") p l t . p l o t ( n , np . power ( n , 3 ) , l a b e l =" n^3 ") p l t . p l o t ( n , np . power ( n , 2 ) , l a b e l =" n^2 ") p l t . p l o t ( n , n∗np . l o g 2 ( n ) , l a b e l =" nlog2 ( n ) ") p l t . p l o t ( n , n , l a b e l =" n ")

p l t . p l o t ( n , np . l o g 2 ( n ) , l a b e l =" l o g 2 ( n ) ")

#

p l t . legend ( l o c =" upper l e f t ")

# s o r t i e e c r a n ou pdf

# p l t . show ( )

pp = PdfPages (’ . / p l t−c o m p l e x i t e s . pdf ’) pp . s a v e f i g ( )

pp . c l o s e ( )

Exercice 3.

(P) Encore le flocon de Von Koch.

Il plus rapide de calculer les coordonnées des sommets des transformés successifs du segment de départ, puis de les tracer comme une courbe. On obtient ainsi le tracé suivant.

1. Écrire la fonction segvk qui à partir des extrémités d’un segment

[a, b], par exemple définies

par ses affixes complexes, calcule et retourne la liste (des affixes) des points significatifs d’une itération de Von Koch de

[a, b].

2. Écrire la fonction listevk qui applique une itération de Von Koch à une liste de segments.

3. Apliquer ces fonctions pour obtenir le tracé matplotlib des premières itérations du segment

[−1,1]. Il est agréable d’obtenir ces tracés sous la forme de sous-figures comme présenté ci-

dessus.

# p l t−segment−vk . py

" " " Flocon de Von Koch avec m a t p l o t l i b " " "

import cmath as cm import numpy as np

import m a t p l o t l i b . pyplot as p l t

from m a t p l o t l i b . backends . backend_pdf import PdfPages def segvk ( za , zb ) :

" " " coupe l e segment [ za , zb ] en 3 ,

r e t o u r n e za e t l e s sommets m1, m2, m3 de VonKoch " " "

d = zb−za # v a l e u r complexe

18

(19)

1.0 0.5 0.0 0.5 1.0

1.0 0.5 0.0 0.5 1.0

1.0 0.5 0.0 0.5 1.0

1.0 0.5 0.0 0.5 1.0

1.0 0.5 0.0 0.5 1.0

Flocon de Von Koch -- version matplotlib

dsur3 = d/3# idem

# w = r o t a t i o n p i sur 3 p i s u r 3 = cm . p i /3

w = complex( np . cos ( p i s u r 3 ) , np . s i n ( p i s u r 3 ) ) zm1 = za + dsur3

zm3 = zm1 + dsur3 zm2 = zm1 + w∗dsur3

r e t u r n [ za , zm1 , zm2 , zm3 ] def l i s t e v k ( L ) :

" " " applique segvk a chaque p a i r e s u c c e s s i v e de segments d e c r i t s dans L r e t o u r n e c e t t e l i s t e Lout " " "

l = l e n( L ) Lout = [ ]

f o r i i n range( l−1) : za = L [ i ]

zb = L [ i +1] # sommet s u i v a n t za Lout . extend ( segvk ( za , zb ) )

Lout . append ( zb ) #ne pas o u b l i e r l e d e r n i e r r e t u r n Lout

# segment de depart a = complex(−1 , 0 ) b = complex( 1 , 0 ) L0 = [ a , b ]

# c a l c u l n i t niveaux de courbes s t o c k e e s dans l i s t e de l i s t e s n i t = 5

LL = [ None ]∗( n i t +1)

(20)

f o r i i n range( 1 , n i t +1) : p r i n t( i )

LL [ i ] = l i s t e v k ( LL [ i−1])

# t r a c e

p l t . f i g u r e ( f i g s i z e = ( 6 , 1 1 ) ) # 1 f i g u r e

p l t . s u p t i t l e ( ’ Flocon de Von Koch −− v e r s i o n m a t p l o t l i b ’) p l t . a x i s ( [−1 , 1 , 0 , 0 . 6 ] )

# p l t . legend ( )

# g e s t i o n des sous−f i g u r e s numfig = 0

f o r i i n range( 1 , n i t +1) :

numfig +=1 # n i t l i g n e s , 1 c o l , i f i g

p l t . s u b p l o t ( n i t +1 , 1 , numfig ) # I n t e g e r s u b p l o t s p e c i f i c a t i o n must be a t h r e e d i g i t number

p l t . y t i c k s ( [ ] ) # r i e n en y

# a f f i c h a g e

p l t . p l o t ( np . r e a l ( LL [ i ] ) , np . imag ( LL [ i ] ) )

# l e s s o r t i e s : commenter ce qui ne va pas

# on t e s t e d ’ abord l a s o r t i e e c r a n

# # p l t . show ( )

# ## e t quand on e s t c o n t e n t : l a s o r t i e pdf pp = PdfPages (’ . / p l t−segvk . pdf ’)

pp . s a v e f i g ( )

# ## pas de s o r t i e j p g dans m a t p l o t l i pp . c l o s e ( )

2.3 Mesurer le temps d’exécution d’un algorithme

Mesurer le temps d’exécution d’un programme sur une machine actuelle est une une tâche beaucoup plus ardue qu’il n’y parait. En effet, l’impression que l’exécution de votre programme mobilise

à elle seule

les ressources de votre ordinateur est une illusion. Le temps que vous attendez pour obtenir votre résultat, même si cela vous parait instantané, est en fait partagé : pendant ce même temps, votre ordinateur effectue certainement de nombreuses autres tâches, par exemple liées au système d’exploitation, aux périphériques, à d’autres utilisateurs si votre machine gère plusieurs sessions, . . .Par ailleurs, les unités de calculs et l’organisation (des niveaux) de la mémoire compliquent très fortement la mesure fiable des temps d’exécution et leur analyse.

En pratique, plus ce que vous voulez mesurer est court, plus la mesure sera incertaine et non repro- ductible. Pour ce qui nous intéresse, l’obtention d’une mesure significative consistera à répéter l’exé- cution dans une boucle, de mesurer le temps d’excution de

cette boucle

et d’en retenir la moyenne. Il n’est pas inutile de répéter ce type de mesures et le cas échéant de traiter l’échantillon obtenu.

Les “gros problèmes” sont aussi sujets à remarque. Des tailles de données importantes nécessitent un volume important de transferts entre la mémoire et les unités de calcul. Ces transferts sont com- plexes et leurs temps est, d’une part non linéaire par rapport à la taille des données, et d’autre part beaucoup plus important que les temps de calcul souvent associés. A titre indicatif, il y a une facteur 10 entre ce temps de transfert et le temps d’un calcul arithmétique élémentaire. Ainsi, les mesures peuvent être surprenantes lorsqu’elles deviennent “dominées” par ces temps de transfert. Le modèle de nos analyses de la complexité des algorithmes ne prend pas en compte cet aspect très difficile.

20

(21)

Pour finir, ceci illustre la différence entre les propriétés d’un algorithme et celles de ses mises en oeuvres et, par la même occasion, confirme l’intérêt des notations asymptotiques

O,θ,Ω

dont on a souvent signalé qu’elle masquent pas mal de détails.

Mesurer avec le moduletime

Ce module fournit beaucoup de fonctions en rapport avec le temps : gestions de calendriers, des systèmes horaires, d’horloges . . .Il permet aussi une estimation des temps d’exécution des programmes python (et autres). La qualité de cette estimation dépend des mises en oeuvre de ce module et des machines visées. Vous pouvez être relativement confiant sur vos PC-linux ou mac.

La fonction time() est annoncée avec une précision de la seconde, sans que ce soit garanti pour toutes les machines. Une seconde est suffisamment long pour effectuer 2 giga = 2

·109

opérations . . .Elle peut donc être utile pour mesurer des simulations ou des exécutions conséquentes.

La fonction perf_counter() est définie depuis Python 3.3. Lorsqu’elle est disponible, elle permet les mesures les plus fines possibles adaptées à la mesure des temps d’exécution de programmes (sans être pour autant exactes, ni reproductibles). Elle utilise des fonctions spécifiques aux architectures des machines qui exploitent les compteurs matériels.

Ces fonctions sont très simples à utiliser. On procède en deux appels qui encadrent la portion de code à mesurer.

t0 = time.perf_counter():

le premier appel initialise une valeur initiale t0 ;

t = time.perf_counter():

le second appel mesure t à l’issue de l’exécution de la portion de code,

et t-t0 est la mesure de ce temps d’exécution (moyennant les réserves précédentes). Ne pas oublier de boucler autour de la portion à analyser et de meurer à l’extérieur de cette boucle.

Exercice 4.

(P) Compteurs de temps . . .

Comparer time et perf_counter pour un algorithme linéaire.

(22)

import time

from random import ∗

import numpy as np

import m a t p l o t l i b . pyplot as p l t

# for mats a f f i c h a g e : f x . format ( v a l )

f i n t = " { : 0 = 5 } " # e n t i e r sur 7 d i g i t s complete avec 0 a gauche

fexp = " { : 7 . 3 e } " # f l o a t en n o t a t i o n e sur 7 d i g i t s dont 3 a p r e s l e . f f l = " { : 5 . 1 f } " # f l o a t sur 5 d i g i t s dont au plus 1 a p r e s l e .

# t i m i n g s o p e r a t i o n s en O( n ) dim = 100

dimmax = dim

# 1 . avec des l i s t e s python

a = [ random ( ) f o r i i n range( dimmax ) ] b = [ random ( ) f o r i i n range( dimmax ) ]

nbrepet = 30

# t i m i n g s o p e r a t i o n s en O( n ) t _ t i m e = [ ]

f o r r e p e t i n range( nbrepet ) : t 0 = time . time ( )

c = 0 . 0

f o r i i n range( dim ) : c = c + a [ i ]∗b [ i ] t = time . time ( )

t _ t i m e . append ( t−t 0 ) t _ t s c = [ ]

f o r r e p e t i n range( nbrepet ) : t 0 = time . p e r f _ c o u n t e r ( ) c = 0 . 0

f o r i i n range( dim ) : c = c + a [ i ]∗b [ i ] t = time . p e r f _ c o u n t e r ( ) t _ t s c . append ( t−t 0 ) f o r r i n range( nbrepet ) :

p r i n t( fexp .format( t _ t i m e [ r ] ) , fexp .format( t _ t s c [ r ] ) )

# ## t r a c e p l t . f i g u r e ( )

p l t . semilogy ( basey =10)

p l t . p l o t ( t_ tim e , ’ ro−’, l a b e l =" time ")

p l t . p l o t ( t _ t s c , ’ b+−’, l a b e l =" p e r f _ c o u n t e r ")

#

p l t . legend ( l o c =’ upper l e f t ’) p l t . show ( )

p l t . c l o s e ( )

Exercice 5.

(P) Performance d’algorithmes de complexités diverses . . .

Citer un algorithme d’algèbre linéaire pour chacune des complexités suivantes : linéaire, quadratique et cubique. Observer leurs temps d’exécution pour des ensembles de dimension correctement choi- sies. Proposer un tracé qui exhibe les complexités attendues.

22

(23)

" " " t i m i n g s de t a b l e a u x sur a l g o s en O( n ^ 1 , 2 , 3 ) avec l i s t e s python

Rmq : l e s t a i l l e s pour n^2 ( 1 0 0 0 ) e t n^3 ( 1 0 0 ) s o n t l e s l i m i t e s de ce macbook .

A v e r i f i e r c a r peu . . . 10^4 x10 ^4 = 10^8 s o i t 1/5 du max c i−a p r e s . The maximum s i z e o f a python l i s t on a 32 b i t system i s 5 3 6 , 8 7 0 , 9 1 2

elements .

(meme l e c a s 10^4 pour n^2 , s e u l , ne passe pas ) " " "

import time

from random import ∗

import numpy as np

import m a t p l o t l i b . pyplot as p l t

# for mats a f f i c h a g e : f x . format ( v a l )

f i n t = " { : 0 = 5 } " # e n t i e r sur 7 d i g i t s complete avec 0 a gauche

fexp = " { : 7 . 3 e } " # f l o a t en n o t a t i o n e sur 7 d i g i t s dont 3 a p r e s l e . f f l = " { : 5 . 1 f } " # f l o a t sur 5 d i g i t s dont au plus 1 a p r e s l e .

dim = [ 1 0∗ ∗i f o r i i n range( 1 , 2 ) ] dimmax = dim [l e n( dim )−1]

# ######################

# t i m i n g s o p e r a t i o n s en O( n )

# deux l i s t e s python

a = [ random ( ) f o r i i n range( dimmax ) ] b = [ random ( ) f o r i i n range( dimmax ) ]

nbrepet = 10

# t i m i n g s o p e r a t i o n s en O( n ) t i m i n g s = [ ]

f o r n i n dim :

t 0 = time . p e r f _ c o u n t e r ( ) f o r r e p e t i n range( nbrepet ) :

# p r o d u i t s c a l a i r e double boucle c = 0 . 0

f o r i i n range( n ) :

(24)

t = time . p e r f _ c o u n t e r ( )

# timing moyen

tmoy = ( t−t 0 ) /nbrepet t i m i n g s . append ( tmoy ) f o r n i n range(l e n( dim ) ) :

p r i n t( f i n t .format( dim [ n ] ) , fexp .format( t i m i n g s [ n ] ) )

# ######################

# t i m i n g s o p e r a t i o n s en O( n^2) dim2 = [ 1 0∗ ∗i f o r i i n range( 1 , 4 ) ]

#dim2 = dim

dimmax2 = dim2 [l e n( dim2 )−1]

# deux l i s t e s python

a = [ random ( ) f o r i i n range( dimmax2 ) ] b = [ random ( ) f o r i i n range( dimmax2 ) ]

# une m a t r i c e

aa = [ None ]∗dimmax2 f o r i i n range( dimmax2 ) :

aa [ i ] = a

# un v e c t e u r r e s u l t a t c = [ 0 . 0 ]∗dimmax2 nbrepet2 = 3

# t i m i n g s o p e r a t i o n s en O( n2 ) t i m i n g s 2 = [ ]

f o r n i n dim2 :

t 0 = time . p e r f _ c o u n t e r ( ) f o r r e p e t i n range( nbrepet ) :

# p r o d u i t v e c t = m a t r i c e ∗ v e c t avec double boucle f o r i i n range( n ) :

f o r j i n range( n ) :

c [ j ] = c [ j ] + aa [ i ] [ j ]∗b [ j ] t = time . p e r f _ c o u n t e r ( )

# timing moyen

tmoy = ( t−t 0 ) /nbrepet2 t i m i n g s 2 . append ( tmoy ) f o r n i n range(l e n( dim2 ) ) :

p r i n t( f i n t .format( dim2 [ n ] ) , fexp .format( t i m i n g s 2 [ n ] ) )

# ######################

# t i m i n g s o p e r a t i o n s en O( n^3) dim3 = [ 1 0∗ ∗i f o r i i n range( 1 , 3 ) ]

#dim3 = dim

dimmax3 = dim3 [l e n( dim3 )−1]

nbrepet3 = 2 t i m i n g s 3 = [ ]

# deux l i s t e s python

a = [ random ( ) f o r i i n range( dimmax3 ) ] b = [ random ( ) f o r i i n range( dimmax3 ) ]

# deux a u t r e s m a t r i c e s bb = [ None ]∗dimmax3 c c = [ None ]∗dimmax3

24

(25)

f o r i i n range( dimmax3 ) : bb [ i ] = b

c c [ i ] = c

# on y va f o r n i n dim3 :

t 0 = time . p e r f _ c o u n t e r ( ) f o r r e p e t i n range( nbrepet ) :

# p r o d u i t v e c t = m a t r i c e ∗ v e c t avec double boucle f o r i i n range( n ) :

f o r j i n range( n ) : f o r k i n range( n ) :

c c [ i ] [ j ] = c c [ i ] [ j ] + aa [ i ] [ j ]∗bb [ j ] [ k ] t = time . p e r f _ c o u n t e r ( )

# timing moyen

tmoy = ( t−t 0 ) /nbrepet3 t i m i n g s 3 . append ( tmoy ) f o r n i n range(l e n( dim3 ) ) :

p r i n t( f i n t .format( dim3 [ n ] ) , fexp .format( t i m i n g s 3 [ n ] ) )

# ## t r a c e p l t . f i g u r e ( )

p l t . l o g l o g ( basex =10 , basey =10)

p l t . p l o t ( dim , timings , ’ bo−’ , l a b e l =" prod s c a l a i r e ") p l t . p l o t ( dim2 , timings2 , ’ r+−’ , l a b e l =" prod mat−v e c t ") p l t . p l o t ( dim3 , timings3 , ’ g+−’ , l a b e l =" prod mat−mat ") p l t . legend ( l o c =’ lower r i g h t ’)

p l t . show ( ) p l t . c l o s e ( )

(26)

26

(27)

Deuxième partie

Exercices

(28)
(29)

Chapitre 3

Remise en jambe et récursivité

3.1 Premières récursions

Exercice 6.

Exponentiation rapide (introduit en cours).

1. (P) Écrire expo_rapide à l’aide de divmod.

2. (P) Ajouter un print pour tracer les appels successifs.

3. Quels sont appels à expo(x,n) évités par expo_rapide(x,n).

Exercice 7.

Fibonacci (introduit en cours).

1. (P) Ajouter un print pour tracer les appels successifs.

Exercice 8.

Écrivons récursif !

1. (P) Écrire une fonction qui calcule

2n

pour

n

entier.

2. (P) Écrire une fonction qui calcule le somme des

n

premiers entiers.

3. Quelle sont les complexités de ces fonctions ?

Exercice 9.

Euclide récursif et itératif . . .surtout.

1. (P) Écrire les versions récursive et itérative de l’algorithme d’Euclide vues en séance de façon à pouvoir comptabiliser (et tracer) les appels ou les itérations.

2. (P) Écrire des versions qui utilisent divmod . 3. Pour la version itérative :

(a) Montrer sa terminaison.

(b) Déterminer et prouver un invariant de boucle.

(c) Prouver sa correction.

(d) (?) Étudier sa complexité.

Exercice 10.

Syracuse . . . c’est loin ?.

L’exemple classique d’une fonction récursive dont on ne sait pas démontrer la terminaison est la suite de Syracuse. En voici une première version . . . économique.

s(0) =s(1) = 1

s(n) =s(n/2)

si

n

est pair,

s(n) =s(3n+ 1)

si

n

est impair.

(30)

(a) (P) Écrire une fonction qui calcule

s(n)

et obtenir les 30 premiers termes de cette suite.

(b) (P) Modifier ce programme pour afficher les valeurs intermédiaires nécessaires au calcul de

s(n). Observer raisonnablement.

(c) (P) Modifier ce programme pour compter le nombre

c(n)

de valeurs intermédiaires calcu- lées dans

s(n).

(d) (P*) Tracer

(n, s(n), c(n))

pour

n∈ {1,30}.Cette question nécessite d’utiliser matplotlib.

(e) (P) Modifier le premier programme pour calculer et retourner la séquence de valeurs in- termédiaires associée à

s(n).

2. Pour les curieux. Comme vous vous en doutiez, on conjecture que pour tout

N > 0,s(N) = 1. La suite un

des valeurs intermédiaires calculées à partir de

s(N)

est la suite de Syracuse associée à

N

. Elle s’écrit :

u0=N

un+1 =un/2

si

un

est pair,

un+1 = 3un+ 1

si

un

est impair.

(a) Tracer les valeurs intermédiaires pour un choix arbitraire de

N

. Observer en particulier les cas

N = 15

et

N = 127

— mais pas qu’eux ! Que se passe-t-il si

un= 1

?

(b) Vu la forme précédente, plusieurs notions sont définies autour de cette suite (source Wiki- pédia).

— Le temps de vol est le plus petit indice

n

tel que

un= 1.

— Le temps de vol en altitude est le plus petit indice

n

tel que

un≤N.

— L’altitude maximale est la valeur maximale

un

de la suite.

Écrire des fonctions qui calculent ces notions pour un

N

arbitraire, puis jouer avec !

def s ( n ) :

" " " s u i t e de Syracuse avec a r r e t s sur 0 e t 1 " " "

i f n==0 or n == 1 : r e t u r n 1

e l i f n%2 == 0 : r e t u r n s ( n//2) e l s e :

r e t u r n s ( 3∗n+1) def s2 ( n ) :

" " " s u i t e de Syracuse avec a r r e t s sur 0 e t 1 " " "

" " " v e r s i o n avec t r a c e v a l e u r s i n t e r m e d i a i r e s " " "

i f n==0 or n == 1 : p r i n t( n )

r e t u r n 1 e l i f n%2 == 0 :

u = n//2

p r i n t(" p a i r " , u ) r e t u r n s2 ( u ) e l s e :

u = 3∗n+1

p r i n t(" impair " , u ) r e t u r n s2 ( u )

def s3 ( n , c ) :

" " " s u i t e de Syracuse avec a r r e t s sur 0 e t 1 " " "

" " " v e r s i o n avec decompte des v a l e u r s i n t e r m e d i a i r e s " " "

i f n==0 or n == 1 : r e t u r n 1 , c +1

30

(31)

e l i f n%2 == 0 :

r e t u r n s3 ( n//2 , c +1) e l s e :

r e t u r n s3 ( 3∗n+1 , c +1)

# #n = i n t ( in pu t ( " s y r a c u s e : nb de termes ? = " ) )

# # f o r i i n range ( 0 , n ) :

# # p r i n t ("−− n = " , i )

# # p r i n t ( " s ( " , i , " ) = " , s ( i ) )

n = i n t(in pu t(" s y r a c u s e : nb de termes ? = ") ) f o r i i n range( 0 , n ) :

p r i n t("−− n = ", i )

p r i n t(" s ( " , i , " ) = " , s2 ( i ) )

# #n = i n t ( in pu t ( " s y r a c u s e ( n ) : n = " ) )

# # p r i n t ("− −")

# # p r i n t ( " s2 ( " , n , " ) = " , s2 ( n ) )

# # r e s , nb_termes = s3 ( n , 0 )

# # p r i n t ( " s3 ( " , n , " ) = " , r e s , " e t nb de termes i n t e r m e d i a i r e s = " , nb_termes )

def s ( a0 ) :

u , i = a0 , 0 while u ! = 1 :

q , r = divmod( u , 2 ) i =+ 1

i f r == 0 : u = q e l s e:

u = 3∗u+1 p r i n t( u , i ) r e t u r n u , i

(32)

n = i n t(in pu t(" s y r a c u s e : nb de termes ? = ") ) f o r i i n range( 0 , n ) :

p r i n t(" s ( " , i , " ) = " , s ( i ) )

3.2 C’est vraiment parti !

Exercice 11.

Décomposition binaire récursive.

La représentation binaire (aussi appelée la valeur binaire) d’un entier positif

n

est la suite de 0 et 1 (bits) notée

(n)2 =bp· · ·b1b0 =Pp

0bi2i

, où

bi∈ {0,1}. La suite de bits de la représentation binaire peut

être stockée comme un tableau (ou une liste) de bits selon un ordre à préciser. Ainsi

(13)2 = [1,0,0,1]

et

(16)2 = [1,0,0,0,0]

pour l’ordre décroissant des puissances de 2.

Bien sûr, la fonction python

bin(n)

calcule cette valeur et la retourne sous la forme d’une chaîne de bits (string en format binaire).

1. Propriétés préliminaires.

(a) (P) Écrire une fonction qui calcule

p

tel que

2p

est la plus grande puissance de 2 inférieure ou égale à

n.

(b) Montrer que cette fonction termine.

(c) Déterminer et prouver un invariant de boucle.

(d) Quelle est la longueur minimale

t

du tableau nécessaire au stockage de la représentation binaire de l’entier positif

n.

(e) Borner

n−2p

pour

2p−1 ≤n <2p

.

(f) Écrire

n

en fonction de

n−2p

. En déduire une relation entre les tableaux qui stockent les valeurs binaire de

n,2p

et

n−2p

.

2. Version récursive.

(a) Décrire les étapes d’un algorithme récursif, d’entrées deux entiers naturels

n

et

t, qui cal-

cule la valeur binaire de

n

et la retourne dans un tableau de longueur

t. Dans un premier

temps, on supposera la longueur

t

suffisante pour stocker des entiers positifs int codés en machine sur 32 bits.

(b) (P) Écrire cet algorithme sous la forme d’une fonction (récursive) python. Vérifier son com- portement sur quelques exemples bien choisis.

(c) Prouver que cet algorithme termine et est correct.

(d) Borner le nombre d’appels récursif de cet algorithme. Expliciter les valeurs de

n

correspon- dantes.

(e) (P) Modifier le programme précédent pour observer ce nombre d’appels.

(f) (P) Modifier le programme initial pour que le tableau

t

soit de longueur arbitraire. Justifier votre choix.

Exercice 12.

Les tours de Hanoï : première visite.

Le problème des

tours de Hanoï

(Édouard Lucas, 1883) consiste à trouver une stratégie pour déplacer une pile de

n

disques (troués en leur milieu) de diamètres tous différents (n

≥1), d’une tour A vers

une tour C en passant par une tour intermédiaire B. Au départ, les disques sont tous empilés sur la tour A par diamètre décroissant, comme indiqué sur la figure ci-dessous, pour

n= 3.

Ensuite, les règles de déplacement sont les suivantes : (R1) un seul disque est déplacé à chaque déplacement,

32

(33)

A B C

(R2) un disque est empilé uniquement sur un disque de plus grand diamètre ou sur une tour vide.

Ce problème est résolu récursivement grâce à l’algorithme Hanoi(n,src,dst,int) qui déplace

n

disques de la tour src vers la tour dst en utilisant la tour intermédiaire int :

— Si

n= 1

: déplacer ce disque de la tour src vers la tour dst,

— Si

n6= 1

:

1) déplacer

(n−1)

disques de la tour src vers la tour int en passant par la tour dst , 2) déplacer 1 disque de la tour src vers la tour dst,

3) et déplacer

(n−1)

disques de la tour int vers la tour dst en passant par la tour src.

L’appel principal sera : Hanoi(n,A,C,B) . 1. Prise en main.

(a) Pour

n = 3, dérouler “à la main” l’algorithme

Hanoi(n,A,C,B) . Expliciter l’arbre des appels récursifs et les déplacements successifs. Vérifier que le problème est ainsi résolu.

(b) En déduire l’arbre des appels pour

n= 4.

(c) Combien de déplacements sont nécessaires à la résolution du problème pour

n = 3. ? Et

pour

n= 4

?

(d) Expliciter des pré-conditions pour que Hanoi s’exécute correctement.

2. Codages et expérimentations.

(a) (P) Coder une première version de l’algorithme récursif qui calcule et affiche la séquence de déplacements qui résolvent le problème des tours de Hanoï (sous la forme A -> C, . . .).

(b) (P) Coder une autre version de Hanoi afin i) de compter les nombres de déplacements de chaque disque, leur nombre total pour un

n

arbitraire (attention en pratique !) et d’afficher ces déplacements.

(c) (P) Proposer et coder une dernière version de l’algorithme initial en remplacant l’étape 2) du cas

n6= 1

par un appel à l’algorithme Hanoi.

3. Correction et terminaison. Soit P

1

(n) la propriété :

P

1

(n) : l’algorithme Hanoi(n,src,des,int) déplace

n

disques de diamètre consécutif de la tour src vers la tour des en respectant les règles de déplacements (R1) et (R2).

(a) Prouver la propriété P

1

(n), pour

n ≥ 1, et conclure que l’algorithme résoud les tours de

Hanoï.

(b) Déduire également que l’agorithme termine.

4. Complexité.

(a) Montrer que pour tout entier

n,Pn

i=02i= 2n+1−1.

(b) On note

c

le coût du déplacement d’un disque d’une tour vers une autre, et

C(n)

le coût de la résolution de Hanoi(n,A,B,C). Donner l’expression du nombre

C(n).

(c) (*) En utilisant les propriétés des opérateurs asymptotiques, montrez les propriétés sui- vantes.

i. Si

f(n) = 2n+c

avec

c∈R,

alors

f = Θ(2n).

ii. Si

f1 = Θ(g1)

et

f2 = Θ(g2)

alors

f1·f2 = Θ(g1·g2).

(d) (*) En déduire la complexité asymptotique de l’algorithme Hanoi.

(34)

34

(35)

Chapitre 4

Les piles

4.1 Exercices de base

Exercice 13.

Piles à capacité bornée ou non (introduit en cours).

1. (a) (P) Pour les piles à capacité bornée, écrire les fonctions creer_pile, empiler, depiler, sommet, est_vide, taille en veillant au bon traitement des cas exceptionnels.

(b) (P) Écrire test_pile qui utilise chacune de ces fonctions pour des arguments admissibles ou non.

2. (a) Écrire les algorithmes de création, modification et observation de piles à capacité non bor- née. Commenter les principales différences avec le cas borné.

(b) (P) Écrire les fonctions de la question 1a pour des piles à capacité non bornée.

(c) (P) Reprendre test_pile et observer le comportement des traitements exceptionnels.

# p i l e b o r n e e . py

" " " p i l e LIFO bornee −− r e a l i s a t i o n par t a b l e a u " " "

def c r e e r _ p i l e ( c ) : p = ( c +1) ∗ [ None ] p [ 0 ] = 0

r e t u r n p

# t a i l l e e s t t r e s v i t e u t i l e ( e t remplace l e s l e c t u r e s de p [ 0 ] ) def t a i l l e ( p ) :

r e t u r n p [ 0 ] def d e p i l e r ( p ) :

a s s e r t t a i l l e ( p ) > 0 , " d e p i l e r p i l e vide "

p [ 0 ] = t a i l l e ( p )−1 def empiler ( p , e ) :

a s s e r t t a i l l e ( p ) < l e n( p ) −1, " empiler p i l e p l e i n e " # = c p [ 0 ] = t a i l l e ( p ) + 1

p [ t a i l l e ( p ) ] = e r e t u r n p

def e s t _ v i d e ( p ) :

r e t u r n t a i l l e ( p ) ==0 def sommet ( p ) :

a s s e r t t a i l l e ( p ) > 0 , " sommet p i l e vide "

(36)

# f o n c t i o n u t i l e mais pas de base def a f f i c h e r ( p ) :

f o r i i n range( 1 , t a i l l e ( p ) ) : p r i n t( p [ i ] , end=" , ")

p r i n t( p [ t a i l l e ( p ) ] ) # sans v i r g u l e f i n a l e

# pi le no n bo rn e e . py

" " " p i l e LIFO non bornee −− r e a l i s a t i o n l i s t e s python ) " " "

def c r e e r _ p i l e ( ) : r e t u r n [ ] def d e p i l e r ( p ) :

a s s e r t l e n( p ) > 0 p . pop ( )

def empiler ( p , e ) : r e t u r n p . append ( e ) def t a i l l e ( p ) :

r e t u r n l e n( p ) def e s t _ v i d e ( p ) :

r e t u r n t a i l l e ( p ) ==0 def sommet ( p ) :

a s s e r t t a i l l e ( p ) > 0 r e t u r n p[−1]

def a f f i c h e r ( p ) :

# a s s e r t not ( e s t _ v i d e ( p ) ) f o r i i n range( t a i l l e ( p ) ) :

p r i n t( p [ i ] , end=’ ’) p r i n t( )

# p i l e t s t . py

" " " U t i l i s a t i o n p i l e bornee ou non " " "

# pour a v o i r l e s s p e c i f s sous l e s yeux :

# # def c r e e r _ p i l e ( c ) :

# # def d e p i l e r ( p ) :

# # def empiler ( p , e ) :

# # def t a i l l e ( p ) :

# # def e s t _ v i d e ( p ) :

# # def sommet ( p ) :

# # def a f f i c h e r ( p ) :

# c h o i x dynamique de l a r e a l i s a t i o n bornee vs non bornee bonnereponse = F a l s e

while not bonnereponse :

rep = i np ut(" Choix p i l e a c a p a c i t e bornee ( b ) ou non ( n ) : ? ") i f rep == ’ b ’:

bonnereponse = True

p r i n t(" p i l e a c a p a c i t e bornee ") from p i l e b o r n e e import ∗

pp = c r e e r _ p i l e ( 5 ) # c a s borne e l i f rep == ’ n ’:

bonnereponse = True

36

(37)

from p il en o nb or ne e import ∗

p r i n t(" p i l e a c a p a c i t e non bornee ") pp = c r e e r _ p i l e ( ) # c a s non borne e l s e:

c o n t i n u e

# on y va

p r i n t(" p i l e vide : ") ; p r i n t( t a i l l e ( pp ) ) ; a f f i c h e r ( pp ) ; empiler ( pp , ’A ’) ; a f f i c h e r ( pp )

empiler ( pp , ’ B ’) ; a f f i c h e r ( pp ) empiler ( pp , ’C ’) ; a f f i c h e r ( pp ) p r i n t(" t a i l l e = ", t a i l l e ( pp ) , " \n ") while not( e s t _ v i d e ( pp ) ) :

p r i n t("On d e p i l e : " , sommet ( pp ) ) d e p i l e r ( pp ) ;

a f f i c h e r ( pp ) ;

p r i n t(" t a i l l e = " , t a i l l e ( pp ) , " e s t _ v i d e : ", e s t _ v i d e ( pp ) )

p r i n t(" L ’ appel s u i v a n t va l e v e r une A s s e r t i o n E r r o r ( d e p i l e r p i l e vide ) ") d e p i l e r ( pp )

Exercice 14.

Manipuler des piles.

1. (a) En utilisant exclusivement les fonctions de base des piles, écrire un algorithme qui du- plique une pile et retourne cette copie.

(b) Évaluer le nombre d’appels aux fonctions de base. Évaluer le coût de l’occupation mémoire selon que le système optimise ou non cette occupation.

(c) (P) Écrire le programme copier correspondant.

2. (a) En utilisant exclusivement les fonctions de base des piles, écrire un algorithme retournant une pile qui contient, en ordre inverse, les éléments d’une pile p donnée, cette dernière n’étant pas modifiée (à l’issue du traitement).

(b) Écrire le programme inverser qui effectue ce traitement.

# manip . py

" " " Manip de p i l e s " " "

from p i l e b o r n e e import ∗ def c o p i e r ( p ) :

" " " q s t o c k e l a c o p i e c h e r c h e e p e s t d e p i l e e e t videe pendant que

r s t o c k e l e s elements de p d e p i l e s , donc en ordre i n v e r s e elements e n s u i t e re−empiles dans p e t q " " "

q = c r e e r _ p i l e ( t a i l l e ( p ) ) r = c r e e r _ p i l e ( t a i l l e ( p ) ) while not e s t _ v i d e ( p ) :

s = sommet ( p ) d e p i l e r ( p ) empiler ( r , s ) while not e s t _ v i d e ( r ) :

s = sommet ( r ) d e p i l e r ( r ) empiler ( p , s ) empiler ( q , s ) r e t u r n q

(38)

def i n v e r s e r ( p ) :

" " " q duplique p : u t i l i s e c o p i e r ( p i l e )

r c o n t i e n t p en ordre i n v e r s e d e p i l e e de q " " "

q = c o p i e r ( p )

r = c r e e r _ p i l e ( t a i l l e ( p ) ) while not e s t _ v i d e ( q ) :

s = sommet ( q ) empiler ( r , s ) d e p i l e r ( q ) r e t u r n r

def on_y_va ( ) :

p = c r e e r _ p i l e ( 5 ) f o r i i n range ( 5 ) :

empiler ( p , 10+ i ) c o p i e = c o p i e r ( p ) inv = i n v e r s e r ( p )

#

p r i n t(" p " , end=" : ") a f f i c h e r ( p )

p r i n t(" c o p i e " , end=" : ") a f f i c h e r ( c o p i e )

#

p r i n t(" p " , end=" : ") a f f i c h e r ( p )

p r i n t(" inv " , end=" : ") a f f i c h e r ( inv )

p r i n t("RAPPEL : a f f e c t e r n ’ e s t pas dupliquer ( n i c r e e r : ) ") p r i n t(" p e t encore_p = p : ")

a f f i c h e r ( p ) encore_p = p

a f f i c h e r ( encore_p ) p r i n t(" on modifie p : ") d e p i l e r ( p )

empiler ( p , 9 9 )

p r i n t(" p " , end=" : ") a f f i c h e r ( p )

p r i n t(" encore_p ne d e v r a i t pas e t r e modifiee . . . " , end=" : ") a f f i c h e r ( encore_p )

p r i n t(" c o p i e p n ’ e s t pas modifiee : ", end=" : ") a f f i c h e r ( c o p i e )

# on_y_va ( )

Exercice 15.

(P) Manipuler des piles (épisode 2).

Toujours en utilisant uniquement les fonctions de base des piles.

1. Écrire le programme item qui lit le

n-ième élément d’une pile. La pile sera laissée inchangée

et le cas où la pile n’est pas de taille suffisante sera traité.

2. Écrire le programme tete_en_bas qui place en fond de pile l’élément-sommet d’une pile non vide, les autres éléments restant dans l’ordre initial.

# manip−p i l e s 2 . py

" " "

Manipuler des p i l e s : episode 2

38

(39)

" " "

from p i l e b o r n e e import ∗ from random import r a n d i n t

# # def c r e e r _ p i l e ( c ) :

# # def d e p i l e r ( p ) :

# # def empiler ( p , e ) :

# # def t a i l l e ( p ) :

# # def e s t _ v i d e ( p ) :

# # def sommet ( p ) :

# # def a f f i c h e r ( p ) : def item ( p , n ) :

" " "

Retourne l e n−ieme element de l a p i l e p sans m o d i f i e r c e t t e d e r n i e r e . Complexite en espace = O( n ) c a r n e c e s s i t e de c o p i e r l a p i l e

" " "

a s s e r t n <= t a i l l e ( p ) , " n > t a i l l e ( p ) "

q = c r e e r _ p i l e ( n ) f o r i i n range( n ) :

v a l = sommet ( p ) empiler ( q , v a l ) d e p i l e r ( p ) f o r i i n range( n ) :

empiler ( p , sommet ( q ) ) d e p i l e r ( q )

r e t u r n v a l def t e t e _ e n _ b a s ( p ) :

" " "

P l a c e en fond de p i l e l ’ element−sommet d ’ une p i l e non vide , l e s a u t r e s elements r e s t a n t dans l ’ ordre i n i t i a l

" " "

a s s e r t not e s t _ v i d e ( p )

s = sommet ( p ) # t e t e e s t conserve

d e p i l e r ( p ) # t e t e i n u t i l e e t maj t a i l l e ( p ) q = c r e e r _ p i l e ( t a i l l e ( p ) )

while not e s t _ v i d e ( p ) : empiler ( q , sommet ( p ) ) d e p i l e r ( p )

# q c o n t i e n t p ( s a u f t e t e ) en ordre i n v e r s e empiler ( p , s ) # t e t e en bas

while not e s t _ v i d e ( q ) : empiler ( p , sommet ( q ) ) d e p i l e r ( q )

# pas de r e t u r n

dim = 5

p = c r e e r _ p i l e ( dim ) f o r e i n range( dim ) :

empiler ( p , e ) p r i n t(" p ", end=" : ") a f f i c h e r ( p )

# item

num = i n t(in pu t(" quel numero d ’ item dans p ? ( 0 pour f i n i r ) ") ) while num > 0 :

(40)

p r i n t(" l e s numeros commencent en t e t e de p i l e : ) ")

num = i n t(in pu t(" quel numero d ’ item dans p ? ( 0 pour f i n i r ) ") )

# t e t e en bas

p r i n t(" p ", end=" : ") a f f i c h e r ( p )

t e t e _ e n _ b a s ( p )

p r i n t(" p t e t e en bas ", end=" : ") a f f i c h e r ( p )

Exercice 16.

Deux piles et un seul tableau.

1. Proposer une réalisation de deux piles dans un seul tableau T de façon à ce qu’on puisse empiler au choix sur l’une d’entre elle tant que le tableau n’est pas plein.

2. (P) Écrire empiler(e,b) qui empile l’élément e sur l’une ou l’autre des piles selon la valeur du booléen b.

# d e u x p i l e s . py

" " "

Deux p i l e s dans un s e u l t a b l e a u T . g e t d : deux p i l e s

. T = [ sommet g , v a l s de g , None , v a l s de d , sommet de d ]

" " "

from p i l e b o r n e e import ∗ from random import r a n d i n t dim = 20

T = [ 0 ]∗( dim+2) def t a i l l e g ( ) :

r e t u r n T [ 0 ] def t a i l l e d ( ) :

r e t u r n T[−1]

def empiler_g ( e ) : T [ 0 ] += 1

T [ t a i l l e g ( ) ] = e def empiler_d ( e ) :

T[−1] += 1

T[−1−t a i l l e d ( ) ] = e def empiler ( e , b ) :

a s s e r t t a i l l e g ( ) + t a i l l e d ( ) < l e n( T )−2 i f b == F a l s e :

empiler_g ( e ) e l s e:

empiler_d ( e ) def i n i t ( ) :

f o r i i n range( 3 ) : empiler_g ( i ) f o r i i n range( 3 ) :

empiler_d ( i ) f o r i i n range( 3 ) :

empiler_g ( i +10) f o r i i n range( 3 ) :

40

(41)

empiler_d ( i +20)

# t e s t i n i t ( )

t = t a i l l e g ( ) + t a i l l e d ( )

nb = [ 0 , 0 ] # compte l e nb de v a l e u r a j o u t e e s a g e t d p r i n t( t , " v a l e u r s s t o c k e e s dans T pour un max de " , dim ) f o r i i n range( dim−t ) :

b = r a n d i n t ( 0 , 1 ) nb [ b ] += 1

empiler (i n t( b ) , b )

p r i n t(" nb a j o u t s de 0 e t 1 : ", nb ) p r i n t( T )

# un de t r o p ! empiler ( 5 , 0 )

4.2 Premières applications

Exercice 17.

Les tours de Hanoï : seconde visite.

On revient sur les tours de Hanoï de l’exercice 12.

1. (P) Reprendre les programmes précédents pour calculer les états successifs des tours A, B, C au fur et à mesure de la résolution en utilisant le module de piles de l’exercice 13 pour représenter et manipuler les tours.

2. (P) Faire de même en comptant les nombres de déplacements de chaque disque, leur nombre total pour un

n

arbitraire et en affichant ces déplacements.

" " " Hanoi : r e s o l u t i o n e t decompte des deplacements de 1 a 9 d i s q u e s " " "

import p i l e b o r n e e as p

# i l e s t t o u j o u r s d e c o n s e i l l e d ’ u t i l i s e r une v a r i a b l e g l o b a l e : ) nbdep = 0

# v o i r hanoi3 pour une s o l u t i o n propre ( a j o u t d ’ un parametres nbdep dans hanoi )

def i n i t _ h a n o i ( nbdisques ) : g = p . c r e e r _ p i l e ( nbdisques ) m = p . c r e e r _ p i l e ( nbdisques ) d = p . c r e e r _ p i l e ( nbdisques )

f o r i i n r e v e r s e d(range( nbdisques ) ) : p . empiler ( g , i )

r e t u r n g , m, d

def hanoi ( n , s r c , dest , interm ) : g l o b a l nbdep

i f n==1:

p . empiler ( dest , p . sommet ( s r c ) ) p . d e p i l e r ( s r c )

# a f f i c h e r _ h a n o i ( )

p r i n t( s r c , " −> ", d e s t ) nbdep += 1

e l s e:

hanoi ( n−1 , s r c , interm , d e s t )

(42)

hanoi ( n−1 , interm , dest , s r c ) def a f f i c h e r _ h a n o i ( ) :

p r i n t("G : " , end=’ ’) p . a f f i c h e r ( g )

p r i n t("M : " , end=’ ’) p . a f f i c h e r (m)

p r i n t("D : " , end=’ ’) p . a f f i c h e r ( d )

nbdisques = 3 r e s = [ ]

f o r n i n range( 1 , nbdisques +1) : nbdep = 0

p r i n t(" Hanoi avec " , n , " d i s q u e s : ") g , m, d = i n i t _ h a n o i ( n )

p r i n t(" depart ") a f f i c h e r _ h a n o i ( ) hanoi ( n , g , d , m) p r i n t(" a r r i v e e ") a f f i c h e r _ h a n o i ( )

r e s = r e s + [ ( n , nbdep ) ] p r i n t("−−−−")

p r i n t(" ( nb disques , nb deplacements ) ") p r i n t( r e s )

" " " Hanoi : d e t a i l des deplacements " " "

import p i l e b o r n e e as p def i n i t _ h a n o i ( nbdisques ) :

g = p . c r e e r _ p i l e ( nbdisques ) m = p . c r e e r _ p i l e ( nbdisques ) d = p . c r e e r _ p i l e ( nbdisques )

f o r i i n r e v e r s e d(range( nbdisques ) ) : p . empiler ( g , 10+ i )

r e t u r n g , m, d

def hanoi_dep ( n , s r c , dest , interm , src_name , dest_name , interm_name ) :

" " " hanoi avec passage des i d e n t i f i a n t s des t o u r s " " "

i f n==1:

p r i n t( p . sommet ( s r c ) , " : sommet ", src_name , " −> ", dest_name ) p . empiler ( dest , p . sommet ( s r c ) )

p . d e p i l e r ( s r c )

# a f f i c h e r _ h a n o i ( ) e l s e:

hanoi_dep ( n−1 , s r c , interm , dest , src_name , interm_name , dest_name ) hanoi_dep ( 1 , s r c , dest , interm , src_name , dest_name , interm_name ) hanoi_dep ( n−1 , interm , dest , s r c , interm_name , dest_name , src_name ) def a f f i c h e r _ h a n o i ( ) :

p r i n t("G : " , end=’ ’) p . a f f i c h e r ( g )

p r i n t("M : " , end=’ ’) p . a f f i c h e r (m)

p r i n t("D : " , end=’ ’) p . a f f i c h e r ( d )

p r i n t("−−−−−−−−−")

42

(43)

nbdisques = 4

g , m, d = i n i t _ h a n o i ( nbdisques ) p r i n t(" depart ")

a f f i c h e r _ h a n o i ( )

hanoi_dep ( nbdisques , g , d , m, "G", "D", "M") p r i n t(" a r r i v e e ")

a f f i c h e r _ h a n o i ( )

" " " Hanoi : decompte des deplacements par disque " " "

import p i l e b o r n e e as p def i n i t _ h a n o i ( nbdisques ) :

g = p . c r e e r _ p i l e ( nbdisques ) m = p . c r e e r _ p i l e ( nbdisques ) d = p . c r e e r _ p i l e ( nbdisques )

f o r i i n r e v e r s e d(range( nbdisques ) ) : p . empiler ( g , i )

r e t u r n g , m, d

def hanoi_dep ( n , s r c , dest , interm , dep ) :

" " " hanoi avec passage d ’ un t a b l e a u dep ( v a l disque , nb dep ) " " "

i f n==1:

dep [ p . sommet ( s r c ) ] += 1

p . empiler ( dest , p . sommet ( s r c ) ) p . d e p i l e r ( s r c )

e l s e:

hanoi_dep ( n−1 , s r c , interm , dest , dep ) hanoi_dep ( 1 , s r c , dest , interm , dep ) hanoi_dep ( n−1 , interm , dest , s r c , dep ) def a f f i c h e r _ h a n o i ( ) :

p r i n t("G : " , end=’ ’) p . a f f i c h e r ( g )

p r i n t("M : " , end=’ ’) p . a f f i c h e r (m)

p r i n t("D : " , end=’ ’) p . a f f i c h e r ( d )

p r i n t("−−−−−−−−−")

nbdisques = 5

deplacements = [ 0 ]∗nbdisques g , m, d = i n i t _ h a n o i ( nbdisques )

p r i n t(" depart ") a f f i c h e r _ h a n o i ( )

hanoi_dep ( nbdisques , g , d , m, deplacements ) p r i n t(" a r r i v e e ")

a f f i c h e r _ h a n o i ( )

p r i n t(" nb deplacements par d i s q u e s : ", deplacements ) Exercice 18.

(P) Mots bien parenthésés.

1. (P) Écrire un programme parentheses qui vérifie la correction d’un mot parenthésé et qui affiche les paires des indices associés (ouvrant/fermant) comme vu en cours.

2. (P) Modifier le programme parentheses pour traiter des mots constitués de parenthèses

“(,)“, de crochets “[, ]“ et d’accolades “{,}”. Le mot “( [ ] { })” est bien parenthésé à la différence

(44)

3. (P) Modifier le programme parentheses pour traiter des mots constitués de parenthèses et d’autres caractères. Le mot “3*(4-5*(6+7)-8)” est bien parenthésé mais pas “3*(4-5)*6)”.

# p a r e n t h e s e . py

" " " v e r i f i e l e bon p a r en t h es a g e d ’ un mot de ( e t ) r e t o u r n e un booleen e t

imprime l e s i n d i c e s ouvrant−fermant c o r r e s p o n d a n t s " " "

from p i l e b o r n e e import ∗ def v e r i f ( s ) :

def p a r e n t h e s e s ( s ) :

p = c r e e r _ p i l e (l e n( s ) ) r e t u r n p

p = p a r e n t h e s e s ( s ) f o r i i n range(l e n( s ) ) :

i f s [ i ] == ’ ( ’: empiler ( p , i ) e l s e:

i f e s t _ v i d e ( p ) : r e t u r n F a l s e e l s e:

j = sommet ( p ) d e p i l e r ( p )

p r i n t( ( j , i ) , end=’ ’) r e t u r n e s t _ v i d e ( p )

des_mots= [’ ’, ’ ( ( ( ) ( ) ) ) ’, ’ ( ) ( ) ’, ’ ( ) ) ( ’, ’ ( ) ) ’, ’ ( ( ( ( ( ) ) ) ) ) ’] f o r s i n des_mots :

p r i n t( s , end=’ : ’ ) , p r i n t( v e r i f ( s ) )

# p a r e n t h e s e 2 . py

" " " v e r i f i e l e bon p a r en t h es a g e d ’ un mot de ( , [ , { e t l e u r s fermants r e t o u r n e un booleen e t

imprime l e s i n d i c e s ouvrant−fermant c o r r e s p o n d a n t s " " "

from p i l e b o r n e e import ∗ def v e r i f ( s ) :

def mot ( s ) :

" " " l a p i l e p c o n t i e n t l e mot " " "

p = c r e e r _ p i l e (l e n( s ) ) r e t u r n p

p = mot ( s )

f o r i i n range(l e n( s ) ) : ch = s [ i ]

i f ch == ’ ( ’ or ch==’ { ’ or ch==’ [ ’:

# ch e s t un ouvrant empiler ( p , ( ch , i ) ) e l s e:

# ch e s t un fermant i f e s t _ v i d e ( p ) :

# pas a s s e z de fermants r e t u r n F a l s e

e l s e:

# ch : fermant lu , i : son i n d i c e

# ch0 : d e r n i e r ouvrant lu , j : son i n d i c e

44

(45)

( ch0 , j ) = sommet ( p ) d e p i l e r ( p )

i f ch==’ ) ’ and ch0==’ ( ’:

p r i n t( ( ch0+ch , j , i ) , end=’ ’) e l i f ch==’ } ’and ch0==’ { ’:

p r i n t( ( ch0+ch , j , i ) , end=’ ’) e l i f ch==’ ] ’ and ch0==’ [ ’:

p r i n t( ( ch0+ch , j , i ) , end=’ ’) e l s e:

# ouvrant−fermant non correspondant r e t u r n F a l s e

r e t u r n e s t _ v i d e ( p )

des_mots= [ ’ ( ) [ ] { } ’, ’ ( ) [ ( { } ) ] { ( ( ) ) } ’, ’ ( ) ] ’] f o r s i n des_mots :

p r i n t( s , end=’ : ’ ) , p r i n t( v e r i f ( s ) )

# p a r e n t h e s e 3 . py

" " " v e r i f i e l e bon p a r en t h es a g e d ’ un mot de ( , [ , { , ( e t l e u r s fermants ) e t de c a r a c t e r e s quelconques

r e t o u r n e un booleen e t

imprime l e s i n d i c e s ouvrant−fermant c o r r e s p o n d a n t s " " "

from p i l e b o r n e e import ∗ def v e r i f ( s ) :

def mot ( s ) :

" " " l a p i l e p c o n t i e n t l e mot " " "

p = c r e e r _ p i l e (l e n( s ) ) r e t u r n p

p = mot ( s )

f o r i i n range(l e n( s ) ) : ch = s [ i ]

i f ch == ’ ( ’ or ch==’ { ’ or ch==’ [ ’:

# ch e s t un ouvrant empiler ( p , ( ch , i ) )

e l i f ch == ’ ) ’ or ch==’ } ’ or ch==’ ] ’: i f e s t _ v i d e ( p ) :

# pas a s s e z de fermants r e t u r n F a l s e

# ch : fermant lu , i : son i n d i c e

# ch0 : d e r n i e r ouvrant lu , j : son i n d i c e ( ch0 , j ) = sommet ( p )

d e p i l e r ( p )

i f ch==’ ) ’ and ch0==’ ( ’:

p r i n t( ( ch0+ch , j , i ) , end=’ ’) e l i f ch==’ } ’and ch0==’ { ’:

p r i n t( ( ch0+ch , j , i ) , end=’ ’) e l i f ch==’ ] ’ and ch0==’ [ ’:

p r i n t( ( ch0+ch , j , i ) , end=’ ’) e l s e:

# ouvrant−fermant non correspondant

r e t u r n F a l s e e l s e:

# ch e s t un c a r a c t e r e quelconque c o n t i n u e

r e t u r n e s t _ v i d e ( p )

Références

Documents relatifs

[r]

peinture au coton- tige : décorer ours avec des ronds collage : décorer ours en collant du fil (poils de l’ours) colorier l’ours avec des craies grasses La voix et

peinture au coton- tige : décorer ours avec des boucles collage : décorer ours en collant du fil (poils de l’ours) colorier l’ours avec des craies grasses La voix

Pour effectuer une tâche , il est parfois nécessaire d’exécu- ter plusieurs fois de suite les mêmes instructions mais le nombre de fois n’est pas déterminé à l’avance :

Introduction des variables : traduire l’algorithme donné en langage naturel à l’exercice 1p11 en langage pseudo algorithmique.. I Exercices

[r]

[r]

En Python, pour interrompre un programme qui boucle à l'infini, il suffit de faire Ctrl + C.3. Remarque :