Analyse des algorithmes : Analyse des fonctions récursives

14 Mar 2026 14 Mar 2026 227 vues ESSADDOUKI Mostafa 8 min de lecture
Complexité algorithmique
1 Introduction à l'analyse des algorithmes 2 Notations asymptotiques 3 Opérations élémentaires et modèles de coût 4 Complexité temporelle et spatiale 5 Méthode de comptage des pas 6 Méthode de comptage des pas pour les boucles
Diviser pour régner & algorithmes de tri
7 Rappel sur l'approche récursive 8 Diviser pour régner 9 la recherche dichotomique 10 Tri par fusion 11 Tri rapide 12 Analyse des fonctions récursives
Les arbres binaires
13 Introduction aux arbres binaires 14 Définitions récursives des arbres 15 Propriétés des arbres binaires 16 Types d'arbre binaire 17 Parcours en profondeur des arbres binaires 18 Parcours en largeur des arbres binaires 19 Exercices corrigés sur les arbres - TD 1 20 Exercices corrigés sur les arbres - TD 2 21 Exercices corrigés sur les arbres - TD 3 22 DS - Arbres binaires de recherche équilibrés (AVL) 23 DS - Codage de Huffman - Compression de données
Algorithmes gloutons
24 Introduction aux algorithmes gloutons 25 Problème de la sélection d'activités 26 Problème de séquencement des tâches 27 Problème du Sac à Dos fraction
Programmation dynamique
28 Introduction à la programmation dynamique 29 Le concept de sous-structure optimale 30 Le concept de sous-problèmes superposés 31 Méthodes de la programmation dynamique 32 Différence entre la programmation dynamique, l'approche diviser pour régner, et les algorithmes gloutons 33 Calculer les nombres de catalan en C++ et Python 34 Calculer le coefficient binomial 35 Le nombre de façons pour construire un mur de dimension 4*N 36 Défi de conversion de mots 37 Décomposition de phrases à partir d'un dictionnaire 38 La collection de pièces dans un labyrinthe 39 Nombre de façons de regrouper les étudiants 40 Compter tous les chemins possibles dans une grille MxN
Méta heuristique
41 Algorithmes heuristiques et métaheuristiques 42 Algorithme de Recuit simulé 43 Algorithme Colonies de fourmis
Théorie des graphes
44 Introduction et notions fondamentales 45 Chemins, cycles et connexité 46 Représentations des graphes 47 Parcours de graphes 48 Algorithme de Dijkstra 49 Algorithme A* 50 Algorithme de Bellman-Ford 51 Algorithme Floyd-Warshall 52 Coloration des graphes
Base de données et SQL
53 Introduction au langage SQL 54 Concepts de base de SGBDR 55 Syntaxes de différentes instructions SQL 56 Création et suppression d'une base de données 57 Opérateurs SQL 58 Les contraintes en SQL 59 Création et suppression des tables en SQL 60 Insertion et modifications des enregistrements 61 Extraction des données - SELECT 62 Filtrer les données - WHERE 63 Recherche de motifs - LIKE 64 Trier les données - ORDER BY 65 Les jointures en SQL - JOIN 66 Fonctions d'agrégation en SQL - SUM, COUNT, AVG, MIN et MAX 67 Organiser des données identiques en groupes - GROUP BY et HAVING 68 Les sous-requêtes en SQL 69 Les fonctions SQL de manipulation de date 70 DS - langage SQL 71 Exercices corrigés de langage SQL
Introduction à l'intelligence artificielle
Introduction à la théorie des jeux
Préparation aux concours
72 Réseau de distribution d'eau 73 Arbre d'expression arithmétique 74 Exploration d'une grotte souterraine

Analyse des fonctions récursives

Introduction

L'analyse des algorithmes récursifs diffère de celle des algorithmes itératifs. Au lieu de compter le nombre d'itérations de boucles, on établit une relation de récurrence qui exprime le temps d'exécution en fonction de la taille de l'entrée.

Principe général

Pour analyser une fonction récursive, on suit les étapes suivantes :

  1. Identifier le ou les cas de base (condition d'arrêt).
  2. Établir la relation de récurrence pour le cas général.
  3. Résoudre la récurrence pour obtenir la complexité asymptotique.

Relations de récurrence simples

  Exemple 1 : Factorielle

int factorielle(int n) {
    if (n <= 1)
        return 1;          // Cas de base
    else
        return n * factorielle(n - 1);  // Appel récursif
}
def factorielle(n):
    if n <= 1:
        return 1          # Cas de base
    else:
        return n * factorielle(n - 1)  # Appel récursif

Analyse :

  • Cas de base : \(T(1) = O(1)\)
  • Cas général : \(T(n) = T(n-1) + O(1)\)

En développant :

\[ \begin{align*} T(n) &= T(n-1) + 1 \\ &= T(n-2) + 2 \\ &= \ldots \\ &= T(1) + (n-1) \\ &= 1 + (n-1) = n \end{align*} \]

Conclusion

Complexité : \(O(n)\)

  Exemple 2 : Puissance

int puissance(int x, int n) {
    if (n == 0)
        return 1;          // Cas de base
    else
        return x * puissance(x, n - 1);  // Appel récursif
}
def puissance(x, n):
    if n == 0:
        return 1          # Cas de base
    else:
        return x * puissance(x, n - 1)  # Appel récursif

Analyse : Même structure que la factorielle → \(O(n)\)

Récurrences linéaires avec plusieurs appels

  Exemple 3 : Fibonacci (récursif naïf)

int fibonacci(int n) {
    if (n <= 1)
        return n;          // Cas de base
    else
        return fibonacci(n - 1) + fibonacci(n - 2);  // Deux appels récursifs
}
def fibonacci(n):
    if n <= 1:
        return n          # Cas de base
    else:
        return fibonacci(n - 1) + fibonacci(n - 2)  # Deux appels récursifs

Relation de récurrence :

\[ T(n) = T(n-1) + T(n-2) + O(1) \]

Cette récurrence est plus difficile à résoudre. On peut montrer que :

\[ T(n) = O(2^n) \quad \text{(exponentiel)} \]

Attention

La version récursive naïve de Fibonacci est extrêmement inefficace (\(O(2^n)\)) car elle recalcule plusieurs fois les mêmes valeurs. Une version itérative ou avec mémoïsation est en \(O(n)\).

Récurrences de type "diviser pour régner"

  Exemple 4 : Recherche dichotomique

int recherche_dichotomique(int tab[], int debut, int fin, int x) {
    if (debut > fin)
        return -1;  // Cas de base : non trouvé
    
    int milieu = (debut + fin) / 2;
    
    if (tab[milieu] == x)
        return milieu;  // Cas de base : trouvé
    else if (tab[milieu] < x)
        return recherche_dichotomique(tab, milieu + 1, fin, x);  // Appel sur moitié droite
    else
        return recherche_dichotomique(tab, debut, milieu - 1, x);  // Appel sur moitié gauche
}
def recherche_dichotomique(tab, debut, fin, x):
    if debut > fin:
        return -1  # Cas de base : non trouvé
    
    milieu = (debut + fin) // 2
    
    if tab[milieu] == x:
        return milieu  # Cas de base : trouvé
    elif tab[milieu] < x:
        return recherche_dichotomique(tab, milieu + 1, fin, x)  # Appel sur moitié droite
    else:
        return recherche_dichotomique(tab, debut, milieu - 1, x)  # Appel sur moitié gauche

Relation de récurrence :

\[ T(n) = T\left(\frac{n}{2}\right) + O(1) \]

Résolution :

\[ \begin{align*} T(n) &= T\left(\frac{n}{2}\right) + 1 \\ &= T\left(\frac{n}{4}\right) + 2 \\ &= \ldots \\ &= T(1) + \log_2 n \end{align*} \]

Conclusion

Complexité : \(O(\log n)\)

  Exemple 5 : Tri fusion (Merge Sort)

void tri_fusion(int tab[], int debut, int fin) {
    if (debut < fin) {
        int milieu = (debut + fin) / 2;
        tri_fusion(tab, debut, milieu);      // Tri de la moitié gauche
        tri_fusion(tab, milieu + 1, fin);    // Tri de la moitié droite
        fusionner(tab, debut, milieu, fin);  // Fusion en O(n)
    }
}
def tri_fusion(tab):
    if len(tab) <= 1:
        return tab  # Cas de base
    
    milieu = len(tab) // 2
    gauche = tri_fusion(tab[:milieu])   # Tri de la moitié gauche
    droite = tri_fusion(tab[milieu:])   # Tri de la moitié droite
    
    return fusion(gauche, droite)       # Fusion en O(n)

Relation de récurrence :

\[ T(n) = 2T\left(\frac{n}{2}\right) + O(n) \]

C'est une récurrence classique du type "diviser pour régner".

Résolution

Par le théorème maître ou par développement :

\[ T(n) = O(n \log n) \]

Tableau des récurrences courantes

Type de récurrenceExempleComplexité
\(T(n) = T(n-1) + O(1)\)Factorielle, puissance\(O(n)\)
\(T(n) = T(n-1) + O(n)\)Tri par sélection récursif\(O(n^2)\)
\(T(n) = 2T(n-1) + O(1)\)Fibonacci (naïf)\(O(2^n)\)
\(T(n) = T(n/2) + O(1)\)Recherche dichotomique\(O(\log n)\)
\(T(n) = T(n/2) + O(n)\)Cas particulier\(O(n)\)
\(T(n) = 2T(n/2) + O(1)\)Parcours d'arbre binaire\(O(n)\)
\(T(n) = 2T(n/2) + O(n)\)Tri fusion\(O(n \log n)\)

Méthodes de résolution des récurrences

1. Méthode par substitution (développement)

On développe la récurrence jusqu'à atteindre le cas de base.

  Exemple : \(T(n) = T(n-1) + n\)

\[ \begin{align*} T(n) &= T(n-1) + n \\ &= T(n-2) + (n-1) + n \\ &= \ldots \\ &= T(1) + 2 + 3 + \ldots + n \\ &= O(1) + \frac{n(n+1)}{2} - 1 \\ &= O(n^2) \end{align*} \]

2. Théorème maître (Master Theorem)

Pour les récurrences de la forme \(T(n) = aT(n/b) + f(n)\) avec \(a \ge 1, b > 1\) :

CasConditionComplexité
Cas 1\(f(n) = O(n^{\log_b a - \varepsilon})\)\(T(n) = \Theta(n^{\log_b a})\)
Cas 2\(f(n) = \Theta(n^{\log_b a} \log^k n)\)\(T(n) = \Theta(n^{\log_b a} \log^{k+1} n)\)
Cas 3\(f(n) = \Omega(n^{\log_b a + \varepsilon})\)\(T(n) = \Theta(f(n))\)

  Application au tri fusion :

\[ T(n) = 2T(n/2) + O(n) \]

Ici \(a = 2, b = 2, \log_b a = 1, f(n) = n = \Theta(n^{\log_b a} \log^0 n)\) → Cas 2 avec \(k = 0\)

Donc \(T(n) = \Theta(n \log n)\)

Complexité spatiale des fonctions récursives

Pour les fonctions récursives, il faut également prendre en compte l'espace utilisé par la pile d'appels (stack).

Complexité spatiale récursive

La complexité spatiale d'une fonction récursive est la somme de :

  • L'espace utilisé par les variables locales à chaque appel.
  • L'espace de la pile d'appels (profondeur de récursion).

  Exemple 6 : Factorielle

  • Profondeur de récursion : \(n\) appels imbriqués.
  • Espace par appel : constant (\(O(1)\)).
  • Complexité spatiale totale : \(O(n)\)

  Exemple 7 : Recherche dichotomique

  • Profondeur de récursion : \(\log n\) appels imbriqués.
  • Espace par appel : constant (\(O(1)\)).
  • Complexité spatiale totale : \(O(\log n)\)

  Exemple 8 : Tri fusion

  • Profondeur de récursion : \(\log n\) appels imbriqués.
  • Espace par appel : constant (hors tableaux temporaires).
  • Mais : la fusion nécessite un tableau temporaire de taille \(O(n)\) qui domine.
  • Complexité spatiale totale : \(O(n)\)

Récapitulatif des exemples

AlgorithmeRécurrenceComplexité temporelleComplexité spatiale
Factorielle\(T(n) = T(n-1) + O(1)\)\(O(n)\)\(O(n)\)
Fibonacci naïf\(T(n) = T(n-1) + T(n-2) + O(1)\)\(O(2^n)\)\(O(n)\)
Recherche dichotomique\(T(n) = T(n/2) + O(1)\)\(O(\log n)\)\(O(\log n)\)
Tri fusion\(T(n) = 2T(n/2) + O(n)\)\(O(n \log n)\)\(O(n)\)
Parcours d'arbre binaire\(T(n) = 2T(n/2) + O(1)\)\(O(n)\)\(O(\log n)\) (hauteur)
Résumé du cours

L'analyse des fonctions récursives repose sur l'établissement et la résolution de relations de récurrence.

  • Cas de base : condition d'arrêt de la récursion.
  • Relation de récurrence : exprime \(T(n)\) en fonction de \(T\) pour des entrées plus petites.
  • Méthodes de résolution : substitution, théorème maître, arbre de récursion.
  • Complexité spatiale : inclut la pile d'appels (profondeur de récursion).

Sortie
// La sortie apparaîtra ici…
Prêt · Ctrl+Entrée pour exécuter

Discussion (0)

Soyez le premier à laisser un commentaire !

Laisser un commentaire

Votre commentaire sera visible après modération.