Un programme C qui calcule n!, avec des tests qui vérifient qu'il ne raconte pas n'importe quoi.
La factorielle de n, notée n!, c'est le produit de tous les entiers de 1 à n. Par convention, 0! = 1 (parce que les mathématiciens aiment bien que leurs formules marchent même quand il n'y a rien à multiplier).
| n | n! |
|---|---|
| 0 | 1 |
| 1 | 1 |
| 2 | 2 |
| 3 | 6 |
| 4 | 24 |
| 5 | 120 |
| 6 | 720 |
| 7 | 5 040 |
| 8 | 40 320 |
| 9 | 362 880 |
| 10 | 3 628 800 |
| 12 | 479 001 600 |
| 20 | 2 432 902 008 176 640 000 |
long long.)
Deux fonctions, pas une de plus. On n'est pas là pour faire du volume.
long long factorielle(int n) { if (n < 0) return -1; // cas d'erreur : pas de factorielle pour les négatifs long long res = 1; for (int i = 2; i <= n; i++) { res *= i; } return res; }
Algorithme : itératif. On part de 1, on multiplie par tous les entiers
de 2 à n. C'est simple, c'est lisible, et ça marche pour n jusqu'à 20
avant que long long (64 bits) ne dise « stop, je déborder ».
-O2 et le compilateur transforme votre belle récursion
en boucle de toute façon. Autant l'écrire directement et économiser
une pile d'appels. Et des nerfs.
int main() { struct { int n; long long attendu; } tests[] = { {0, 1}, {1, 1}, {2, 2}, {3, 6}, {5, 120}, {10, 3628800}, {12, 479001600}, {20, 2432902008176640000LL}, }; // ... parcourir, comparer, afficher OK/FAIL ... }
8 cas de test qui couvrent :
long long tient la routeQuand tout va bien :
OK factorielle(0) = 1
OK factorielle(1) = 1
OK factorielle(2) = 2
OK factorielle(3) = 6
OK factorielle(5) = 120
OK factorielle(10) = 3628800
OK factorielle(12) = 479001600
OK factorielle(20) = 2432902008176640000
8 passes, 0 echoues sur 8 tests
Si un test échoue (par exemple si on a cassé la fonction) :
OK factorielle(0) = 1
FAIL factorielle(3) = 5 (attendu 6)
OK factorielle(5) = 120
...
1 passes, 1 echoues sur 8 tests
Le code de retour est EXIT_SUCCESS (0) si tout passe,
EXIT_FAILURE (1) sinon. Utile pour les scripts : on peut faire
make run && echo "tout va bien".
exit(1) bien placé, c'est l'assurance-vie de votre pipeline CI.
long longlong long (signé, 64 bits) peut stocker au maximum
9 223 372 036 854 775 807 (263−1).
20! = 2 432 902 008 176 640 000 < 9,22×10¹⁸ → ✅ OK
21! = 51 090 942 171 709 440 000 > 9,22×10¹⁸ → ❌ Débordement
Quand ça déborde, le compilateur ne dit rien (pas de vérification à l'exécution en C). On obtient juste un résultat faux, modulo 264 — c'est-à-dire complètement à côté de la plaque.
| n | Résultat réel | long long retourné |
|---|---|---|
| 20 | 2 432 902 008 176 640 000 | ✅ Correct |
| 21 | 51 090 942 171 709 440 000 | ❌ 30 414 093 201 713 037 824 (modulo 264) |
| 30 | 265 252 859 812 191 058 636 308 480 000 000 | ❌ Faux complet |
Le projet utilise un Makefile conçu pour l'exploration pédagogique :
| Commande | Ce qui se passe |
|---|---|
make run | Compile et lance les tests |
make asm | Génère le fichier assembleur |
make debug | Compile en debug et ouvre gdb |
make perf | Compare les niveaux d'optimisation |
make clean | Nettoie tout (comme si on n'avait jamais rien fait) |
make clean a le même effet psychologique que ranger son bureau
le vendredi soir: on a l'impression que tout est possible lundi matin.
(Spoiler : lundi matin, on relance make et ça repart.)
| Variante | Description | Avantage | Inconvénient |
|---|---|---|---|
| Récursive | if (n <= 1) return 1; | Élégante, mathématique | Risque de stack overflow pour grand n, plus lent sans tail-call |
| Récursive terminale | return fact_iter(n, 1) avec accumulateur | Le compilateur peut optimiser en boucle | Syntaxe plus lourde, nécessite deux fonctions |
| Table précalculée | Un tableau fact[21] rempli à la compilation | Temps constant (O(1)) | Fixe, pas de généralisation |
| Arbitrary Precision | Utilise __int128 (GCC/clang) ou GMP | Plus de limite de taille | Moins portable, plus complexe |
| Template / générique | Macro ou fonction template pour choisir le type retour | Flexible | Horrible à débugger (surtout les macros) |
La factorielle sert (entre autres) à :
40! pour rigoler| Propriété | Valeur |
|---|---|
| Langage | C99 |
| Algorithme | Itératif, O(n) en temps, O(1) en mémoire |
| Type de retour | long long (64 bits signé) |
| Plage valide | 0 ≤ n ≤ 20 (n < 0 → -1, n > 20 → overflow silencieux) |
| Tests | 8 cas, couvrant n ∈ {0, 1, 2, 3, 5, 10, 12, 20} |
| Compilation | gcc -Wall -Wextra -std=c99 -o factorielle factorielle.c |
| Make | make run |
hello world. C'est le « bonjour le monde » des mathématiques.
Si votre factorielle ne marche pas, vous n'êtes pas prêt pour le tri fusion.
(Allez, on rigole, mais rm -rf * c'est quand même pas une bonne idée.)