Adloun

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 fait maxRec(T, n) ? Que se passe-t-il pour ?
  • Dans la fonction suivante, l'invariant de la boucle max du 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 * y par puiss(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 test k == T.length est 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

Exercice suivant et tuteur

Sans compte : le cours et le premier QCM. Exercices et tuteur : inscription gratuite en 30 secondes, sans carte bancaire.

Le cours du chapitre · Tous les exercices du chapitre

Exercice suivant et tuteur →