Chapitre 1 - joffrempsi1

publicité
Chapitre 1 : introduction à la récursivité
Table des matières
1 Révisions et compléments sur les fonctions
1.1 Les notions et le vocabulaire à maı̂triser . . . . . . . . . . . . . . . . . . . . . . . . . .
1.2 Une fonction peut appeler une autre fonction et même renvoyer une autre fonction !
1
1
2
2 Nouveauté : une fonction peut s’appeler elle-même
2.1 Définition et parallélisme avec la récurrence . . . . . . . . . . . . . . . . . . . . . . . . .
2.2 Comment ça marche une fonction récursive ? . . . . . . . . . . . . . . . . . . . . . . . .
2.3 Points importants à retenir pour une fonction récursive . . . . . . . . . . . . . . . . . .
2
2
3
4
3 Versions récursives d’algorithmes vus en première année
3.1 L’algorithme d’Euclide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2 Algorithme de Horner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2.1 Méthode ≪ naturelle ≫ (mais qui demande déjà une bonne éducation) d’évaluation
d’un polynôme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2.2 Méthode plus rusée : l’idée de la méthode de Horner (cf. 1ère année)
3.2.3 Algorithme itératif pour Horner . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2.4 Ecriture récursive de Horner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.3 Exponentiation rapide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.3.1 L’algorithme ≪ naif ≫ pour le calcul des puissances . . . . . . . . . . . . . . . .
3.3.2 Rappels sur l’idée de l’exponentiation rapide et implémentation récursive . .
3.3.3 Comparaison avec les algorithmes itératifs vus en première année . . . . . . .
3.4 Une morale provisoire : le récursif c’est plus facile ? . . . . . . . . . . . . . . . . . . . .
5
5
5
5
6
6
6
7
7
7
7
8
4 Encore plus de récursion : récursion multiple
8
4.1 Eviter de multiplier les appels récursifs inutiles . . . . . . . . . . . . . . . . . . . . . . . 8
4.2 Des fonctions qui sont vraiment doublement récursives . . . . . . . . . . . . . . . . . . 9
4.3 Des suites récurrentes croisées l’une avec l’autre : récursivité croisée . . . . . . . . . . 10
5 Les trois questions qu’on doit se poser sur un algorithme : le cas des fonctions
récursives
10
5.1 Rappel de première année : . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
5.2 Le cas des fonctions récursives sur l’exemple de l’exp. rapide . . . . . . . . . . . . . . . 10
1
1.1
Révisions et compléments sur les fonctions
Les notions et le vocabulaire à maı̂triser
Définition : Une fonction informatique f est un objet qui prend des arguments (ou dit
aussi paramètres) et qui renvoie des valeurs de retours (ou pas). Une fonction peut modifier
son ou ses arguments si les arguments en question sont des objets modifiables a (des listes
par exemple).
a. on dit aussi mutables
● Définition d’une fonction avec le mot-clef def : en Python, on sait que la syntaxe de
définition d’une fonction est, par exemple pour la fonction mathématique f ∶ x ↦ x2 :
def f(x) : # x est ici l’unique argument de f
return x*x
1
Le fait de calculer f(2) s’appelle un appel de la fonction f. Dans le langage informatique, on
dit aussi qu’on passe 2 comme argument à la fonction f.
En python, il suffit de taper f(2).
● Définition d’une fonction avec le mot-clef lambda : la même fonction peut être déclarée
en une ligne comme suit :
f=lambda x : x*x
Pour les optionnaires d’info. comparer à la syntaxe Caml ... 1
On peut encore appeler f(2). Mais on va voir ci-après qu’un intérêt de la déclaration avec
lambda est éventuellement de ne pas donner de nom à la fonction.
● Un exemple de fonction qui ne retourne rien, mais modifie son argument :
def ajoute0(L):
"""prend une liste en argument et la modifie en rajoutant 0"""
L.append(0)
Question Qu’obtient-on pour M si on fait :
L=[1,2]
M=ajoute0(L)
1.2
Une fonction peut appeler une autre fonction et même renvoyer une
autre fonction !
a) Pour le premier point, vous le savez bien. Si vous avez créé une fonction qui résout un
problème, vous pouvez vous en servir à l’intérieur d’une autre. C’est même une recommandation
utile pour vos devoirs écrits : utilisez les fonctions que vous avez déjà définies !
b) Le deuxième point est peut-être plus surprenant pour l’instant, voyons un exemple mettant
en valeur l’usage de fonctions lambda :
def f_additionneur(n):
""" renvoie la fonction x-> x+n"""
return lambda x: x+n
f=f_additionneur(2)
g=f_additionneur(3)
2
Nouveauté : une fonction peut s’appeler elle-même
2.1
Définition et parallélisme avec la récurrence
Définition Une fonction récursive est une fonction f telle que le code de définition de f
contienne un (ou plusieurs) appel(s) à f. Ces appels sont appelés appels récursifs.
Comme on va le voir dans les exemples ci-dessous, cette méthode de définition de fonction est
très proche de la notion de définition par récurrence en maths.
⎧
⎪
⎪0! = 1,
Exemple 1 : En maths, on peut définir la factorielle d’un entier n ∈ N par ⎨
⎪
∀ n ∈ N∗ , n! = n × (n − 1)!
⎪
⎩
Une déf. récursive de la factorielle en Python, calquée sur la déf. mathématique précédente,
est la suivante :
def fact(n):
if n==0 :
return 1
else :
return n*fact(n-1)
1. let f x : x*x ;;
2
On constate bien ici que le cas de base existe, 0 ! = 1, que la fonction modifie son état par la
relation (n − 1) et s’appelle elle-même, (n − 1) !.
Implémenté sous forme récursive, la factorielle nécessite l’utilisation de la commande
Remarque : On utilise la même syntaxe de déf. de fonctions : il n’y pas de déclaration particulière
return qui doit renvoyer à l’étage précédent la valeur calculée.
ée par les boucles
d’appels récursifs
pour les fonctions récursives (important pour les habitués à Caml).
e la profondeur de
⎧
⎪
Console python
⎪u0 = 0,
Console python
√
Exemple 2, exercice : (i) Si (un ) est définie par réc. par ⎨
, définir une
⎪
2 + un
∀ n ∈ N,1un+1>>>
= fact(5)
⎪
1
>>> def fact(n):
⎩
fonction
récursive
u if
en Python
tout
...
n==0: qui renvoie la valeur u(n) = un pour
2
120n ∈ N.
2
...
return
1 passe-t-il si on veut calculer u1000 ?
(ii) 3Expérimentation
: Que se
4
...
...
else:
return n*fact(n-1)
(iii) Calculer u1000 à l’aide d’un algorithme itératif (boucle).
5
2.2 Comment
ça marche
une fonction
récursive
? ci-après. Il y a diminution proLe déroulement
des opérations
nécessaires
est présenté
er plus de 1000 fois.
gressive de la valeur à calculer, lors de la « descente », jusqu’à arriver au cas de base, qui
it(). Ainsi, l’appel Un schéma intuitif
déclenche alors la « remontée » des valeurs du contexte pour donner la valeur finale.
u message d’erreur
On peut schématiser les opérations faites pour le calcul de fact(3) comme suit :
fiche un compppel.
_________
F IGURE 1 – Factorielle récursive
_________
_________
_________
_________
2
La gestionExercice
informatique
de ce schéma
Implémenter une version itérative de la fonction factorielle.
Lorsqu’on appelle fact(3), dans l’espace mémoire local de la fonction, on définit n=3, le else
s’exécute et donc voudrait exécuter le return 3*fact(2).
Réponse
2 est différée car pour exécuter ce return, on doit d’abord appeler fact(2).
Mais cette
exécution
Se crée alors un nouveau sous-espace mémoire dédié à l’appel de cette fonction fact avec une
_______________________________________________________
variable locale n=2 dans laquelle va s’exécute fact.
_ _ _ la
_ _phase
_ _ _ _de
_ _descente
_ _ _ _ _ _du_ _schéma
_ _ _ _ _précédent,
_ _ _ _ _ _ on
_ _ crée
_ _ _ donc
_ _ _ _quatre
_ _ _ _espaces
_ _ _ _ _de
_ _va___
Pendant_ _toute
doit toujours possériables locales
_ _ _qu’on
_ _ _ _nommera
_ _ _ _ _ _ici
_ _espace
_ _ _ _3,2,1,0,
_ _ _ _ _avec
_ _ _des
_ _ _variables
_ _ _ _ _ _locales
_ _ _ _correspondantes
_ _ _ _ _ _ _ _ _ _ qu’on
_____
indicera par le numéro de l’espace correspondant n3 = 3, . . . , n0 = 0.
_______________________________________________________
on fonctionnement
Dans l’espace 0, on peut calculer fact(0).
_ _ _valeur,
_ _ _ _ _on
_ _peut
_ _ _ enfin
_ _ _ _exécuter
_ _ _ _ _ _le_return
_ _ _ _ _ 1*
_ _ _fact(0)
_ _ _ _ _dans
_ _ _ _l’espace
_ _ _ _ _1_et
_ _ainsi
_ _ _ de
___
Avec cette
suite.
;
Comment rendre ce déroulement explicite à l’aide d’un script ?
2 Différents types de récursivité
def fact(n):
if Iln==0
: surtout de citer les différentes situations de récursivité qui existent. Les définitions
s’agit
return 1
parlent d’elles-mêmes.
else :
print(’--’*n+’> appel de fact’,n-1)
resultat=n*fact(n-1)
# on a séparé le calcul du return pour permettre l’affichage
#de la ligne suivante
2/7
print(’--’*n+’> retour de fact’,n-1)
return resultat
Qui donne pour fact(3) :
3
------> appel de fact 2
----> appel de fact 1
--> appel de fact 0
--> retour de fact 0
----> retour de fact 1
------> retour de fact 2
Remarque : Commentez la place des deux instructions, print par rapport aux deux autres lignes
(appel et retour).
2.3
Points importants à retenir pour une fonction récursive
Ce que dit le lien avec les récurrences mathématiques
D’une façon générale, deux propriétés sont à respecter pour s’assurer du bon fonctionnement
d’une fonction récursive :
— elle doit contenir un cas de base ;
— elle doit s’appeler elle-même, en modifiant son état, pour pouvoir se ramener au
cas de base.
La modification de l’état est une modification d’au moins un des arguments de la fonction.
Dans l’exemple de la fonction fact, on a :
— Un cas de base fact(0)=1 ;
— un appel à fact(n-1) qui va de proche en proche nous ramener à fact(0).
Ce que dit la gestion informatique de la mémoire
Une fonction récursive peut rapidement devenir très gourmande en mémoire !
Exemple : Le calcul de u1000 plus haut donne le message d’erreur :
RuntimeError: maximum recursion depth exceeded
Le module sys de Python permet de régler le nombre maximum d’appels récursifs autorisé.
Par défaut ce nombre est de 1000 comme en témoignent les instructions :
import sys
sys.getrecursionlimit()
Une fonction récursive risque de ne pas s’arrêter
Que se passe-t-il si on appelle fact(-1) ?
En fait elle s’arrête grâce à ....
Mais on est dans un cas d’erreur du programme. On peut utiliser la commande assert qui
permettra de détecter tout de suite l’erreur.
def fact(n):
assert n >=0
if n==0 :
return 1
etc
Alors fact(-1) provoquera une AssertionError. On peut même rajouter un message pour
préciser l’erreur 2
2. Avec la syntaxe : assert n>=0, ’Attention n doit ^
etre positif’.
4
3
Versions récursives d’algorithmes vus en première année
C’est aussi l’occasion de réviser des algo. qu’il faut savoir mettre en oeuvre !
3.1
L’algorithme d’Euclide
En première année, on a étudié l’algo. d’Euclide pour trouver le pgcd de deux entiers naturels
a et b. Une version récursive de cet algorithme :
def Euclide2(a,b):
if b==0 :
return a
else :
r=a%b
return Euclide2(b,r)
Question : A l’aide ou bien de vos souvenirs de première année, ou bien de l’algo. ci-dessus,
redonner une version itérative (boucle while) pour ce même algo.
Commentaires : Qu’est ce qui permet de transformer ici l’algorithme récursif en algorithme
itératif (ou réciproquement) ?
3.2
3.2.1
Algorithme de Horner
Méthode ≪ naturelle ≫ (mais qui demande déjà une bonne éducation) d’évaluation
d’un polynôme
Pour évaluer P (x) = an xn + ⋯ + a1 x + a0 , une première méthode raisonnable serait la suivante :
(on suppose que polynôme P est donné par P=[a0,..,an] la liste des coefficients)
def evaluation(P,x):
resultat=0
puissancex=1
for i in range(len(P)):
resultat=resultat+P[i]*puissancex
puissancex=puissancex*x
return resultat
Question : Calculer le nombre d’opérations nécessaires pour l’évaluation d’un polynôme de degré
n ? Comparer à la méthode très naı̈ve où on ferait plutôt :
for i in range(len(P)):
resultat=resultat+P[i]*x**i
Le fait d’utiliser la boucle simultanément pour le calcul des xi fait partie des bonnes pratiques indispensables !
5
3.2.2
Méthode plus rusée : l’idée de la méthode de Horner (cf. 1ère année)
P (x)
=
an xn + ⋯ + a1 x +a0 ,
´¹¹ ¹ ¹ ¹ ¹ ¹ ¹ ¹ ¹ ¹ ¹ ¹ ¹ ¹ ¹ ¹ ¹ ¹ ¹ ¹ ¸¹ ¹ ¹ ¹ ¹ ¹ ¹ ¹ ¹ ¹ ¹ ¹ ¹ ¹ ¹ ¹ ¹ ¹ ¹ ¹ ¶
on met x en facteur
⎛
⎞
n−1
⎟ x + a0 (†)
= ⎜
a
x
+
⋯
+
a
x
+
a
+a
n
2
1
1
⎜
⎟
´¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¸
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¹
¶
⎝ on met x en facteur
⎠
= ⋅ ⋅ ⋅ = (⋯(((an x + an−1 )x + an−2 )x + an−3 )x + ⋯ + a1 )x + a0
3.2.3
(‡)
Algorithme itératif pour Horner
def HornerI(x,P):
"""le polyn^
ome P est rentré comme une liste ou un tuple
[a_n,...a_0] resp. (a_n,...,a_0)"""
eval=0
for coefficient in P:# parcourt des coeff. de P dans le bon ordre !
eval=eval*x+coefficient
return eval
Cet algorithme itératif, se comprend dans l’écriture (‡) en partant de la parenthèse la plus
profonde :
● A l’étape 0 de la boucle for, la variable eval prend la valeur a_n .
● Ensuite à chaque étape, l’égalité eval=eval*x+coefficient va permettre d’aller d’une expression entre parenthèse à celle complétée d’un cran à droite.
Remarque : Avec l’écriture itérative ci-dessus, on a commencé par le calcul des termes les plus
profondément enfouis dans les parenthèses de (†).
Question : Comparer le nombre d’opérations par rapport à l’algorithme du 3.2.1.
3.2.4
Ecriture récursive de Horner
Pour l’écriture récursive, on va en sens inverse : on n’a pas besoin de penser à (‡) mais seulement à (†).
Pour mettre en oeuvre cette écriture, comme on a stocké P comme une liste, on va utiliser la
méthode pop sur les listes.
Rappel : si P est une liste et i est un nombre entre 0 et len(P)-1, alors P.pop(i) renvoie l’entrée
P[i] et transforme P en lui enlevant l’entrée numéro i. Mieux par défaut P.pop() fait sortir la
dernière entrée de la liste P.
Exercice : Avec tout ce qui précède écrire une fonction HornerR(x,P) qui effectue l’algorithme
de Horner de manière récursive.
Attention : Comme pop modifie l’argument, si on veut que la fonction agisse sans modifier P, on
commencera par...
6
3.3
3.3.1
Exponentiation rapide
L’algorithme
≪
naif
≫
pour le calcul des puissances
≪ Normalement ≫, prendre un nombre x à la puissance N , c’est faire N − 1 multiplications.
Question : Ecrire une fonction récursive puissance(x,N) qui permet de calculer xN à partir
de la définition mathématique, par récurrence, de xN .
3.3.2
Rappels sur l’idée de l’exponentiation rapide et implémentation récursive
En première année, on a étudié et implémenté un algorithme d’exponentiation rapide qui permet
de réduire ce nombre de multiplications à au plus 2n où n + 1 est le nombre de chiffres de l’écriture
de N en base 2. Ici :
Ici l’écriture récursive est plus facile que l’écriture itérative !
L’idée essentielle de l’exponentiation rapide : elle tient en deux égalités :
x2k = (xk )2 ,
x
2k+1
= x.(xk )2 .
Ainsi pour calculer xN si N est pair, on le voit comme (xN /2 )2 et si N est impair, on le voit
comme x.(x(N −1)/2 )2 .
Exercice : Déduire de ce qui précède l’écriture d’une fonction récursive exp_rapide(x,N).
Indication – On pourra utiliser avec profit N//2.
3.3.3
Comparaison avec les algorithmes itératifs vus en première année
L’écriture récursive précédente a permis d’implémenter directement l’idée de :
Diviser pour régner, en anglais divide and conquer.
On divise ici à chaque étape la taille des données par deux.
En première année, la mise en oeuvre itérative de l’idée précédente a demandé comparativement
plus d’effort !
Rappel de l’idée essentielle : Si N = a0 + a1 2 ⋅ ⋅ ⋅ + an 2n , écriture en base deux, avec ai ∈ {0, 1}
alors :
n
xN = xa0 (x2 )a1 . . . (x2 )an .
Ensuite deux points de vue possibles, suivant qu’on connait déjà l’écriture en base 2 de N ou
pas.
(M1) Des points forts vers les poids faibles : Cette méthode suppose qu’on connaı̂t déjà le
développement de N en base deux.
Elle repose alors sur l’égalité (analogue à celle vue pour Horner) :
xN = ((. . . (xan )2 xan−1 )2 . . . )xa1 )2 xa0
7
Par exemple avec 24 qui s’écrit 11000 en base 2 : x24 = (((x)2 .x)2 )2 )2 ).
On lit donc l’écriture en base 2 de N de gauche à droite (on dit qu’on commence par les bits
de poids forts de N ), on part de res= 1 et à chaque étape s’il y a un 1 dans le développement de
x en base 2 on remplace res par (res*res)*x, sinon on prend juste le carré i.e. on remplace res
par (res*res).
Exercice : On rappelle qu’en Python, la commande bin(N) renvoie une chaı̂ne de caractères
qui commence par ’0b suivie de l’écriture de N en base deux.
Par exemple si N = 25, bin(N) renvoie ’0b11001’.
A l’aide de cette commande bin, implémenter en Python l’idée d’algorithme itératif précédente.
(M2) des poids faibles vers les poids forts Un peu plus délicat à programmer, mais permet
de calculer simultanément l’écriture en base 2 et l’exponentiation, cf. T.P. ou 1ère année.
3.4
Une morale provisoire : le récursif c’est plus facile ?
Les algorithmes récursifs peuvent permettent de traduire très agréablement des relations de
récurrence, dont la mise en place en itératif peut demander davantage de soin.
On va voir néanmoins que ce ≪ confort ≫ est relatif, et que, par exemple, quand on va vouloir
évaluer le coût (nombre d’opérations) de ces algorithmes, il faudra bien maı̂triser toutes les étapes
du déroulement.
4
Encore plus de récursion : récursion multiple
Définition : on parle de récursivité multiple si la définition d’une fonction contient plusieurs
appel à elle-même.
4.1
Eviter de multiplier les appels récursifs inutiles
Supposons qu’on veut calculer, pour a > 0 quelconque, les termes de la suite (un ) définie par
a
1
) qui est bien connue (depuis les Babyloniens) pour converger vers ....
{u0 = 1, un+1 = (un +
2
un
Une première programmation est :
def u(n,a):
if n==0:
return 1
else :
return .5*(u(n-1,a)+a/u(n-1,a))
Cette fonction contient deux appels récursifs, mais pour la même valeur u(n-1,a). Une bonne
pratique est alors de se ramener à un seul appel de u(n-1,a). Comment faire ?
Code amélioré
def ubis(n,a):
if n==0:
return 1
else :
Bonne pratique générale (même en non récursif) éviter de faire calculer deux fois la même chose
8
Quelle différence entre u et ubis : un premier calcul de complexité d’algo. récursif :
Avec un seul appel récursif, i.e. pour ubis :
Notons C ′ (n) le nombre d’opérations arithmétiques effectuées pour le calcul de ubis(n,a).
Alors C ′ (0) = 0 puisque la fonction retourne ubis(0,a)=1 sans opérations.
Ensuite, si n > 0, pour calculer ubis(n,a) on commence par calculer ubis(n-1,a) ce qui par
définition nécessite C ′ (n − 1) opérations, puis on fait 1 quotient, 1 addition, 1 multiplication, soit
3 opérations, donc :
C ′ (n) = C ′ (n − 1) + 3
On en déduit immédiatement que C ′ (n) = 3n .
Avec deux appels récursifs, i.e. pour u :
On note C(n) le nombre d’opérations arithmétiques effectuées pour le calcul de u(n,a).
On a encore C(0) = 0, mais si n > 0 pour calculer u(n,a) on effectue deux fois le calcul de
u(n-1,a) ce qui nécessite 2C(n − 1) opérations, puis on fait les 3 opérations comme ci-dessus, et
donc cette fois :
C(n) = 2C(n − 1) + 3
On en déduit que C(n) = 3(2n − 1).
La différence d’efficacité entre les deux fonctions est donc énorme !
Remarque : Ce calcul de complexité ne regarde pas les aspects d’occupation mémoire qui seront
aussi importants pour les fonctions récursives, voir plus loin.
4.2
Des fonctions qui sont vraiment doublement récursives
Exemple des suites récurrentes doubles
⎧
⎪
⎪F0 = 0, F1 = 1,
L’exemple le plus basique est celui de la suite de Fibonacci (Fn ) définie par ⎨
⎪
∀n ∈ N≥2 , Fn = Fn−1 + Fn−2 .
⎪
⎩
Exercice : Ecrire une fonction récursive Fibo(n) qui renvoie Fn pour ≪ tout ≫ entier n.
Question : Pourquoi ai-je mis des guillemets à
≪
tout ≫ ?
Analyse de cette fonction du point de vue de la complexité :
Une particularité liée à la récursivité : pour compter le nombre d’appels récursifs, incrémenter
une variable globale lors de chaque appel de la fonction.
On rajoute au code de déf. de la fonction les lignes
global c
c=c+1
On appelle la fonction récursive après avoir initialisé c=0. Pour Fibo(6), on obtient 25 appels
de la fonction !
Question 1 : Pourquoi ? Pour comprendre pourquoi, on peut faire un arbre des appels (cf.
notes manuscrites).
Question 2 : Pourquoi a-t-on besoin d’une variable globale pour compter les appels récursifs.
Autrement dit pourquoi est-ce que c=c+1 tout seul ne suffirait pas ?
9
Le nombre d’appels devient vite énorme et pour n > 35, le calcul de Fibo(n) devient (très) long puis impossible.
Ici l’algorithme récursif est donc inopérant par rapport à l’algorithme itératif.
En exercices : on verra des méthodes pour corriger ce défaut d’un trop grand nombre d’appels.....
4.3
Des suites récurrentes croisées l’une avec l’autre : récursivité croisée
Un exercice classique de première année est l’étude des suites (un ) et (vn ) définies par u0 = 2,
⎧
⎪
⎪un+1 = un 2+vn ,
v0 = 3 et ∀ n ∈ N, ⎨
√
⎪
⎪vn+1 = un vn .
⎩
Il est possible d’écrire deux fonctions Python qui reflètent exactement cette dépendance :
def u(n):
if n==0:
return 3
else:
return .5*(u(n-1)+v(n-1))
def v(n):
if n==0:
return 2
else:
return sqrt(u(n-1)*v(n-1))
5
Les trois questions qu’on doit se poser sur un algorithme :
le cas des fonctions récursives
5.1
Rappel de première année :
Quelles sont les trois questions à se poser sur un algorithme ?
5.2
Le cas des fonctions récursives sur l’exemple de l’exp. rapide
a) Pour un appel principal de exp_rapide(x,N), les appels récursifs qui suivront seront des
exp_rapide{x,n1}, exp_rapide{x,n2},...,exp_rapide{x,nk}, avec N>n1>n2>..>nk des entiers positifs. Une famille d’entiers naturels strictement décroissante est forcément finie, donc l’algorithme s’arrête (plus d’appel récursif = terminaison !).
Comme pour les algo. itératifs, la terminaison se montre donc souvent avec un marqueur qui décroit.
b) Comment montrer que pour tout ≪ nombre ≫ 3 , x, exp_rapide(x,N) calcule vraiment xN ?
Pour les algo. récursifs, la correction se démontre par récurrence
c) Pour la complexité d’un algorithme récursif, on peut prendre plusieurs mesures.
● Le nombre d’appels récursifs. Celui-ci permet aussi d’évaluer l’occupation mémoire car chaque
appel définit ses variables locales.
● Le nombre total d’opérations comme fait au § 4.1.
Nous reviendrons sur les problèmes de complexité au chap. suivant.
3. on met nombre entre guillemets car cet algorithme s’applique par exemple aussi pour les puissances de matrices
10
Téléchargement