Beruflich Dokumente
Kultur Dokumente
Chapitre 7
http://ow.ly/mKyXL
Chapitre 7
Rcursivit et fractales
7.1. La rcursivit
En mathmatiques, en informatique, en biologie, mais aussi dans notre quotidien, nous faisons
souvent face des situations o un problme doit tre rsolu en utilisant une mthode de rsolution
qui est rpte plusieurs fois. Dans l'itration, cette mthode est applique par paliers de faon
squentielle, dans la rcursivit, la mthode s'appelle elle-mme.
La rcursivit est un principe de pense exigeant et est souvent dsign comme trop
compliqu . La rcursivit est cependant si fondamentale qu'il n'est pas possible de l'viter.
M. C. Escher
1898 1972
Didier Mller
7-1
dcembre 2013
Rcursivit et fractales
De son ct, la publicit a aussi utilis la mise en abyme, rendant clbres les fromages La vache
qui rit , le vermouth Dubonnet , et le chocolat Droste .
En anglais, on parle
de Droste effect
pour mise en
abyme .
7.1.2. Biologie
La rcursivit est particulirement prsente en biologie, notamment dans les motifs de vgtaux
et les processus de dveloppement. Les diatomes prsentent en particulier de belles structures
rcursives.
Didier Mller
7-2
dcembre 2013
L'informatique au lyce
Chapitre 7
A(i) est obtenu de A(i-1) en pliant dans sa longueur la feuille de papier. La relation de rcurrence
est donc :
Longueur de A(i)) = Largeur de A(i-1)
Largeur de A(i) = Longueur de A(i-1)
Exercice 7.1
Ce programme calcule xn. Expliquez comment il fonctionne.
Calculez la main puissance(2,9).
Didier Mller
7-3
dcembre 2013
Rcursivit et fractales
Le cas n=1 est appel cas de base. Sans sa prsence, l'algorithme ne peut pas se terminer.
On peut videmment crire une fonction itrative de la factorielle :
def factorielle(n):
resultat = 1
for i in range(1,n+1):
resultat *= i
return resultat
Le calcul de la factorielle est rarement utilis tel quel en pratique. Pour des valeurs de n
suprieures 10, la formule de Stirling donne un rsultat correct 0.8% prs :
n!
2 n e , avec e = 2.718281828459...
Un appel de fonction est une opration plus coteuse en soi que, par exemple, une opration
arithmtique ou un test. C'est pourquoi on prfre souvent la fonction itrative la version rcursive.
Dans le cas de la factorielle, on prendra plutt la version itrative, mais il y a des cas o la fonction
rcursive est clairement prfrable, par exemple pour parcourir des arbres (voir chapitre 8), ou faire
des tris (voir chapitre 9).
Didier Mller
(formule de Pascal)
7-4
dcembre 2013
L'informatique au lyce
Chapitre 7
def C(n,p):
if (n>p) and (p>0):
return C(n-1,p-1)+C(n-1,p)
else:
return 1
Exercice 7.2
Syracuse est ici le
nom d'une ville
universitaire
amricaine (New
York).
{
1.
2.
x1
xn 1
xn
si x n est pair
2
3 x n1 si x n est impair
crivez en Python une fonction itrative donnant la suite de Syracuse commenant par a.
crivez une version rcursive.
La terminaison d'un algorithme rcursif peut tre un problme extrmement difficile. Ainsi,
personne n'a jusqu' prsent t capable de dmontrer que la fonction Syracuse prsente plus haut se
termine pour toute valeur de n.
Exercice 7.3
crivez un programme rcursif permettant d'valuer un nombre crit en chiffres romains.
Rappelons que :
M = 1000
D = 500
C = 100
L = 50
X = 10
V=5
I=1
Didier Mller
7-5
dcembre 2013
Rcursivit et fractales
On voit la forme de l'algorithme gnral :
On constate qu'avec l'algorithme ci-dessus, le nombre MIM (qui pourtant n'existe pas chez les
romains) est valu 1999. Le programme n'effectuera pas un test d'exactitude de la chane passe
en paramtre, il se contentera de l'valuer.
Voici une table des rsultats (ces temps sont approximatifs et dpendent videmment du
processeur, mais ils mettent bien en vidence les problmes de la rcursivit) :
Didier Mller
7-6
dcembre 2013
L'informatique au lyce
Chapitre 7
10
710
310
20
310
310
30
0.5
310
40
58
410
50
7264
410
Pourquoi une telle diffrence ? Pour le comprendre, il faut observer comment se passe le calcul
rcursif. Calculons fib(5) avec la mthode rcursive :
fib(5)
On voit que ce n'est pas efficace : par exemple, fib(3) est appel deux fois, ce qui est une perte de
temps. De plus on imagine bien que l'arbre va devenir trs vite gigantesque, avec un trs grand
nombre d'appels inutiles.
La mise en uvre des algorithmes rcursifs ncessite le plus souvent une pile. C'est la difficult
d'implanter cette pile ou d'viter son emploi qui a fait dire pendant longtemps que les programmes
rcursifs taient moins efficaces que les programmes itratifs, mais la situation a chang. En fait, le
dbat sur le choix entre codage rcursif ou itratif est aussi vieux que l'informatique et les progrs de
la compilation des langages de programmation rduit encore la diffrence d'efficacit. Voici quelques
arguments en faveur de la prsentation rcursive :
La prsentation rcursive permet de prsenter simplement des algorithmes beaucoup plus
astucieux (et donc plus efficaces) et cela a t admirablement montr par Tony Hoare avec
son algorithme de tri rapide (Quicksort).
Les compilateurs d'aujourd'hui sont tellement astucieux que plus le programme leur est
prsent de faon abstraite et sans effets de bord, plus ils peuvent mettre en uvre leurs
optimisations et aboutir des codes objets efficaces.
Didier Mller
7-7
dcembre 2013
Rcursivit et fractales
Des structures de donnes rcursives ont t conues pour leur efficacit. On ne voit pas
comment on pourrait excuter sur elles des algorithmes non rcursifs.
Didier Mller
Pour rsoudre le problme des tours de Hano, il faut raisonner rcursivement. Pour fixer les
ides, prenons par exemple quatre disques. Parmi eux, un est remarquable : le plus grand. Il ne peut
tre pos sur aucun autre, il est donc le plus contraignant dplacer. Une solution idale ne devrait
7-8
dcembre 2013
L'informatique au lyce
Chapitre 7
dplacer ce disque qu'une seule fois. Nommons A, B et C les trois piquets, la pile de disques au
dpart sur A. Idalement, donc, le plus grand disque sera dplac de A C en une seule fois. Comme
il ne peut tre pos sur aucun autre disque car il est le plus grand , cela ne pourra se faire que si
aucun disque n'est prsent sur C. Autrement dit, on ne pourra effectuer ce dplacement particulier
que si le grand disque est seul sur A, et que tous les autres disques sont sur B. Incidemment, nous
venons de rduire le problme : pour pouvoir dplacer nos quatre disques de A vers C, il faut d'abord
dplacer (seulement) trois disques de A vers B, puis dplacer le grand sur C, etnfin dplacer les trois
disques de B vers C.
Le grand disque prsente une autre particularit : il est le seul sur lequel on peut poser n'importe
lequel des autres disques. Donc, dans le cadre d'un dplacement des trois disques suprieurs, il n'a
aucune incidence : tout se passe comme s'il n'tait pas l. Cette remarque nous amne un constat
intressant : on peut traiter le problme du dplacement des trois disques exactement de la mme
manire que nous avons trait celui des quatre. La figure ci-dessous, dans sa partie droite, montre
comment la premire des trois tapes peut elle-mme tre dcompose en trois sous-tapes :
dplacer les deux disques suprieurs, puis le disque infrieur, puis dplacer les deux disques
suprieurs. La troisime tape de la partie gauche pourrait tre dcompose de la mme faon.
Didier Mller
7-9
dcembre 2013
Rcursivit et fractales
En rsum, dplacer n disques de A vers C en passant par B consiste :
1. dplacer (n1) disques de A vers B (en passant par C);
2. dplacer le plus grand disque de A vers C ;
3. dplacer (n1) disques de B vers C (en passant par A).
Les tapes 1 et 3 peuvent elles-mmes se dcomposer selon le mme principe, sauf que les rles
des paquets sont intervertis. Par exemple, dans la premire, on va de A vers B, donc forcment par
1'intermdiaire de C. Voici la marche suivre, donne sur deux niveaux :
1. dplacer (n1) disques de A vers B (en passant par C);
1.1. dplacer (n2) disques de A vers C (en passant par B);
1.2. dplacer un disque de A vers B ;
1.3. dplacer (n2) disques de C vers B (en passant par A).
2. dplacer le plus grand disque de A vers C ;
3. dplacer (n1) disques de B vers C (en passant par A).
3.1. dplacer (n2) disques de B vers A (en passant par C);
3.2. dplacer un disque de B vers C ;
3.3. dplacer (n2) disques de A vers C (en passant par B).
Et ainsi de suite...
Ce qui donne en Python :
def hanoi(n,de,a,par):
if n>0:
hanoi(n-1, de, par, a)
print(de,"-->",a)
hanoi(n-1, par, a, de)
print("""
Tours de Hanoi
Il faut deplacer les disques de la tour A vers la tour C
""")
n = int(input("Nombre de disques : "))
hanoi(n,"A","C","B")
Exercice 7.4
crivez un programme en Python rsolvant les tours de Hano sans utiliser la rcursivit. Vous
trouverez une bauche sur le site web compagnon.
Didier Mller
7-10
dcembre 2013
L'informatique au lyce
Chapitre 7
Le principe du jeu
En choisissant 6 nombres (on peut choisir plusieurs fois le mme) dans l'ensemble {1, 2, 3, 4, 5,
6, 7, 8, 9, 10, 25, 50, 75, 100} et en leur appliquant les quatre oprations lmentaires (addition,
soustraction, multiplication et division), il s'agit d'atteindre le rsultat demand (ceci est possible
dans 94% des cas). Tous les rsultats des calculs intermdiaires doivent tre des nombres entiers
positifs. Chacun des nombres (parmi ceux de dpart et ceux obtenus par un calcul intermdiaire) ne
peut tre utilis qu'une fois au plus. Si le rsultat demand ne peut pas tre atteint, il faut l'approcher
au plus prs.
Exemple : atteindre 952 avec les nombres 25, 50, 75, 100, 3, 6.
http://ow.ly/4tqwT
100 + 3 = 103
103 x 6 = 618
618 x 75 = 46350
46350 / 50 = 927
927 +25 = 952
Algorithme de rsolution
1.
2.
3.
4.
5.
6.
7.
8.
9.
Didier Mller
7-11
dcembre 2013
Rcursivit et fractales
t1[j1] = a
t1[j2] = 0
t1.sort()
t1.reverse()
operations(t1, max-1)
if trouve :
print(t[j1],signe[i],t[j2],'=',a)
break
if trouve :
break
if trouve :
break
signe = '+-*/'
t1 = [0]*6
trouve = False
objectif = 951
nombres = [100, 75, 50, 25, 6, 3]
print("Objectif",objectif)
print("Tirage",nombres)
print("Lire la solution (si elle existe) de bas en haut")
print()
operations(nombres, 6)
7-12
dcembre 2013
L'informatique au lyce
Chapitre 7
def imprimer_labyrinthe():
global case, haut, larg
for i in range(haut):
for j in range(larg):
print(case[i][j],end="")
print()
print()
def thesee(i,j):
global case
if case[i][j] == "S":
imprimer_labyrinthe()
elif case[i][j] == " ":
case[i][j] = "."
thesee(i,j+1)
thesee(i,j-1)
thesee(i+1,j)
thesee(i-1,j)
case[i][j] = " "
# programme principal
i0, j0 = lire_labyrinthe()
imprimer_labyrinthe()
print("Solution(s)")
thesee(i0, j0)
La succession des . depuis la case de dpart est imprime avec la donne lorsque la sortie est
atteinte. Tous les chemins possibles seront imprims. Dans notre exemple, on obtient :
XXXXXXXXXXXXXXXXXXXXXXXXX
X.X X
X
XX
X
X.X X XXXXX XXXXXX XXXX X
X.X X X X X X
XXX.S
X.....X X X XXX XXX XXX.X
XXXXX.X X...XXX X XX XX.X
X
.....X... X ......X
XXX XX XXXXXX.XXXX.XXXX X
X
X
......
X X
XXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXX
X.X X
X ........XX
X
X.X X XXXXX.XXXXXX.XXXX X
X.X X X X X.X .... XXX.S
X.....X X X.XXX.XXX XXX.X
XXXXX.X X...XXX.X XX XX.X
X
.....X ...X ......X
XXX XX XXXXXX.XXXX.XXXX X
X
X
......
X X
XXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXX
X.X X
X
XX
X
X.X X XXXXX XXXXXX XXXX X
X.X X X X X X
XXX.S
X.....X X X XXX XXX XXX.X
XXXXX.X X
XXX X XX XX.X
X
..
X
X ......X
XXX XX.XXXXXX XXXX.XXXX X
X
X.............
X X
XXXXXXXXXXXXXXXXXXXXXXXXX
Didier Mller
7-13
dcembre 2013
Rcursivit et fractales
Exercice 7.5
Essayez de comprendre le fonctionnement de ce programme en l'excutant la main sur ce
petit labyrinthe :
XXXXX
XE X
X X S
X
X
XXXXX
Exercice 7.6
Modifiez le programme Python prsent ci-dessus de telle manire que le fil d'Ariane constitu
des points soit marqu par les lettres g , d , h , b pour indiquer le sens du parcours ( g
pour gauche , etc.)
Voici la partie Rsolution d'un programme Python qui trouve la solution d'un Sudoku par
backtracking. Le backtracking (retour sur trace en franais, mais le terme est peu employ) est une
mthode qui consiste revenir lgrement en arrire sur des dcisions prises afin de sortir d'un
blocage. Le terme est surtout utilis en programmation, o il dsigne une stratgie pour trouver des
solutions des problmes de satisfaction de contraintes.
La grille de 9 cases sur 9 sera implmente par une liste de 81 lments. Chaque lment de la
liste sera un chiffre de 0 9. 0 indique une case vide.
Les cases sont numrotes de gauche droite et de haut en bas :
0
9 10 11 12 13 14 15 16 17
18 19 20 21 22 23 24 25 26
27 28 29 30 31 32 33 34 35
36 37 38 39 40 41 42 43 44
45 46 47 48 49 50 51 52 53
54 55 56 57 58 59 60 61 62
63 64 65 66 67 68 69 70 71
72 73 74 75 76 77 78 79 80
La fonction conflit(i,j) permet de savoir si les cases i et j sont
dans la mme colonne ou
dans la mme ligne ou
dans le mme bloc.
# Solveur de Sudoku par backtracking
# ----- Partie rsolution --------bloc = [0]*9
bloc[0] = {0,1,2,9,10,11,18,19,20}
bloc[1] = {3,4,5,12,13,14,21,22,23}
bloc[2] = {6,7,8,15,16,17,24,25,26}
bloc[3] = {27,28,29,36,37,38,45,46,47}
bloc[4] = {30,31,32,39,40,41,48,49,50}
bloc[5] = {33,34,35,42,43,44,51,52,53}
bloc[6] = {54,55,56,63,64,65,72,73,74}
Didier Mller
7-14
dcembre 2013
L'informatique au lyce
Chapitre 7
bloc[7] = {57,58,59,66,67,68,75,76,77}
bloc[8] = {60,61,62,69,70,71,78,79,80}
def conflit_bloc(i,j):
k=0
while not i in bloc[k]:
k+=1
return j in bloc[k]
def conflit_colonne(i,j):
return (i-j)%9 == 0
def conflit_ligne(i,j):
return i//9 == j//9
def conflit(i,j):
return (conflit_colonne(i,j) or conflit_ligne(i,j) or conflit_bloc(i,j))
On essaie de remplir la grille selon la numrotation des cases. Pour chaque case vide, on essaiera
les chiffres possibles en allant de 1 9. Si un chiffre est possible, on le met (peut-tre
provisoirement) et on passe la case vide suivante. On avance ainsi dans le remplissage de la grille.
Il arrivera forcment un moment o l'on sera bloqu : aucun chiffre de 1 9 ne pourra tre plac. On
revient alors une case en arrire et on essaie un autre chiffre. Si on trouve une possibilit, on pourra
continuer d'avancer, sinon, on revient encore une case en arrire pour un autre essai. Et ainsi de suite.
Il est trs possible que l'on doive ainsi remonter jusqu' la premire case.
Le temps de rsolution est trs variable : il va de quelques secondes plusieurs minutes.
On le voit, cette mthode n'utilise aucun raisonnement. C'est une recherche exhaustive ou
recherche par force brute : une technique extrmement simple, mais trs gnrale, qui consiste
numrer tous les candidats possibles, jusqu' trouver le candidat qui satisfait au problme.
Exercice 7.7*
Encore un casse-tte, moins connu : le jeu du gratte-ciel. Sur une grille 4 x 4, chaque case
contient un immeuble de 1, 2, 3 ou 4 tages (les grilles n x n contiendront des immeubles de 1 n
tages). Comme au Sudoku, les immeubles d'une mme range (ligne ou colonne) ont tous des tailles
diffrentes. Les informations donnes sur les bords indiquent le nombre d'immeubles visibles sur la
range correspondante par un observateur situ cet endroit.
Par exemple, si une ligne contient la disposition 4-2-1-3, un immeuble est visible depuis la
gauche (celui 4 tages), et deux immeubles sont visibles partir de la droite (ceux 3 et 4 tages).
Le but du jeu est bien sr de remplir la grille.
Didier Mller
7-15
dcembre 2013
Rcursivit et fractales
Si l'on ne regarde qu'une des pointes, on a l'impression de voir un chou en entier. C'est le principe
d'auto-similarit. On retrouve de l'auto-similarit dans les fougres ( droite) : chaque feuille
ressemble la fougre entire.
Les fractales sont trs utilises pour gnrer des paysages virtuels. Dans l'image de synthse cidessous, les montagnes sont des fractales.
Didier Mller
7-16
dcembre 2013
L'informatique au lyce
Chapitre 7
Un des objets mathmatiques les plus fascinants et les plus complexe est l'ensemble de
Mandelbrot, qui est devenu l'icne des fractales. Il est dfini comme l'ensemble des points du plan
complexe pour lesquels la suite dfinie par la rcurrence :
z0
=
zn 1 =
0
2
z nc
Si t = 0, 'ordinateur doit dessiner une ligne. C'est la base (ou l'initiateur). La longueur a peu
d'importance. On dfinit la longueur une fois avec s. La commande tortue est : av(s).
Sinon, si t > 0 :
Dragon(t) = Dragon (t1) Dragon (t 1). C'est la rgle de rcursivit (ou le gnrateur).
L'ordinateur doit dessiner une courbe de dragon avec profondeur de rcursion t1. Cela donne :
Dessiner Dragon (t1)
Tourner gauche (90)
Dessiner Dragon (t1)
Didier Mller
7-17
dcembre 2013
Rcursivit et fractales
Il y a un petit problme : on ne peut pas dessiner Dragon(t1) exactement de la mme faon les
deux fois. En effet, le premier Dragon (t1) est dessin vers l'extrieur en partant du milieu de
Dragon(t). Ensuite on tourne de 90. Le deuxime Dragon(t1) est dessin l'inverse du milieu de
Dragon(t) vers l'extrieur. Pour que les deux Dragon(t1) soit reprsente de la mme faon, le
deuxime Dragon(t1) doit tre dessin en miroir. Cela veut dire que tous les angles
(a) sont en miroir et
(b) doivent tre dessins dans l'ordre inverse.
L'astuce consiste donner un signe qui indique le sens (vz = 1 veut dire + , vz = 1 veut dire
). On dessine d'abord un Dragon (t1) avec signe positif (vz = 1). Ensuite on tourne de 90 et
dessinons un Dragon (t1) avec signe ngatif (vz = 1).
Dessiner Dragon (t 1) signe (+)
Tourner gauche (vz90)
Dessiner Dragon (t1) signe ()
# Courbe du dragon avec instructions Logo
from tkinter import *
from math import sin, cos, radians
# ------------------------ commandes logo -------------------------def fpos(x0,y0):
# place la tortue en (x0; y0)
global x,y
x = x0
y = y0
def fcap(angle0):
global angle
# oriente la tortue dans une direction (en degrs)
angle = angle0
def av(d):
# avance en dessinant
global x, y
x2 = x + d*cos(angle)
y2 = y + d*sin(angle)
can.create_line(x, y, x2, y2, width=2, fill="black")
x = x2
y = y2
def tg(a):
# tourne gauche de a degrs
global angle
angle -= radians(a)
# ------------------------------------------------------------------def dragon(t,vz):
if t==0:
av(15)
else:
dragon(t-1,1)
tg(vz*90)
dragon(t-1,-1)
def dessiner():
fpos(300,600)
fcap(0)
dragon(10,1)
Didier Mller
7-18
dcembre 2013
L'informatique au lyce
Chapitre 7
# ------------------------------------------------------------------fen = Tk()
can = Canvas(fen, bg='white', height=800, width=800)
can.pack(side=TOP)
# Boutons
bou1 = Button(fen, text='Quitter', width=10, command=fen.quit)
bou1.pack(side=RIGHT)
dessiner()
fen.mainloop()
fen.destroy()
1.
2.
3.
7-19
dcembre 2013
Rcursivit et fractales
Exercice 7.8
En utilisant les commandes Logo vues avec la courbe du dragon (vous pouvez ajouter la
procdure td(a) qui permettra de tourner la tortue vers la droite), dessinez le flocon de von Koch
avec un niveau de rcursivit de 4.
Vous trouverez une bauche sur le site web compagnon.
Exercice 7.9
Voici une fractale obtenue en modifiant l'exercice 7.8. Saurez-vous la reproduire ?
Sources
[1] ETH-Programme dirig de Programmation rcursive, A. Alder et al., ETHZ, 1995 (traduction
de B. Canvel)
[2] Cours modulaire de programmation, G. Coray, EPFL, 1986
[3] Cours sur la rcursivit , Axel Chambily-Casadesus et Ptrut Constantine,
<http://recursivite.developpez.com>, 2009
[4] Initiation la programmation avec Python et C++, Y. Bailly, Pearson, 2011 (2me dition)
[5] Ensemble de Mandelbrot , Wikipdia,
<http://fr.wikipedia.org/wiki/Ensemble_de_Mandelbrot>
Didier Mller
7-20
dcembre 2013