Le let qui change la complexité
Exercice · niveau 2 · informatique (MP2I/MPI), chapitre 5 — Récursivité
Énoncé
Ces deux exponentiations rendent la même valeur. Combien de multiplications fait chacune pour ?
(* A *) let rec puiss x n =
if n = 0 then 1
else let m = puiss x (n/2) in
if n mod 2 = 0 then m * m else m * m * x
(* B *) let rec puiss x n =
if n = 0 then 1
else if n mod 2 = 0 then puiss x (n/2) * puiss x (n/2)
else puiss x (n/2) * puiss x (n/2) * xCorrigé
Mesuré, en instrumentant les deux fonctions d'un compteur :
n=10 A : 6 multiplications, 5 appels B : 25 multiplications, 31 appels
n=20 A : 7 multiplications, 6 appels B : 51 multiplications, 63 appels
n=40 A : 8 multiplications, 7 appels B : 103 multiplications, 127 appels
Pourquoi. Dans A, le let m = ... calcule une fois et le nomme. La récurrence est , deuxième forme du cours avec : .
Dans B, l'expression puiss x (n/2) apparaît deux fois, donc elle est évaluée deux fois. La récurrence devient , soit : le nombre d'appels double à chaque niveau, il y en a , et . On mesure bien appels pour , contre pour A.
Le piège nommé. Une variable n'est pas un raccourci d'écriture : en écrivant deux fois la même expression, on demande deux fois le même calcul. Tout le gain de l'exponentiation rapide — le gain qui fait passer de à — tient à ce let. Et la version B est même pire que la méthode naïve à multiplications, puisqu'elle en fait pour .
À retenir comme réflexe : dès qu'un appel récursif figure deux fois dans une expression, comptez les nœuds de l'arbre des appels avant de conclure quoi que ce soit sur le coût.
Les autres exercices de ce chapitre Le cours du chapitre
Un blocage sur cet exercice ? Le tuteur d'Adloun guide par questions, sans donner la réponse.