Fibonacci Python - Boucle, récursion, cache - Lequel choisir ?

Alfred Jacques .

19 mars 2026

Spirale de Fibonacci illustrant la suite logique, comme une fonction fibonacci python. Les nombres 1, 1, 2, 3, 5, 8, 13, 21 sont représentés.
Une bonne implémentation de Fibonacci en Python ne se résume pas à quelques lignes de code. Il faut surtout choisir une convention de départ, décider si l’on veut un seul terme ou toute la suite, puis sélectionner une approche qui reste lisible et rapide. Je vais passer en revue la version avec boucle, la récursion, la mémoïsation et le générateur, avec les pièges qui font perdre du temps aux débutants comme aux profils plus expérimentés.

Les trois décisions qui comptent avant d’implémenter Fibonacci

  • Fixer la convention dès le départ, le plus souvent avec F0 = 0 et F1 = 1.
  • Choisir le bon retour : un terme unique, une liste ou un générateur.
  • Éviter la récursion naïve dès que n commence à monter, car elle devient vite coûteuse.
  • Prévoir la validation des entrées, surtout pour les valeurs négatives.
  • Préférer la boucle pour un usage courant, et le cache seulement si les appels se répètent.

Ce qu’il faut décider avant d’écrire le code

Le point le plus mal compris, c’est rarement l’algorithme. C’est la définition exacte de ce que la fonction doit renvoyer. Je vois encore souvent des confusions entre le n-ième terme et les n premiers termes, ou entre les conventions F0 = 0, F1 = 1 et F1 = 1, F2 = 1. Si cette base n’est pas claire, le code peut être “correct” mais produire un résultat inattendu.

Le point de départ

Dans un contexte Python classique, je pars presque toujours sur F0 = 0 et F1 = 1. C’est lisible, standard, et cela facilite les tests. Si vous devez travailler avec une convention différente, le bon réflexe est de la documenter dans la docstring, pas de la cacher dans les lignes de calcul.

Lire aussi : π en Python - Le guide complet pour des calculs précis

Le type de sortie

Une fonction peut renvoyer un entier, une liste ou un itérateur. Ce choix change tout. Si vous voulez juste le terme Fn, une fonction simple suffit. Si vous voulez parcourir la suite, un générateur est plus propre. Si vous voulez réutiliser les mêmes résultats plusieurs fois, le cache devient intéressant. Cette distinction me sert ensuite à choisir la bonne implémentation, sans sur-ingénierie.

Une fois ce cadre posé, la version la plus sûre à écrire est presque toujours la plus simple : une boucle.

La version avec boucle que j’utilise par défaut

Pour calculer un terme précis, la solution itérative est celle que je recommande en premier. Elle est courte, facile à lire, et sa complexité reste linéaire. En pratique, cela veut dire qu’elle scale proprement tant que n reste raisonnable, sans explosion du nombre d’appels comme avec la récursion naïve.

def fibonacci(n: int) -> int:
    if n < 0:
        raise ValueError("n doit être positif ou nul")
    if n < 2:
        return n

    a, b = 0, 1
    for _ in range(2, n + 1):
        a, b = b, a + b
    return b

Cette version a trois avantages concrets. D’abord, elle est prévisible : un seul passage dans la boucle. Ensuite, elle consomme très peu de mémoire, car elle garde seulement les deux derniers termes. Enfin, elle se teste facilement avec quelques cas simples : 0, 1, 2, 10 et une valeur négative.

Si l’on veut les premiers termes de la suite plutôt qu’un seul nombre, il suffit d’adapter la logique et de produire une collection. Pour une suite destinée à être consommée en aval, j’évite de construire une liste géante si un générateur suffit. La récursion, elle, raconte une histoire plus élégante sur le papier, mais elle a un coût bien réel.

Pourquoi la récursion séduit, mais piège vite

La version récursive est souvent celle que l’on écrit en premier quand on découvre Fibonacci. Elle reflète bien la définition mathématique : chaque terme est la somme des deux précédents. Le problème, c’est qu’en Python cette beauté conceptuelle cache une redondance massive des calculs.

def fibonacci_recursive(n: int) -> int:
    if n < 0:
        raise ValueError("n doit être positif ou nul")
    if n < 2:
        return n
    return fibonacci_recursive(n - 1) + fibonacci_recursive(n - 2)

Le défaut principal est simple : la fonction recalcule plusieurs fois les mêmes valeurs. Pour F10, cela reste supportable. Pour F30 ou F40, le nombre d’appels grimpe très vite. On bascule alors vers une complexité exponentielle, ce qui rend l’approche inadaptée à un usage sérieux.

Il y a aussi une contrainte moins visible : la pile d’appels. Python n’est pas conçu pour encaisser de longues chaînes de récursion sans limite. La version récursive reste donc excellente pour expliquer le principe, mais je la considère comme une version pédagogique, pas comme un choix de production.

Si l’on veut garder l’écriture récursive tout en supprimant le vrai problème, il faut ajouter une couche de cache. C’est là que la mémoïsation devient utile.

Quand la mémoïsation change vraiment la donne

La documentation officielle Python présente cache comme un cache léger et non borné, équivalent à lru_cache(maxsize=None). C’est exactement le genre d’outil qui transforme une récursion coûteuse en solution propre, parce qu’il évite de recalculer les mêmes termes encore et encore.

from functools import cache

@cache
def fibonacci_cached(n: int) -> int:
    if n < 0:
        raise ValueError("n doit être positif ou nul")
    if n < 2:
        return n
    return fibonacci_cached(n - 1) + fibonacci_cached(n - 2)

Avec ce décorateur, chaque valeur est calculée une fois, puis réutilisée. On garde la lisibilité de la récursion, mais on supprime l’essentiel du surcoût. En retour, on accepte une mémoire supplémentaire pour stocker les résultats. Ce compromis est souvent raisonnable quand on appelle plusieurs fois la fonction avec des entrées proches.

Je n’utilise pas ce pattern par réflexe dans tous les projets. Il est pertinent si plusieurs appels répètent les mêmes valeurs, si l’on veut garder une structure récursive pour des raisons pédagogiques, ou si l’on doit comparer rapidement différentes entrées. Sinon, la boucle reste plus sobre. Il reste alors à choisir entre une sortie matérialisée en mémoire et une sortie produite à la demande.

Arbre de calcul récursif pour la fonction fibonacci python. fib(7) donne 13.

Choisir entre boucle, cache et générateur

Une fois les variantes en main, la vraie question devient : quel outil sert le mieux le besoin réel ? Pour un calcul isolé, je privilégie la boucle. Pour un accès répété avec les mêmes arguments, la mémoïsation a du sens. Pour produire une suite terme par terme sans tout stocker, le générateur est plus élégant.

Méthode Temps Mémoire Je la choisis quand
Boucle O(n) O(1) Je veux le terme n-ième de manière simple et robuste.
Récursion naïve Exponential, ≈ O(φ^n) O(n) Je démontre l’idée, mais pas pour un usage réel.
Récursion avec cache O(n) O(n) Les mêmes entrées reviennent souvent et je veux garder une écriture récursive.
Générateur O(n) pour n termes O(1) Je veux parcourir la suite sans la stocker entièrement.

Le générateur mérite une place à part, parce qu’il correspond très bien à la philosophie de Python. La documentation Python rappelle qu’une fonction qui utilise yield devient un générateur : elle produit les valeurs une par une, au lieu de construire un objet complet dès le départ.

def fibonacci_sequence(count: int):
    if count < 0:
        raise ValueError("count doit être positif ou nul")

    a, b = 0, 1
    for _ in range(count):
        yield a
        a, b = b, a + b

Cette version est très pratique pour afficher les n premiers termes, alimenter une boucle, ou traiter la suite dans un pipeline. Elle évite le gaspillage mémoire, mais elle ne remplace pas toujours une liste. Si vous devez indexer plusieurs fois les valeurs obtenues, une liste reste parfois plus confortable. Le bon choix dépend donc moins de Fibonacci lui-même que de l’usage que vous faites des résultats.

Avant d’arrêter une implémentation, j’examine toujours les erreurs de logique les plus courantes, parce que ce sont elles qui rendent une fonction “presque bonne” mais fausse.

Les erreurs qui cassent le plus souvent une fonction Fibonacci

  • Confondre le rang et la valeur : F10 n’est pas la dixième valeur affichée selon toutes les conventions possibles, c’est le terme à l’indice 10 dans une convention précise.
  • Oublier le cas n = 0 ou n = 1, ce qui crée des résultats incohérents dès les premières entrées.
  • Retourner un print au lieu de retourner la valeur, ce qui rend la fonction difficile à réutiliser.
  • Mélanger génération de suite et calcul d’un terme, alors que ces deux besoins méritent souvent deux fonctions distinctes.
  • Ignorer les entrées négatives, qui devraient lever une exception explicite plutôt que produire un résultat absurde.
  • Sous-estimer la taille des nombres : en Python, les entiers sont arbitrairement grands, mais le coût d’addition augmente quand les valeurs grossissent.

Le dernier point est important. Il ne s’agit pas d’un “bug” de Python, mais d’un effet réel sur les performances. Plus les termes deviennent grands, plus l’addition de grands entiers prend du temps. Pour des valeurs modestes, on ne le sent pas. Pour des suites longues, cela compte.

Quand je révise ce type de fonction, je fais aussi attention aux noms. Une fonction nommée fibonacci devrait faire une seule chose clairement définie. Si elle imprime, retourne une liste, accepte des cas exotiques et calcule des indices dans le même bloc, elle devient vite moins fiable qu’elle n’en a l’air. C’est précisément pour cela que je préfère une version finale courte, documentée et testable.

Ce que je livrerais pour un vrai projet Python

Pour un code propre en 2026, je choisirais une version itérative pour le calcul d’un terme, et une fonction séparée si j’ai besoin de parcourir la suite. J’ajouterais une docstring courte, une validation d’entrée et quelques tests unitaires. Ce trio suffit souvent à éviter 80 % des erreurs qui apparaissent plus tard en revue de code.

def fibonacci(n: int) -> int:
    """Retourne le terme F_n avec F_0 = 0 et F_1 = 1."""
    if n < 0:
        raise ValueError("n doit être positif ou nul")
    if n < 2:
        return n

    a, b = 0, 1
    for _ in range(2, n + 1):
        a, b = b, a + b
    return b
  • Je documente la convention dès la docstring, pour éviter les ambiguïtés sur le point de départ.
  • Je teste les bornes avec 0, 1 et 2, parce que c’est là que les erreurs apparaissent le plus vite.
  • Je garde la récursion pour l’explication, pas pour le chemin nominal d’exécution.
  • J’utilise cache uniquement si plusieurs appels répètent les mêmes calculs ou si je veux comparer l’approche à la boucle.
  • Je choisis un générateur si le besoin réel est de parcourir une suite, pas de la stocker.

En pratique, la meilleure réponse à la suite de Fibonacci en Python est rarement la plus sophistiquée. Pour un calcul isolé, la boucle gagne presque toujours. Pour une suite consommée en streaming, le générateur est plus élégant. Et pour un exercice pédagogique, la récursion mérite sa place tant qu’on garde à l’esprit ses limites réelles.

Questions fréquentes

La meilleure méthode dépend de votre besoin. Pour un terme unique, une boucle est la plus efficace (O(n) temps, O(1) mémoire). Pour une suite à parcourir, un générateur est idéal. La récursion avec cache est bonne si les mêmes valeurs sont souvent demandées.
La récursion naïve est déconseillée car elle entraîne des calculs redondants massifs, menant à une complexité exponentielle (≈ O(φ^n)). Elle consomme aussi beaucoup de mémoire avec la pile d'appels, la rendant inefficace pour des valeurs de 'n' élevées.
Utilisez la mémoïsation (avec `@cache`) lorsque vous avez une implémentation récursive et que les mêmes arguments sont appelés plusieurs fois. Cela évite de recalculer des valeurs déjà connues, transformant la complexité exponentielle en linéaire (O(n)) au prix d'une mémoire O(n)).
Un générateur est préférable si vous voulez parcourir la suite terme par terme sans la stocker entièrement, économisant ainsi de la mémoire (O(1)). Cependant, si vous avez besoin d'accéder plusieurs fois aux valeurs par index ou de les manipuler comme une collection, une liste peut être plus pratique.
Évitez de confondre rang et valeur, d'oublier les cas F0/F1, de mélanger calcul de terme et génération de suite, ou d'ignorer les entrées négatives. Assurez-vous que votre fonction retourne une valeur, pas un affichage, et documentez bien la convention de départ.

Évaluer l'article

Moyenne: 0.0 / 5 · 0 évaluations

Tags

implémentation fibonacci python fibonacci python performance fibonacci python récursif vs itératif fonction fibonacci python
Autor Alfred Jacques
Alfred Jacques
Je m'appelle Alfred Jacques et je suis passionné par les technologies, en particulier dans les domaines du web, de l'intelligence artificielle, des réseaux et de la sécurité. Fort de plusieurs années d'expérience en tant qu'analyste de l'industrie, j'ai eu l'opportunité d'explorer en profondeur les tendances et les innovations qui façonnent notre monde numérique. Mon expertise se concentre sur l'analyse des systèmes de sécurité, l'impact de l'IA sur les entreprises et l'évolution des infrastructures web. Je m'efforce de simplifier des données complexes pour les rendre accessibles à tous, tout en garantissant une analyse objective et rigoureuse. Mon engagement envers mes lecteurs est de fournir des informations précises, à jour et fiables, afin de les aider à naviguer dans cet écosystème technologique en constante évolution.

Commentaires (0)

Ajouter un commentaire