Méthodes d'Optimisation

Suite de Fibonacci, Exponentiation & Analyse de Complexité

0. Méthodologie : Comment compter les appels ?

Outil d'analyse

Pour déterminer la complexité d'un algorithme récursif, on ne devine pas. On suit une méthode stricte :

1. Contexte Historique (1202)

Introduit par Léonard de Pise (Fibonacci) dans le Livre de l’abaque. L'objectif initial était d'étudier l'évolution d'une population de lapins.

Modèle de croissance :
  • \( x \) : couple de lapereaux (ne se reproduisent pas encore)
  • \( X \) : couple de lapins adultes (se reproduisent)

Relation de récurrence : \( F(n) = F(n-1) + F(n-2) \) pour \( n \ge 2 \)
Mois (n) Lapereaux (x) Adultes (X) Total F(n)
1101
2011
3112
4123
5235

2. Premier Algorithme : Formule de Binet

O(1) Mathématique

On résout l'équation caractéristique \( r^2 - r - 1 = 0 \). Les racines incluent le nombre d'or \( \phi \).

$$ F(n) = \frac{1}{\sqrt{5}} \left[ \left(\frac{1+\sqrt{5}}{2}\right)^n - \left(\frac{1-\sqrt{5}}{2}\right)^n \right] $$
Approximation : \( F(n) \sim \frac{1}{\sqrt{5}} \phi^n \)

⚠️ Bien que mathématiquement directe, cette méthode pose des problèmes de précision flottante sur ordinateur pour les grands nombres.

3. Deuxième Algorithme : Récursif Naïf

Exponentiel
Fonction F(n)
    Si n <= 1 Alors Retourne n 
    Sinon Retourne F(n-1) + F(n-2) 
Fin Si

Analyse : Le nombre d'additions est \( P(n) = F(n+1) - 1 \).
La complexité explose de manière exponentielle : \( O(\phi^n) \).

4. Troisième Algorithme : Itératif

O(n) Linéaire
Fonction F(n)
    a = 1, b = 0
    Pour i de 1 à n :
        a, b = a + b, a
    Retour b

Complexité : \( P(n) = n \). Simple et efficace pour la plupart des usages standards.

5. Algorithme Dichotomique (Fast Doubling)

O(log n) Logarithmique

On utilise les formules de duplication pour sauter les étapes (méthode "Divide and Conquer").

$$ F(2n) = F(n)^2 + 2F(n)F(n-1) $$ $$ F(2n+1) = F(n)^2 + F(n+1)^2 $$
Implémentation (Fonction G)

La fonction \( G(n) \) retourne le couple \( (F(n), F(n+1)) \).

Fonction G(n) : (entier, entier)
    Si n = 0 Alors Retour (0, 1)

    // Appel récursif sur la moitié
    (p, q) = G((n-1)/2)  
    
    Si n est pair Alors
        Retour (2pq + q², q² + (p+q)²)
    Sinon
        Retour (p² + q², 2pq + q²)
    FinSi
🚀
Performance : \( A(n) = 1 + \log_2(n+1) \).
Pour \( n=1000 \), seulement ~10 appels nécessaires !

6. Exercice d'Application

Analyse

Soit la fonction \( f(n) \) définie par la récurrence :

$$ f(2n) = 2n + 2f(n) $$ $$ f(2n+1) = (2n+1) + f(n) + f(n+1) $$
nA(n) Appels
11
23
35
47

On observe que \( A(n) = 2n - 1 \).
C'est donc une complexité Linéaire.
Pour l'optimiser en logarithmique, il faudrait utiliser la technique du couple (comme Fibonacci).

7. Généralisation : Exponentiation Rapide

O(log n) Logarithmique

Même principe appliqué au calcul de puissance \( x^n \). Propriété clé : \( x^{2n} = (x^n)^2 \).

def e(x, n):
    if n == 0 : return 1        // Arrêt
    val = e(x, n // 2)          // 1 seul appel récursif !
    val = val * val
    if n % 2 == 0 : return val  // n pair
    return val * x              // n impair
Analyse du nombre d'appels

1. Récurrence : \( A(n) = 1 + A(\lfloor n/2 \rfloor) \)

2. Résolution : \( A(n) = \lfloor \log_2(n) \rfloor + 2 \)

Le +2 provient de l'appel pour \( n=1 \) et de la condition d'arrêt \( n=0 \).