algorithmique (école d'ingénieur) · Chapitre 11 · Exercice de TD
Invariants et récursivité
Programmes justes : logique de Hoare et invariants · niveau 2
Énoncé
Une boucle se justifie par un invariant, une fonction récursive par une récurrence : on suppose que l'appel récursif établit sa postcondition, et l'on montre que l'appel courant établit la sienne. On prend pour les exemples.
- Écrire une fonction récursive
maxRec(T, k)qui retourne pour . Donner sa précondition et sa postcondition, la justifier (base, hérédité) et montrer qu'elle termine. Combien d'appels faitmaxRec(T, n)? Que se passe-t-il pour ? - Dans la fonction suivante, l'invariant de la boucle
maxdu cours est devenu une précondition.
static int maxAcc(int[] T, int m, int k) { // 1 ≤ k ≤ n et m = max T[0:k]
if (k == T.length) return m;
return maxAcc(T, Math.max(m, T[k]), k + 1);
}
Montrer que l'appel récursif est fait sous la précondition et que est un variant. Avec quel appel obtient-on le maximum de ? À quelles étapes de la boucle correspondent cet appel, le test k == T.length et l'appel récursif ?
- Démontrer par récurrence forte sur que la fonction suivante retourne , puis compter ses appels pour , et pour quelconque.
static long puiss(long a, int b) { // b ≥ 0
if (b == 0) return 1;
long y = puiss(a, b / 2);
if (b % 2 == 0) return y * y;
return a * y * y;
}
- On remplace
y * yparpuiss(a, b / 2) * puiss(a, b / 2)(et de même dans le cas impair). La fonction reste-t-elle juste ? Combien d'appels fait-elle pour ? Quelle est sa complexité ?
Corrigé
- Précondition ; postcondition : la valeur retournée est .
static int maxRec(int[] T, int k) { // 1 ≤ k ≤ n
if (k == 1) return T[0]; // max T[0:1] = T[0]
int m = maxRec(T, k - 1); // m = max T[0:k-1]
return Math.max(m, T[k - 1]); // max T[0:k]
}
Base : pour , ne contient que . Hérédité : pour , l'appel maxRec(T, k - 1) est fait sous sa précondition () ; on suppose qu'il retourne , et alors . Terminaison : décroît strictement et reste . Comme pour la boucle, la base ne peut pas être : le maximum d'un tableau vide n'existe pas.
maxRec(T, n) fait appels ( pour l'exemple, qui retourne ), tous en attente les uns des autres : la pile d'appels a une profondeur . Avec la pile par défaut de Java, maxRec passe pour et provoque une StackOverflowError pour ; la boucle, elle, n'a pas de limite.
- Si , alors et : l'appel récursif est fait sous la précondition, c'est la progression de la boucle. Le variant est un entier positif qui diminue de à chaque appel. L'appel
maxAcc(T, T[0], 1)établit la précondition comme l'initialisation , ; le testk == T.lengthest l'arrêt, qui retourne ; l'appel récursif est un tour de boucle. Sur l'exemple, prend les valeurs , , , , , , , , exactement les états de la boucle du cours.
L'appel récursif est la dernière action de la fonction (récursion terminale) : rien ne reste à faire au retour, et la fonction n'est qu'une boucle écrite autrement. Java ne transforme pas ces appels en boucle : la profondeur de pile reste .
- Récurrence forte sur . Base : . Hérédité : pour , , donc par hypothèse . Si est pair, ; si est impair, et . Terminaison : décroît strictement jusqu'à . C'est l'invariant du cours, lu de l'autre côté : la boucle consomme les bits de à partir de la droite, la récursion à partir de la gauche, au retour des appels.
Pour , les appels portent sur : appels, et puiss(3, 13) retourne . Pour , est divisé par jusqu'à , puis vient : appels ( pour , pour ), soit .
- Elle reste juste : la démonstration précédente vaut mot pour mot, chacun des deux appels retournant . Mais chaque appel en fait maintenant deux : et , d'où :
puissDouble(1, 13) : 31 appels
puissDouble(1, 1000) : 2047 appels
puissDouble(1, 1024) : 4095 appels
En général , compris entre et : la complexité est , celle de la boucle naïve. Calculer une fois et le garder dans est tout le gain de l'exponentiation rapide.
100 % gratuit
La suite du chapitre est sur Adloun
- 12 autres exercices corrigés dans ce chapitre
- Les 2 QCM du chapitre, le premier sans compte
- Le tuteur, qui guide pas à pas sans donner la réponse
Sans compte : le cours et le premier QCM. Exercices et tuteur : inscription gratuite en 30 secondes, sans carte bancaire.