Récursivité - Maths en PCSI2 à Berthollet

publicité
Récursivité
Lycée Berthollet, MP/MP* 2016-17
I
Structures récursives
Définition 1 Une structure est dite récursive lorsque la description de cette structure fait référence à la structure elle-même.
Exemple 2 En calcul des propositions (logique), on dit qu’une expression est une proposition
si :
— soit c’est une variable logique (A, B...) ;
— soit elle est du type non(P), où P est une proposition ;
— soit elle est du type (P et Q), (P ou Q) ou (P =⇒ Q), où P est Q sont deux propositions.
Exemple 3 Les fractals sont des objets mathématiques “autosimilaires” qui peuvent être décrit
de manière récursive. Voir un exemple dans la suite.
Exemple 4 La notion de “mise en abyme” en littérature ou au cinéma est une sorte de récursi-
vité. Voir par exemple la publicité de la “vache-qui-rit”.
Enfin, ce qui nous préoccupe ici est la notion d’algorithme récursif :
Définition 5 Dans un langage informatique, une fonction est dite récursive, si elle s’appelle
elle-même soit directement, soit par le biais d’une autre fonction.
Beaucoup de langages modernes permettent de gérer les fonctions récursives. C’est le cas
du langage Python. Cette gestion nécessite de gérer les paramètres, variables locales et points
de retour de chaque appel de la fonction dans une ou plusieurs piles, comme nous le verrons
dans un exemple simple. Voici un exemple de fonction récursive en pseudo-code,
qui calcule le
√
n-ième terme de la suite (un )n∈N définie par u0 = 0 et la relation un+1 = 1 + un :
fonction u(n):
si n==0, retourner 0
sinon, retourner sqrt(1+u(n-1))
1
II
Fonctions récursives en Python
Voir le fichier recusivite.py en annexe
1
Factorielle
On programme dans un premier temps la factorielle de manière récursive et on observe la
pile des paramètres ainsi que la pile des appels. On remarque que le nombre d’appels imbriqués
est limité en Python (par défaut à 1000). On programme aussi cette factorielle de manière non
récursive et on remarque que l’ordre de complexité est inchangé (linéaire dans les deux cas),
mais que la fonction non récursive est environ 2 fois plus rapide.
2
Puissances
On programme le calcul des puissances entières positives d’un nombre de manière récursive
(linéaire en l’exposant n), puis de manière récursive et rapide (logarithmique en l’exposant n).
3
PGCD
L’algorithme d’Euclide se prête très bien à la récursivité avec une complexité dont on pourrait montrer qu’elle est au pire logarithmique.
4
Fibonacci
Le calcul naïf de la suite de Fibonacci de manière récursive est d’une complexité catastrophique, alors qu’un algorithme itératif est très facile à écrire et efficace (linéaire). On montre
comment faire un calcul récursif linéaire, mais cela reste sensiblement plus lent que le calcul
itératif précédent.
On représentera au tableau l’arbre des appels pour avoir une idée de la complexité de l’algorithme naïf.
5
Permutations de séquences
On montre comment créer la liste de toutes les permutations d’une séquence donnée (liste
ou chaîne de caractères) de manière récursive (ce qui est beaucoup plus délicat de manière non
récursive). On mettra la complexité en lien avec l’arbre des appels.
6
Flocon de neige
Il est possible de dessiner assez facilement certaines courbes fractales à l’aide de la récursivité et du module graphique turtle. On donne l’exemple archi-classique du flocon de neige
de Van Koch.
2
III
Correction et terminaison
Sur certains des exemples précédents, on voit que la récurrence (normale ou forte) est l’instrument adapté aux preuves de correction dans le cas récursif, tandis que la terminaison repose
sur la stricte décroissance de la taille du ou des paramètres des appels successifs.
IV
Conclusion
Au vu des exemples ci-dessus, on tire quelques enseignements.
Quand utiliser la récursivité ?
— Lorsqu’on ne sait pas faire autrement ;
— Lorsqu’on travaille sur des structures elle-même récursives (listes, arbres, fractales,...) ;
— Lorsque l’algorithme récursif est très élégant, et ne change pas l’ordre de grandeur de
la complexité par rapport à un algorithme itératif. Attention cependant à la limite du
nombre d’appels imbriqués !
Quand ne pas utiliser la récursivité ?
— Lorsqu’il existe un algorithme non-récursif raisonnablement simple et élégant faisant la
même chose ;
— Lorsque l’arbre des appels est potentiellement très profond ;
— S’il y a des paramètres ou des variables locales “volumineux” dans la fonction.
3
V
Annexe : code
# -----------------------------------------------------------------------# Mesure du temps d’execution d’une fonction
# -----------------------------------------------------------------------import time
def teste(appel):
print(appel+’ ---> ’,end=’’)
tInit = time.clock()
res = eval(appel)
tFin = time.clock()
print(res,tFin-tInit)
# -----------------------------------------------------------------------# Factorielle
# -----------------------------------------------------------------------def facto(n):
if n==0:
return 1
else:
return n*facto(n-1)
print(facto(10))
# produit une erreur:
# print(facto(1000))
# Visualisation de la pile des variables locales et des appels
from inspect import *
def factoVisu(n):
print(locals())
if n==0:
pile = stack()
for i in range(len(pile)):
print(getframeinfo(pile[i][0]))
return 1
else:
res = n*factoVisu(n-1)
print(locals())
return res
print(factoVisu(10))
4
# Comparaison avec la version non recursive
def factoIter(n):
res = 1
for i in range(2,n+1):
res *= i
return res
teste(’factoIter(900)’)
teste(’facto(900)’)
print(factoIter(10000))
# -----------------------------------------------------------------------# Puissance
# -----------------------------------------------------------------------def puiss(x,n):
""" Calcule x**n de facon recursive """
if n==0:
return 1
else:
return x*puiss(x,n-1)
print(puiss(2,32))
def puissRapide(x,n):
""" Calcule rapidement x**n de facon recursive """
if n==0:
return 1
else:
y =puissRapide(x,n//2)
if (n%2==0):
return y*y
else:
return x*y*y
teste(’puissRapide(3,900)’)
teste(’puiss(3,900)’)
5
# -----------------------------------------------------------------------# PGCD
# -----------------------------------------------------------------------def pgcd(a,b):
if b==0:
return a
else:
return pgcd(b,a%b)
print(pgcd(10,6))
print(pgcd(1235674567543467567,908765345432342453243234254254233212356453456))
# -----------------------------------------------------------------------# Fibonacci
# -----------------------------------------------------------------------def fibo(n):
if n<2:
return n
else:
return fibo(n-1)+fibo(n-2)
for i in range(10):
print(fibo(i),end=’ ’)
print(fibo(30))
print(fibo(35))
def fiboIter(n):
if n<2:
return n
else:
Fnm1,Fn = 0,1
for i in range(n-1):
Fnm1,Fn = Fn,Fnm1+Fn
return Fn
for i in range(10):
print(fiboIter(i),end=’ ’)
print(fiboIter(35))
print(fiboIter(100000)
def fiboRecBis(n):
""" Retourne F(n-1) et Fn """
if n==0:
return 1,0
6
elif n==1:
return 0,1
else:
Fnm2,Fnm1 = fiboRecBis(n-1)
return Fnm1,Fnm2+Fnm1
print(fiboRecBis(35)[1])
# Comparaison recursif/iteratif
teste(’fiboIter(900)’)
teste(’fiboRecBis(900)[1]’)
# -----------------------------------------------------------------------# Permutations
# -----------------------------------------------------------------------def permChaine(ch):
if len(ch)==1:
return [ch]
else:
res = []
for i,car in enumerate(ch):
res += [car+s for s in permChaine(ch[:i]+ch[i+1:])]
return res
print(permChaine(’abc’))
print(permChaine(’abricot’))
def permListe(liste):
if len(liste)==1:
return [liste]
else:
res = []
for i,item in enumerate(liste):
res += [[item]+s for s in permListe(liste[:i]+liste[i+1:])]
return res
print(permListe(list(’abc’)))
7
# -----------------------------------------------------------------------# Flocon
# -----------------------------------------------------------------------from math import *
from turtle import *
def fractTurtle(n):
if n==0:
forward(1)
else:
fractTurtle(n-1)
right(60)
fractTurtle(n-1)
left(120)
fractTurtle(n-1)
right(60)
fractTurtle(n-1)
def floconTurtle(n):
clearscreen()
speed(’fastest’)
#
hideturtle()
getpen()
setheading(240)
fractTurtle(n)
left(120)
fractTurtle(n)
left(120)
fractTurtle(n)
floconTurtle(5)
exitonclick()
# -----------------------------------------------------------------------# Arbres et tris seront vus plus tard
# ------------------------------------------------------------------------
8
Téléchargement