Voici une fonction en C qui implémente la suite définie par U(0)=0, U(1)=1, et U(n)=3U(n−1)−U(n−2) pour tout n≥2 :
c#include <stdio.h> // Fonction pour calculer le n-ième terme de la suite int suite(int n) { if (n == 0) { return 0; } else if (n == 1) { return 1; } else { int U_n_2 = 0; // U(0) int U_n_1 = 1; // U(1) int U_n; for (int i = 2; i <= n; i++) { U_n = 3 * U_n_1 - U_n_2; U_n_2 = U_n_1; U_n_1 = U_n; } return U_n; } } int main() { int n; printf("Entrez la valeur de n : "); scanf("%d", &n); if (n < 0) { printf("La valeur de n doit être un entier positif ou nul.\n"); } else { int resultat = suite(n); printf("U(%d) = %d\n", n, resultat); } return 0; }
Fonction suite :
n en entrée et retourne le n-ième terme de la suite.n est 0, elle retourne 0.n est 1, elle retourne 1.n supérieures ou égales à 2, elle utilise une boucle pour calculer les termes de la suite en utilisant la relation de récurrence U(n)=3U(n−1)−U(n−2).Fonction main :
n.n est un entier positif ou nul.suite pour calculer le n-ième terme de la suite et affiche le résultat.Vous pouvez compiler et exécuter ce programme pour voir les termes de la suite pour différentes valeurs de n.
Si on appelle la fonction suite pour des valeurs de plus en plus grandes, on peut observer plusieurs phénomènes intéressants :
Croissance rapide des valeurs :
Dépassement de capacité :
int) ont une plage de valeurs limitée. Par exemple, un int sur une machine 32 bits peut représenter des valeurs allant de -2,147,483,648 à 2,147,483,647. Pour des valeurs de n suffisamment grandes, les termes de la suite peuvent dépasser cette plage, ce qui entraîne un dépassement de capacité (overflow). Cela peut conduire à des résultats incorrects ou à un comportement imprévisible du programme.Temps de calcul :
Pour illustrer, voici quelques termes de la suite pour des valeurs croissantes de n :
On voit que les valeurs augmentent très rapidement. Pour des valeurs de n au-delà de 30 ou 40, les termes de la suite peuvent dépasser la capacité d'un int en C.
Utiliser des types de données plus grands :
long long en C, qui peuvent représenter des valeurs plus grandes.Utiliser des bibliothèques de calcul en précision arbitraire :
Vérifier les dépassements de capacité :
Voici un exemple de modification de la fonction pour utiliser long long :
c#include <stdio.h> // Fonction pour calculer le n-ième terme de la suite long long suite(int n) { if (n == 0) { return 0; } else if (n == 1) { return 1; } else { long long U_n_2 = 0; // U(0) long long U_n_1 = 1; // U(1) long long U_n; for (int i = 2; i <= n; i++) { U_n = 3 * U_n_1 - U_n_2; U_n_2 = U_n_1; U_n_1 = U_n; } return U_n; } } int main() { int n; printf("Entrez la valeur de n : "); scanf("%d", &n); if (n < 0) { printf("La valeur de n doit être un entier positif ou nul.\n"); } else { long long resultat = suite(n); printf("U(%d) = %lld\n", n, resultat); } return 0; }
Cette version utilise long long pour gérer des valeurs plus grandes, mais elle peut encore rencontrer des limitations pour des valeurs très grandes de n.
La complexité temporelle d'un algorithme mesure comment le temps d'exécution de l'algorithme augmente en fonction de la taille de l'entrée. Dans le cas de la fonction suite, la taille de l'entrée est représentée par le paramètre n.
Analysons la fonction suite pour comprendre pourquoi sa complexité temporelle est linéaire :
clong long suite(int n) { if (n == 0) { return 0; } else if (n == 1) { return 1; } else { long long U_n_2 = 0; // U(0) long long U_n_1 = 1; // U(1) long long U_n; for (int i = 2; i <= n; i++) { U_n = 3 * U_n_1 - U_n_2; U_n_2 = U_n_1; U_n_1 = U_n; } return U_n; } }
Cas de base :
n est 0 ou 1, la fonction retourne immédiatement une valeur constante (0 ou 1). Ces cas de base ont une complexité temporelle constante O(1).Boucle for :
n supérieures ou égales à 2, la fonction utilise une boucle for qui itère de 2 à n.n - 1 itérations (de i = 2 à i = n).U_n en utilisant l'expression 3 * U_n_1 - U_n_2.U_n_2 et U_n_1.Complexité totale :
for effectue n - 1 itérations, et chaque itération a une complexité O(1).La fonction suite a une complexité temporelle linéaire O(n) parce que le temps d'exécution de la fonction augmente linéairement avec la valeur de n. En d'autres termes, si n double, le temps d'exécution double également (à une constante près). Cela est dû à la boucle for qui effectue un nombre d'itérations proportionnel à n, avec chaque itération ayant un coût constant.
Si on implémente une version récursive de la fonction suite en reprenant directement la définition U(n)=3U(n−1)−U(n−2), la complexité temporelle sera exponentielle. Voici pourquoi :
clong long suite_recursive(int n) { if (n == 0) { return 0; } else if (n == 1) { return 1; } else { return 3 * suite_recursive(n - 1) - suite_recursive(n - 2); } }
Cas de base :
n est 0 ou 1, la fonction retourne immédiatement une valeur constante (0 ou 1). Ces cas de base ont une complexité temporelle constante O(1).Appels récursifs :
n supérieures ou égales à 2, la fonction fait deux appels récursifs :
suite_recursive(n - 1)suite_recursive(n - 2)Arbre des appels récursifs :
n, et le nombre total de nœuds dans un arbre binaire complet de hauteur n est 2n+1−1.Complexité totale :
n.La version récursive de la fonction suite a une complexité temporelle exponentielle O(2n) parce que chaque appel récursif entraîne deux nouveaux appels récursifs, ce qui conduit à un arbre d'appels de taille exponentielle. Cela signifie que le temps d'exécution de la fonction augmente très rapidement avec la valeur de n, rendant cette version inefficace pour des valeurs de n même modérément grandes.