Exercices : Suites numériques et approximations
Cette série d'exercices explore les suites numériques, les approximations de constantes et les fonctions mathématiques à travers des algorithmes itératifs et récursifs.
Constante de Catalan
On se propose de calculer une valeur approchée de la constante K de Catalan en utilisant la formule suivante :

Écrire une fonction val_app(epsilon) qui permet de retourner une valeur approchée de la constante K en utilisant la formule ci-dessus et en s'arrêtant dès que la valeur absolue de la différence entre deux sommes successives devient inférieure ou égale à une erreur epsilon donnée en paramètre.
#include <stdio.h>
#include <math.h>
float val_app(float epsilon)
{
float elm1, elm2;
int signe, val;
elm1 = 1;
elm2 = elm1 - 1 / (pow(3, 2));
signe = 1;
val = 5;
while (fabsf(elm2 - elm1) > epsilon)
{
elm1 = elm2;
elm2 = elm1 + signe * (1 / (pow(val, 2)));
val += 2;
signe *= (-1);
}
return elm2;
}
int main(void)
{
printf("val_app(0.05) = %f \n", val_app(0.05));
printf("val_app(0.001) = %f \n", val_app(0.001));
return 0;
}def val_app(epsilon):
"""
Calcule une approximation de la constante de Catalan
avec une précision epsilon.
"""
elm1 = 1
elm2 = elm1 - 1 / (3 ** 2)
signe = 1
val = 5
while abs(elm2 - elm1) > epsilon:
elm1 = elm2
elm2 = elm1 + signe * (1 / (val ** 2))
val += 2
signe *= (-1)
return elm2
def val_app_detail(epsilon):
"""
Version détaillée avec affichage des étapes.
"""
elm1 = 1
elm2 = elm1 - 1 / (3 ** 2)
signe = 1
val = 5
iteration = 1
print(f"Itération {iteration}: {elm2:.8f}")
while abs(elm2 - elm1) > epsilon:
iteration += 1
elm1 = elm2
elm2 = elm1 + signe * (1 / (val ** 2))
val += 2
signe *= (-1)
print(f"Itération {iteration}: {elm2:.8f} (diff = {abs(elm2 - elm1):.8f})")
return elm2
# Tests
print(f"val_app(0.05) = {val_app(0.05):.6f}")
print(f"val_app(0.01) = {val_app(0.01):.6f}")
print(f"val_app(0.001) = {val_app(0.001):.6f}")
print("\nDétail pour epsilon=0.1:")
val_app_detail(0.1)val_app(0.05) = 0.928889 val_app(0.01) = 0.915976 val_app(0.001) = 0.915966 Détail pour epsilon=0.1: Itération 1: 0.88888889 Itération 2: 0.92222222 (diff = 0.03333333) Itération 3: 0.91739967 (diff = 0.00482255) Itération 4: 0.91599409 (diff = 0.00140558)
epsilon = 0.05
0.928889
Approximation de cos(x)
Soit la formule suivante qui permet de déterminer une valeur approchée de cos(x) :

Écrire une fonction Calcul_cos(x) qui permet de :
- Saisir un réel x appartenant à l'intervalle [-1, 1]
- Calculer et afficher une valeur approchée de cos(x) en utilisant la formule donnée. Le calcul s'arrête lorsque la différence entre deux termes consécutifs devient inférieure à 10-4.
#include <stdio.h>
#include <math.h>
float Calcul_cos(float x)
{
float elm1, elm2;
int signe, f, puis;
elm1 = 1;
elm2 = elm1 - (pow(x, 2) / 2);
signe = 1;
puis = 2;
f = 2;
while (fabsf(elm2 - elm1) > 0.0001)
{
f = f * (puis + 1) * (puis + 2);
puis += 2;
elm1 = elm2;
elm2 = elm1 + signe * (pow(x, puis) / f);
signe *= (-1);
}
return elm2;
}
int main(void)
{
printf("cos(0.5) = %f \n", Calcul_cos(0.5));
printf("cos(0.2) = %f \n", Calcul_cos(0.2));
printf("cos(1.0) = %f \n", Calcul_cos(1.0));
return 0;
}def Calcul_cos(x, epsilon=1e-4):
"""
Calcule une approximation de cos(x) par la série de Taylor.
"""
if x < -1 or x > 1:
raise ValueError("x doit être dans [-1, 1]")
elm1 = 1
elm2 = elm1 - (x**2 / 2)
signe = 1
puis = 2
f = 2
iteration = 1
print(f"Itération {iteration}: {elm2:.8f}")
while abs(elm2 - elm1) > epsilon:
iteration += 1
f = f * (puis + 1) * (puis + 2)
puis += 2
elm1 = elm2
elm2 = elm1 + signe * (x**puis / f)
signe *= (-1)
print(f"Itération {iteration}: {elm2:.8f} (diff = {abs(elm2 - elm1):.8f})")
return elm2
def Calcul_cos_simple(x, epsilon=1e-4):
"""Version sans affichage."""
if x < -1 or x > 1:
raise ValueError("x doit être dans [-1, 1]")
terme = 1
somme = terme
n = 1
signe = -1
while abs(terme) > epsilon:
terme = terme * x * x / (2*n) / (2*n - 1)
somme += signe * terme
signe *= -1
n += 1
return somme
# Tests
print(f"cos(0.5) = {Calcul_cos(0.5):.6f}")
print(f"cos(0.2) = {Calcul_cos_simple(0.2):.6f}")
print(f"cos(1.0) = {Calcul_cos_simple(1.0):.6f}")
print(f"cos(0.0) = {Calcul_cos_simple(0.0):.6f}")
# Comparaison avec math.cos
import math
print(f"math.cos(0.5) = {math.cos(0.5):.6f}")cos(0.5) = 0.877582 cos(0.2) = 0.980067 cos(1.0) = 0.540278 cos(0.0) = 1.000000 math.cos(0.5) = 0.877583
Suite de Syracuse (3n+1)
Soit la suite U définie par :
- U₀ est un entier positif pris au hasard (avec 3 < U₀ < 40)
- Uₙ = Uₙ₋₁/2 si Uₙ₋₁ est pair, sinon Uₙ = 3×Uₙ₋₁ + 1 (n > 0)
Cette suite aboutit au cycle redondant formé par les trois termes 4, 2, 1 à partir d'un certain rang.
Exemple
Pour U₀ = 3 : 3 → 10 → 5 → 16 → 8 → 4 → 2 → 1 → 4 → 2 → 1...
La suite entre dans le cycle 4,2,1 à partir du 6ème terme (rang = 6).
Écrire une fonction permettant de déterminer le rang à partir duquel la suite U aboutit au cycle redondant 4, 2 et 1.
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
int rang()
{
int u0, pos, Un;
// Générer une valeur aléatoire dans l'intervalle 3-40
srand(time(0));
u0 = rand() % (40 - 3 + 1) + 3;
printf("U0 = %d\n", u0);
pos = 1;
while (u0 != 4)
{
if (u0 % 2 == 0)
Un = u0 / 2;
else
Un = 3 * u0 + 1;
printf("U%d = %d\n", pos, Un);
u0 = Un;
pos += 1;
}
return pos;
}
int main(void)
{
printf("Position d'entrée dans le cycle 4,2,1 : %d \n", rang());
return 0;
}import random
def syracuse(u0):
"""
Calcule la suite de Syracuse à partir de u0.
Retourne le nombre d'étapes pour atteindre 4.
"""
u = u0
etapes = 0
print(f"U0 = {u}")
while u != 4:
if u % 2 == 0:
u = u // 2
else:
u = 3 * u + 1
etapes += 1
print(f"U{etapes} = {u}")
return etapes
def rang_cycle():
"""
Génère un u0 aléatoire et retourne le rang d'entrée dans le cycle 4,2,1.
"""
u0 = random.randrange(4, 40) # 4 ≤ u0 < 40
return syracuse(u0)
def temps_de_vol(n):
"""
Calcule le temps de vol (nombre d'étapes avant d'atteindre 1)
pour la conjecture de Syracuse.
"""
if n == 1:
return 0
if n % 2 == 0:
return 1 + temps_de_vol(n // 2)
else:
return 1 + temps_de_vol(3 * n + 1)
# Tests
print("=== Test avec u0 aléatoire ===")
rang = rang_cycle()
print(f"Rang d'entrée dans le cycle 4,2,1 : {rang}")
print("\n=== Temps de vol pour quelques valeurs ===")
for n in [7, 13, 27, 42]:
print(f"Temps de vol de {n} : {temps_de_vol(n)}")=== Test avec u0 aléatoire === U0 = 23 U1 = 70 U2 = 35 U3 = 106 U4 = 53 U5 = 160 U6 = 80 U7 = 40 U8 = 20 U9 = 10 U10 = 5 U11 = 16 U12 = 8 U13 = 4 Rang d'entrée dans le cycle 4,2,1 : 13 === Temps de vol pour quelques valeurs === Temps de vol de 7 : 16 Temps de vol de 13 : 9 Temps de vol de 27 : 111 Temps de vol de 42 : 8
La conjecture de Syracuse (ou problème de Collatz) affirme que quel que soit l'entier de départ, la suite finit toujours par atteindre 1. C'est un problème non résolu à ce jour.
Limite d'une suite
Écrivez un programme permettant de calculer la limite à epsilon près de la suite définie par la relation de récurrence :
- U₀ = 2
- Uₙ₊₁ = Uₙ + 2/Uₙ , n ≥ 0
On arrête d'itérer quand l'intervalle entre deux termes consécutifs devient strictement inférieur à epsilon.
#include <stdio.h>
#include <math.h>
float limite(float epsilon)
{
float U0, Un;
U0 = 2;
Un = U0 + 2 / U0;
int iteration = 1;
printf("U0 = 2\n");
printf("U1 = %f\n", Un);
while (fabsf(Un - U0) > epsilon)
{
iteration++;
U0 = Un;
Un = U0 + 2 / U0;
printf("U%d = %f\n", iteration, Un);
}
return Un;
}
int main(void)
{
printf("Limite = %f\n", limite(0.05));
return 0;
}def limite(epsilon, afficher=True):
"""
Calcule la limite de la suite U_{n+1} = U_n + 2/U_n
avec une précision epsilon.
"""
U0 = 2
Un = U0 + 2 / U0
if afficher:
print(f"U0 = {U0:.6f}")
print(f"U1 = {Un:.6f}")
n = 1
while abs(Un - U0) > epsilon:
n += 1
U0 = Un
Un = U0 + 2 / U0
if afficher:
print(f"U{n} = {Un:.6f}")
return Un
def limite_analytique():
"""
La suite converge vers √(U0² + 4) ? Non, vérifions.
En fait, U_{n+1} - √(U_n² + 4) ? Non.
Cette suite converge vers √(2 * U0²) ? Testons.
"""
pass
# Tests
print("=== epsilon = 0.05 ===")
lim = limite(0.05)
print(f"Limite = {lim:.6f}")
print("\n=== epsilon = 0.001 ===")
lim = limite(0.001, afficher=False)
print(f"Limite = {lim:.6f}")=== epsilon = 0.05 === U0 = 2.000000 U1 = 3.000000 U2 = 3.666667 U3 = 4.212121 U4 = 4.687166 U5 = 5.113667 U6 = 5.504849 U7 = 5.868179 U8 = 6.208938 U9 = 6.531207 U10 = 6.837883 U11 = 7.131106 U12 = 7.412554 U13 = 7.683539 U14 = 7.945107 U15 = 8.198116 U16 = 8.443288 U17 = 8.681250 U18 = 8.912556 U19 = 9.137712 U20 = 9.357184 U21 = 9.571406 U22 = 9.780787 U23 = 9.985718 U24 = 10.186569 U25 = 10.383692 U26 = 10.577421 Limite = 10.577421
Cette suite semble diverger vers l'infini, pas converger vers une limite finie. En réalité, Uₙ ~ √(2n) pour les grandes valeurs.
Suite récurrente linéaire
Écrivez un programme Python permettant de calculer le n-ième terme de la suite définie par :
- F₀ = 1, F₁ = 2
- Fₙ = 4 × Fₙ₋₁ + 3 × Fₙ₋₂ (n ≥ 2)
#include <stdio.h>
int suite(int n)
{
int F0 = 1, F1 = 2, Fn;
if (n == 0) return F0;
if (n == 1) return F1;
for (int i = 2; i <= n; i++)
{
Fn = 4 * F1 + 3 * F0;
F0 = F1;
F1 = Fn;
}
return F1;
}
int suite_recursive(int n)
{
if (n == 0) return 1;
if (n == 1) return 2;
return 4 * suite_recursive(n-1) + 3 * suite_recursive(n-2);
}
int main(void)
{
printf("F5 (itératif) = %d\n", suite(5));
printf("F5 (récursif) = %d\n", suite_recursive(5));
for (int i = 0; i <= 10; i++)
printf("F%d = %d\n", i, suite(i));
return 0;
}def suite_iterative(n):
"""
Calcule le n-ième terme de la suite Fn = 4Fn-1 + 3Fn-2
Version itérative.
"""
if n == 0:
return 1
if n == 1:
return 2
F0, F1 = 1, 2
for _ in range(2, n + 1):
Fn = 4 * F1 + 3 * F0
F0, F1 = F1, Fn
return F1
def suite_recursive(n):
"""
Version récursive (peu efficace pour n grand).
"""
if n == 0:
return 1
if n == 1:
return 2
return 4 * suite_recursive(n-1) + 3 * suite_recursive(n-2)
def suite_memo(n, memo=None):
"""
Version récursive avec mémoïsation.
"""
if memo is None:
memo = {0: 1, 1: 2}
if n in memo:
return memo[n]
memo[n] = 4 * suite_memo(n-1, memo) + 3 * suite_memo(n-2, memo)
return memo[n]
# Tests
print("=== Suite itérative ===")
for i in range(11):
print(f"F{i} = {suite_iterative(i)}")
print("\n=== Suite récursive mémoïsée ===")
for i in range(11):
print(f"F{i} = {suite_memo(i)}")=== Suite itérative === F0 = 1 F1 = 2 F2 = 11 F3 = 50 F4 = 233 F5 = 1082 F6 = 5027 F7 = 23354 F8 = 108497 F9 = 504146 F10 = 2342603
Suite de Fibonacci (récursive)
La suite de Fibonacci est définie comme suit :

Écrire une fonction récursive calculant Fib(n).
#include <stdio.h>
int Fib(int n)
{
if (n < 2)
return 1; // F0 = 1, F1 = 1
else
return Fib(n - 1) + Fib(n - 2);
}
int main(void)
{
for (int i = 0; i <= 10; i++)
printf("F%d = %d\n", i, Fib(i));
return 0;
}def Fib(n):
"""
Version récursive naïve de Fibonacci.
F0 = 1, F1 = 1.
"""
if n < 2:
return 1
return Fib(n-1) + Fib(n-2)
def Fib_memo(n, memo=None):
"""
Version récursive avec mémoïsation (efficace).
"""
if memo is None:
memo = {}
if n < 2:
return 1
if n not in memo:
memo[n] = Fib_memo(n-1, memo) + Fib_memo(n-2, memo)
return memo[n]
from functools import lru_cache
@lru_cache(maxsize=None)
def Fib_cache(n):
if n < 2:
return 1
return Fib_cache(n-1) + Fib_cache(n-2)
# Tests
print("=== Fibonacci récursif (attention: lent pour n>30) ===")
for i in range(10):
print(f"F{i} = {Fib(i)}")
print("\n=== Fibonacci avec mémoïsation ===")
for i in range(20, 31, 2):
print(f"F{i} = {Fib_memo(i)}")
print("\nStatistiques du cache:", Fib_cache.cache_info())=== Fibonacci récursif (attention: lent pour n>30) === F0 = 1 F1 = 1 F2 = 2 F3 = 3 F4 = 5 F5 = 8 F6 = 13 F7 = 21 F8 = 34 F9 = 55 === Fibonacci avec mémoïsation === F20 = 10946 F22 = 46368 F24 = 196418 F26 = 832040 F28 = 3524578 F30 = 14930352
Suite récurrente d'ordre 2
Soit la suite définie par :

Écrire une fonction récursive permettant de calculer le n-ième terme de la suite.
#include <stdio.h>
int suite(int n)
{
if (n < 2)
return 1;
else
return 3 * suite(n - 1) + suite(n - 2);
}
int main(void)
{
for (int i = 0; i <= 10; i++)
printf("U%d = %d\n", i, suite(i));
return 0;
}def suite(n):
"""
Suite définie par U0=1, U1=1, Un = 3*U(n-1) + U(n-2)
Version récursive naïve.
"""
if n < 2:
return 1
return 3 * suite(n-1) + suite(n-2)
def suite_memo(n, memo=None):
"""
Version avec mémoïsation.
"""
if memo is None:
memo = {0: 1, 1: 1}
if n in memo:
return memo[n]
memo[n] = 3 * suite_memo(n-1, memo) + suite_memo(n-2, memo)
return memo[n]
def suite_iterative(n):
"""
Version itérative optimale.
"""
if n < 2:
return 1
U0, U1 = 1, 1
for _ in range(2, n + 1):
Un = 3 * U1 + U0
U0, U1 = U1, Un
return U1
# Tests
print("=== Suite récursive (naïve) ===")
for i in range(10):
print(f"U{i} = {suite(i)}")
print("\n=== Suite itérative ===")
for i in range(10):
print(f"U{i} = {suite_iterative(i)}")
print("\n=== Suite mémoïsée pour grandes valeurs ===")
print(f"U20 = {suite_memo(20)}")
print(f"U30 = {suite_memo(30)}")=== Suite récursive (naïve) === U0 = 1 U1 = 1 U2 = 4 U3 = 13 U4 = 43 U5 = 142 U6 = 469 U7 = 1549 U8 = 5116 U9 = 16897 === Suite itérative === U0 = 1 U1 = 1 U2 = 4 U3 = 13 U4 = 43 U5 = 142 U6 = 469 U7 = 1549 U8 = 5116 U9 = 16897 === Suite mémoïsée pour grandes valeurs === U20 = 165580141 U30 = 1149850263680
Suites couplées U et V
Soient u et v les deux suites définies par :

Écrire deux fonctions CalculerU(a, b, n) et CalculerV(a, b, n) pour calculer respectivement les deux termes Uₙ et Vₙ des deux suites.
#include <stdio.h>
#include <math.h>
// Déclarations mutuelles
int CalculerU(int a, int b, int n);
int CalculerV(int a, int b, int n);
int CalculerV(int a, int b, int n)
{
if (n == 0)
return b;
else
return CalculerV(a, b, n - 1) + 3 * CalculerU(a, b, n - 1);
}
int CalculerU(int a, int b, int n)
{
if (n == 0)
return a;
else
return (int)pow(CalculerU(a, b, n - 1), 2) + 2 * CalculerV(a, b, n - 1);
}
int main(void)
{
int a = 1, b = 3;
printf("Pour a=%d, b=%d\n", a, b);
for (int n = 0; n <= 3; n++)
{
printf("U%d = %d, V%d = %d\n", n, CalculerU(a, b, n), n, CalculerV(a, b, n));
}
return 0;
}def CalculerU(a, b, n):
"""
Calcule le n-ième terme de la suite U.
U0 = a, U_{n+1} = U_n² + 2V_n
"""
if n == 0:
return a
else:
return CalculerU(a, b, n-1)**2 + 2 * CalculerV(a, b, n-1)
def CalculerV(a, b, n):
"""
Calcule le n-ième terme de la suite V.
V0 = b, V_{n+1} = V_n + 3U_n
"""
if n == 0:
return b
else:
return CalculerV(a, b, n-1) + 3 * CalculerU(a, b, n-1)
def calculer_couples(a, b, n):
"""
Version itérative pour calculer les deux suites.
Retourne (Un, Vn).
"""
U, V = a, b
print(f"U0 = {U}, V0 = {V}")
for i in range(1, n + 1):
newU = U**2 + 2 * V
newV = V + 3 * U
U, V = newU, newV
print(f"U{i} = {U}, V{i} = {V}")
return U, V
# Tests
a, b = 1, 3
print("=== Version récursive ===")
for n in range(4):
print(f"U{n} = {CalculerU(a, b, n)}, V{n} = {CalculerV(a, b, n)}")
print("\n=== Version itérative ===")
calculer_couples(a, b, 3)=== Version récursive === U0 = 1, V0 = 3 U1 = 7, V1 = 6 U2 = 61, V2 = 27 U3 = 3727, V3 = 210 === Version itérative === U0 = 1, V0 = 3 U1 = 7, V1 = 6 U2 = 61, V2 = 27 U3 = 3727, V3 = 210
Puissance rapide (exponentiation rapide)
Considérons la méthode suivante pour calculer Xⁿ :

n/2 représente la division entière de n par 2.
Écrire une fonction récursive pour calculer Xⁿ.
#include <stdio.h>
int puissance(int x, int n)
{
if (n == 1)
return x;
if (n % 2 == 0)
return puissance(x, n / 2) * puissance(x, n / 2);
else
return puissance(x, n / 2) * puissance(x, n / 2) * x;
}
int main(void)
{
printf("2^10 = %d\n", puissance(2, 10));
printf("3^5 = %d\n", puissance(3, 5));
printf("5^0 = %d\n", puissance(5, 0)); // Note: notre fonction ne gère pas n=0
return 0;
}def puissance(x, n):
"""
Calcule x^n par exponentiation rapide (récursive).
"""
if n == 0:
return 1
if n == 1:
return x
if n % 2 == 0:
moitie = puissance(x, n // 2)
return moitie * moitie
else:
moitie = puissance(x, n // 2)
return moitie * moitie * x
def puissance_iterative(x, n):
"""
Version itérative de l'exponentiation rapide.
"""
resultat = 1
while n > 0:
if n % 2 == 1:
resultat *= x
x *= x
n //= 2
return resultat
# Tests
print("=== Exponentiation rapide récursive ===")
print(f"2^10 = {puissance(2, 10)}")
print(f"3^5 = {puissance(3, 5)}")
print(f"5^0 = {puissance(5, 0)}")
print("\n=== Exponentiation rapide itérative ===")
print(f"2^10 = {puissance_iterative(2, 10)}")
print(f"3^5 = {puissance_iterative(3, 5)}")
print(f"5^0 = {puissance_iterative(5, 0)}")
print("\n=== Comparaison avec pow intégré ===")
for i in range(10):
print(f"2^{i} = {puissance(2, i)} (attendu: {2**i})")=== Exponentiation rapide récursive === 2^10 = 1024 3^5 = 243 5^0 = 1 === Exponentiation rapide itérative === 2^10 = 1024 3^5 = 243 5^0 = 1 === Comparaison avec pow intégré === 2^0 = 1 (attendu: 1) 2^1 = 2 (attendu: 2) 2^2 = 4 (attendu: 4) 2^3 = 8 (attendu: 8) 2^4 = 16 (attendu: 16) 2^5 = 32 (attendu: 32) 2^6 = 64 (attendu: 64) 2^7 = 128 (attendu: 128) 2^8 = 256 (attendu: 256) 2^9 = 512 (attendu: 512)
L'exponentiation rapide a une complexité de O(log n) multiplications, contre O(n) pour la méthode naïve.
Récapitulatif
| Exercice | Fonctionnalité | Méthode clé |
|---|---|---|
| 1 - Constante de Catalan | Approximation par série alternée | Itération avec test de convergence |
| 2 - Approximation de cos(x) | Série de Taylor | Calcul des termes successifs |
| 3 - Suite de Syracuse | Conjecture de Collatz | Itération jusqu'au cycle 4,2,1 |
| 4 - Limite d'une suite | Suite divergente | Itération jusqu'à convergence |
| 5 - Suite récurrente linéaire | Calcul de termes | Itératif et récursif avec mémoïsation |
| 6 - Fibonacci | Suite classique | Récursif avec/sans mémoïsation |
| 7 - Suite d'ordre 2 | Récurrence linéaire | Récursif et itératif |
| 8 - Suites couplées | Récursivité mutuelle | Fonctions s'appelant mutuellement |
| 9 - Puissance rapide | Exponentiation rapide | Diviser pour régner |
- Les séries alternées permettent d'approcher des constantes mathématiques.
- La conjecture de Syracuse est un problème ouvert fascinant.
- La mémoïsation optimise considérablement les fonctions récursives.
- La récursivité mutuelle permet de modéliser des systèmes couplés.
- L'exponentiation rapide est un exemple classique de diviser pour régner.
Discussion (0)
Soyez le premier à laisser un commentaire !
Laisser un commentaire
Votre commentaire sera visible après modération.