Calcul du factorielle en C
Calculez n! instantanément, comparez les méthodes itérative et récursive, vérifiez les limites des types C, et visualisez la croissance explosive de la factorielle avec un graphique interactif.
Saisissez un entier puis cliquez sur le bouton pour afficher n!, la vérification d’overflow, un exemple de code C et un graphique d’évolution.
Évolution de la factorielle
Comprendre le calcul du factorielle en C
Le calcul du factorielle en C est un exercice fondamental en algorithmique, en mathématiques discrètes et en programmation système. La factorielle d’un entier naturel n, notée n!, correspond au produit de tous les entiers de 1 à n. Par convention, 0! = 1. Cette définition paraît simple, mais sa mise en oeuvre en langage C révèle immédiatement plusieurs notions essentielles : la gestion des types numériques, le contrôle des entrées utilisateur, la complexité algorithmique, la récursivité, les boucles, et surtout les limites de représentation en mémoire.
En pratique, le factorielle intervient dans le calcul des permutations, des combinaisons, de certaines séries mathématiques, de probabilités, de statistiques et de modèles numériques. Pour un développeur C, ce sujet constitue un terrain idéal pour apprendre à écrire du code sûr, lisible et performant. Même un programme de quelques lignes peut produire de mauvais résultats si l’on ignore l’overflow, les entrées négatives ou les limites d’un type comme int ou unsigned long long.
Définition mathématique de la factorielle
Mathématiquement, la factorielle est définie ainsi :
- 0! = 1
- n! = n × (n – 1) × (n – 2) × … × 1 pour tout entier naturel n > 0
Cette définition conduit naturellement à deux grandes approches en C :
- L’approche itérative, qui utilise une boucle
forouwhile. - L’approche récursive, qui exprime n! en fonction de (n – 1)! jusqu’au cas de base.
Les deux stratégies ont une complexité temporelle simple en O(n), car elles effectuent chacune un nombre linéaire de multiplications. En revanche, la version récursive utilise aussi une pile d’appels, ce qui augmente la consommation mémoire et peut rendre l’approche moins robuste pour de grandes valeurs si aucune protection n’est mise en place.
Version itérative en C
La solution itérative est généralement la plus recommandée dans un code C de production pour ce problème. Elle est concise, claire, facile à déboguer et évite la surcharge liée aux appels récursifs.
Cette version présente plusieurs avantages :
- elle est simple à lire, même pour un débutant ;
- elle n’utilise pas la pile d’appels de manière répétée ;
- elle se prête facilement à l’ajout de contrôles d’overflow ;
- elle est souvent préférée dans les environnements bas niveau et embarqués.
Version récursive en C
La version récursive illustre parfaitement la définition mathématique de la factorielle. Elle peut être élégante dans un cadre pédagogique, mais elle doit être utilisée avec discernement.
Cette approche est intuitive, mais elle présente aussi plusieurs limites :
- chaque appel ajoute un niveau à la pile ;
- la fonction devient plus difficile à sécuriser contre certains cas extrêmes ;
- la lisibilité peut diminuer si l’on ajoute des tests d’erreur et des protections d’overflow.
Pourquoi l’overflow est le vrai sujet en C
Le principal défi du calcul du factorielle en C n’est pas l’algorithme lui-même, mais la capacité du type numérique à contenir le résultat. En C, chaque type possède une plage de valeurs finie. Dès que n! dépasse cette plage, le résultat affiché n’est plus fiable. Dans le cas des entiers signés, le comportement peut même devenir problématique selon le contexte et la plateforme.
Voici pourquoi il faut choisir le bon type :
intest souvent limité à 32 bits sur de nombreuses plateformes modernes ;longdépend du modèle de données de la plateforme ;unsigned long longest un choix courant pour maximiser la plage entière standard ;doublepeut représenter des valeurs beaucoup plus grandes, mais avec une perte de précision sur les grands entiers.
| Type C | Capacité usuelle | Plus grand n exact typique pour n! | Remarque pratique |
|---|---|---|---|
| int | Jusqu’à 2 147 483 647 sur 32 bits | 12 | 13! dépasse la capacité d’un int 32 bits |
| long | Souvent 32 bits sous Windows, souvent 64 bits sous Linux 64 bits | 12 ou 20 selon la plateforme | Type portable avec prudence, car sa taille varie |
| unsigned long long | Jusqu’à 18 446 744 073 709 551 615 | 20 | Le standard pratique le plus fréquent pour des factorials exactes |
| double | Très grande plage, environ jusqu’à 1,7 × 10^308 | Au-delà de 20 en magnitude, mais pas en exactitude entière | Utile pour l’ordre de grandeur, pas pour l’exactitude entière |
Les valeurs ci-dessus correspondent à des comportements fréquemment observés sur les plateformes courantes, mais il faut toujours vérifier les limites réelles avec les en-têtes standards comme limits.h et float.h. C’est une bonne habitude pour produire un programme portable.
Statistiques réelles sur quelques valeurs de factorielle
Pour bien comprendre la croissance, examinons quelques résultats exacts et leur ordre de grandeur. Ces données montrent pourquoi la factorielle devient rapidement difficile à manipuler sans bibliothèques multiprécision.
| n | n! | Nombre approximatif de chiffres | Compatible avec unsigned long long ? |
|---|---|---|---|
| 5 | 120 | 3 | Oui |
| 10 | 3 628 800 | 7 | Oui |
| 12 | 479 001 600 | 9 | Oui, et aussi dans int 32 bits |
| 13 | 6 227 020 800 | 10 | Oui, mais plus dans int 32 bits |
| 20 | 2 432 902 008 176 640 000 | 19 | Oui |
| 21 | 51 090 942 171 709 440 000 | 20 | Non |
| 50 | ≈ 3,0414 × 10^64 | 65 | Non |
| 100 | ≈ 9,3326 × 10^157 | 158 | Non |
Comment écrire un programme C robuste
Un bon programme de calcul du factorielle ne se contente pas de multiplier des entiers. Il doit aussi gérer les erreurs utilisateur et informer clairement lorsque le résultat ne peut plus être représenté. Voici les bonnes pratiques essentielles :
- Refuser les nombres négatifs, car la factorielle simple n’est définie que sur les entiers naturels.
- Valider les entrées lues avec
scanfou une autre méthode. - Choisir un type adapté selon l’objectif du programme.
- Détecter l’overflow avant la multiplication si l’on veut éviter les résultats invalides.
- Documenter les limites dans l’interface ou dans les commentaires du code.
Une stratégie classique de détection consiste à vérifier, avant chaque multiplication, si resultat > MAX / i. Si c’est le cas, l’opération suivante dépasserait la capacité du type.
Itératif ou récursif : que choisir vraiment ?
Dans la plupart des cas, l’approche itérative est le meilleur choix en C. Elle limite la surcharge, elle est plus simple à sécuriser et elle correspond bien à la philosophie d’un langage proche de la machine. La récursivité reste très utile pour l’enseignement, pour illustrer la définition mathématique ou pour comparer différents styles de programmation, mais elle n’apporte pas de gain pratique sur ce problème précis.
- Préférez l’itératif si vous voulez du code fiable, direct et efficace.
- Utilisez la récursivité si votre objectif est pédagogique ou si vous travaillez sur des exercices académiques.
- Passez à une bibliothèque multiprécision si vous devez calculer de très grands factorials sans perte d’exactitude.
Que faire pour des valeurs très grandes ?
Dès que vous dépassez les limites des types natifs, plusieurs solutions existent. La plus sérieuse est l’utilisation de bibliothèques de grands nombres, capables de représenter des entiers de taille arbitraire. Une autre solution consiste à ne calculer que le logarithme de n!, ce qui est utile en statistiques et en analyse combinatoire. Dans certains cas, une approximation comme la formule de Stirling suffit pour estimer l’ordre de grandeur.
Si votre but est purement mathématique ou scientifique, vous pouvez :
- utiliser une bibliothèque multiprécision pour conserver tous les chiffres ;
- travailler en
doublepour obtenir une approximation ; - calculer
log(n!)pour éviter les débordements ; - n’afficher qu’une notation scientifique lorsque la valeur exacte n’est pas nécessaire.
Applications concrètes du factorielle
Le calcul du factorielle ne se limite pas aux exercices scolaires. Il intervient dans de nombreuses applications concrètes :
- calcul du nombre de permutations d’un ensemble ;
- calcul des combinaisons via les coefficients binomiaux ;
- lois de probabilité discrètes comme la loi de Poisson ;
- séries de Taylor et méthodes numériques ;
- cryptographie, théorie des nombres et algorithmique combinatoire.
Dans un projet C, comprendre le factorielle signifie donc aussi comprendre comment structurer un calcul numérique, comment choisir un type de donnée et comment anticiper les limites de votre environnement d’exécution. C’est un petit problème, mais il concentre des réflexes très utiles pour la programmation réelle.
Sources fiables pour aller plus loin
Pour approfondir la programmation numérique et les limites des types, consultez des ressources de confiance : NIST, Carnegie Mellon University, University of Utah Mathematics.
Conclusion
Le calcul du factorielle en C est un excellent sujet pour progresser à la fois en mathématiques et en développement. La formule paraît élémentaire, mais sa traduction correcte en code impose de penser à la validité des données, au choix du type, à la détection d’overflow, à la lisibilité de l’algorithme et à la portabilité du programme. Pour un usage courant, l’approche itérative avec unsigned long long et contrôle des limites constitue souvent le meilleur compromis. Pour des besoins scientifiques ou de très grands n, il faut rapidement se tourner vers des bibliothèques multiprécision ou des méthodes d’approximation. En résumé, maîtriser ce calcul, c’est déjà adopter des réflexes de développeur C expérimenté.