Adloun

Probleme – Élever à une puissance, et Fibonacci en

Exercice · niveau 3 (difficile) · informatique (MP2I/MPI), chapitre 15 — Diviser pour régner

Énoncé

Corrigé

1. L'exponentiation rapide.


(* Renvoie x^n. Précondition : n >= 0.
   Complexité : Theta(log n) multiplications. *)
let rec puissance x n =
  if n = 0 then 1
  else if n mod 2 = 0 then let y = puissance x (n / 2) in y * y
  else x * puissance x (n - 1)

Terminaison : variant , qui décroît strictement — soit divisé par deux, soit diminué de ; et deux tours ne peuvent pas être impairs de suite, donc est au moins divisé par deux tous les deux appels : appels.

Correction : par récurrence forte sur . Si est pair, ; s'il est impair, . Le point à ne pas manquer est le let y = ... in y <em> y : écrire puissance x (n/2) </em> puissance x (n/2) ferait deux appels au lieu d'un, et l'on retomberait sur — tout le gain perdu par une lettre.

Multiplications comptées :

méthode naïveexponentiation rapide
10105
1009
15
22

(Le calcul a été fait modulo pour que rien ne déborde ; le comptage ne s'en trouve pas modifié.)

2. La formule matricielle. Posons . Par récurrence sur .

Base : avec , .

Hérédité : si , alors

La récurrence de Fibonacci est exactement ce qui fait fonctionner le produit.

3. Le calcul et la comparaison. On applique l'exponentiation rapide au produit de matrices , qui est associatif : est le coefficient en haut à droite de .


type mat = { a : int; b : int; c : int; d : int }
let produit m n =
  { a = m.a*n.a + m.b*n.c; b = m.a*n.b + m.b*n.d;
    c = m.c*n.a + m.d*n.c; d = m.c*n.b + m.d*n.d }

(* F(n) en Theta(log n) produits de matrices 2x2. Précondition : n >= 0. *)
let rec puiss_mat m n =
  if n = 0 then { a = 1; b = 0; c = 0; d = 1 }
  else if n mod 2 = 0 then let p = puiss_mat m (n / 2) in produit p p
  else produit m (puiss_mat m (n - 1))

let fibonacci n =
  if n = 0 then 0 else (puiss_mat { a = 1; b = 1; c = 1; d = 0 } n).b
récursif naïfitératifmatriciel
10177 appels10 additions5 produits
20 appels206 produits
30 appels308 produits
90hors de portée9010 produits

Les trois méthodes ont été confrontées et donnent les mêmes valeurs partout où les trois sont calculables.

Ce que ce tableau met en scène — et il y a trois régimes, pas deux.

Le récursif naïf est exponentiel : appels, parce qu'il recalcule les mêmes valeurs. C'est le défaut du chapitre chap:dynamique, et la mémoïsation le ramène à .

L'itératif est en additions, et c'est déjà excellent. Diviser pour régner n'a rien à voir avec la correction de la récursion naïve : les deux améliorations sont indépendantes.

Le matriciel est en produits. Il n'y a plus rien à mémoïser — il n'y a plus de sous-problème répété. Le gain vient d'ailleurs : d'avoir remarqué que la récurrence est linéaire, donc représentable par une matrice, donc justiciable de l'exponentiation rapide. Le même procédé accélère toute récurrence linéaire à coefficients constants.

Une nuance sur le « ». tient encore dans un entier de bits ; non. En arithmétique exacte, les nombres manipulés ont chiffres, et chaque produit coûte alors par Karatsuba. La complexité en opérations est logarithmique ; la complexité en temps ne l'est pas. Compter les opérations suppose qu'elles sont à coût constant, et il faut le dire.

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.