Algorithmique et programmation1ère

Analyse de complexité simple
Exercices corrigés

Maîtrisez l'analyse de complexité : notation O(n), algorithmes linéaires, quadratiques, logarithmiques et exponentiels grâce à ces 5 exercices détaillés.

Concepts & Exercices
\(O(f(n))\)
Notation de Landau
Constante
\(O(1)\)
Temps constant
Linéaire
\(O(n)\)
Proportionnel à n
Quadratique
\(O(n^2)\)
Croissance quadratique
Logarithmique
\(O(\log n)\)
Croissance logarithmique
Exponentielle
\(O(2^n)\)
Croissance exponentielle
🎯
Définition : La complexité mesure le nombre d'opérations élémentaires en fonction de la taille des données.
📊
Notation O : Donne un majorant asymptotique du temps d'exécution.
📈
Classes : O(1) < O(log n) < O(n) < O(n log n) < O(n²) < O(2ⁿ).
Importance : Permet de comparer l'efficacité des algorithmes.
Exercice 1
Analyser la complexité de la fonction somme(n)
Exercice 2
Complexité d'un algorithme de tri à bulles
Exercice 3
Recherche binaire dans un tableau trié
Exercice 4
Calcul de la suite de Fibonacci récursive
Exercice 5
Multiplication de matrices carrées
Corrigé : Exercices 1 à 3
1 Somme des entiers
Définition :

Complexité algorithmique : Mesure de la quantité de ressources (temps ou espace) nécessaire à un algorithme.

Méthode d'analyse :
  1. Identifier les opérations élémentaires
  2. Compter leur nombre en fonction de la taille des données
  3. Exprimer la complexité en notation O
fonction somme(n):
  resultat ← 0
  pour i de 1 à n:
    resultat ← resultat + i
  retourner resultat
Initialisation
O(1)
Boucle
n fois O(1)
Total
O(n)
Étape 1 : Analyse des opérations

L'instruction resultat ← 0 s'exécute 1 fois : O(1)

La boucle pour s'exécute n fois

Chaque itération contient une addition : O(1)

Étape 2 : Calcul du total

O(1) + n × O(1) = O(1) + O(n) = O(n)

Étape 3 : Simplification

On néglige les constantes et termes d'ordre inférieur

Complexité : O(n) - Linéaire
Réponse finale :

La complexité temporelle de la fonction somme(n) est O(n).

Règles appliquées :

Boucles simples : Si une boucle s'exécute n fois, complexité O(n)

Opérations élémentaires : Assignations, additions, comparaisons sont O(1)

Simplification : On garde le terme dominant dans la notation O

2 Tri à bulles
Définition :

Tri à bulles : Algorithme de tri qui compare les éléments adjacents et les échange si nécessaire.

fonction triBulle(tableau, n):
  pour i de 0 à n-2:
    pour j de 0 à n-2-i:
      si tableau[j] > tableau[j+1]:
        echanger(tableau[j], tableau[j+1])
Boucle externe
O(n)
Boucle interne
O(n)
Total
O(n²)
Étape 1 : Analyse de la boucle externe

La boucle externe s'exécute (n-1) fois : O(n)

Étape 2 : Analyse de la boucle interne

La boucle interne s'exécute environ n-i fois pour chaque i

Moyenne sur toutes les itérations : ≈ n/2 fois

Étape 3 : Calcul total

O(n) × O(n) = O(n²)

Complexité : O(n²) - Quadratique
Réponse finale :

La complexité temporelle du tri à bulles est O(n²).

Règles appliquées :

Boucles imbriquées : Multiplier les complexités des boucles

Complexité quadratique : Se produit souvent avec des algorithmes à deux dimensions

Comparaison : O(n²) devient inefficace pour grandes valeurs de n

3 Recherche binaire
Définition :

Recherche binaire : Algorithme qui divise l'espace de recherche par 2 à chaque étape.

fonction rechercheBinaire(tableau, element):
  debut ← 0
  fin ← taille(tableau) - 1
  tant que debut ≤ fin:
    milieu ← (debut + fin) / 2
    si tableau[milieu] = element:
      retourner milieu
    sinon si tableau[milieu] < element:
      debut ← milieu + 1
    sinon:
      fin ← milieu - 1
  retourner -1
Initialisation
O(1)
Itérations
log₂(n)
Total
O(log n)
Étape 1 : Principe de la recherche binaire

À chaque itération, on divise l'espace de recherche par 2

Nombre d'étapes nécessaires : log₂(n)

Étape 2 : Opérations par itération

Calcul du milieu, comparaison, mise à jour des bornes : O(1)

Étape 3 : Calcul total

log₂(n) × O(1) = O(log n)

Complexité : O(log n) - Logarithmique
Réponse finale :

La complexité temporelle de la recherche binaire est O(log n).

Règles appliquées :

Division par 2 : Chaque étape divise l'espace de recherche par 2 → O(log n)

Condition préalable : Le tableau doit être trié

Efficacité : Très efficace comparé à la recherche linéaire O(n)

Corrigé : Exercices 4 à 5
4 Fibonacci récursif
Définition :

Suite de Fibonacci : F(0)=0, F(1)=1, F(n)=F(n-1)+F(n-2) pour n≥2.

fonction fibonacci(n):
  si n ≤ 1:
    retourner n
  retourner fibonacci(n-1) + fibonacci(n-2)
Arbre d'appel
2^n
Calculs redondants
Oui
Total
O(2^n)
Étape 1 : Construction de l'arbre d'appel

Pour fibonacci(n), on appelle fibonacci(n-1) et fibonacci(n-2)

Chaque appel génère 2 nouveaux appels, sauf pour les cas de base

Étape 2 : Analyse de l'arbre

Hauteur de l'arbre : n

Nombre approximatif de noeuds : 2^n

Cela donne une complexité exponentielle

Étape 3 : Problème de redondance

fibonacci(k) est calculé plusieurs fois pour k < n

Cela rend l'algorithme très inefficace

Complexité : O(2^n) - Exponentielle
Réponse finale :

La complexité temporelle de la version récursive de Fibonacci est O(2^n).

Règles appliquées :

Récursion sans mémoïsation : Peut conduire à des calculs redondants

Complexité exponentielle : Très inefficace pour grandes valeurs de n

Amélioration : Utiliser la programmation dynamique pour O(n)

5 Multiplication de matrices
Définition :

Multiplication de matrices : Produit de deux matrices carrées de taille n×n.

fonction multiplierMatrices(A, B, n):
  C ← matrice vide n×n
  pour i de 0 à n-1:
    pour j de 0 à n-1:
      C[i][j] ← 0
      pour k de 0 à n-1:
        C[i][j] ← C[i][j] + A[i][k] * B[k][j]
  retourner C
Boucle i
O(n)
Boucle j
O(n)
Boucle k
O(n)
Total
O(n³)
Étape 1 : Analyse des boucles imbriquées

3 boucles imbriquées, chacune exécutée n fois

Complexité : O(n) × O(n) × O(n) = O(n³)

Étape 2 : Calcul pour chaque élément

Chaque élément C[i][j] nécessite n multiplications et additions

Total : n² éléments × n opérations = n³ opérations

Étape 3 : Conclusion

Algorithme classique de multiplication de matrices : O(n³)

Complexité : O(n³) - Cubique
Réponse finale :

La complexité temporelle de la multiplication de matrices carrées est O(n³).

Règles appliquées :

Boucles imbriquées : Multiplier les complexités de chaque niveau

Algorithmes avancés : Strassen propose O(n^2.807)

Complexité cubique : Inefficace pour grandes matrices

Cours bien détaillé
\(T(n) = O(f(n))\)
Notation O de Landau
🎯
Définition : La complexité mesure le comportement asymptotique d'un algorithme.
📊
Notation O : O(f(n)) signifie que T(n) ≤ c×f(n) pour n suffisamment grand.
📐
Hiérarchie : O(1) < O(log n) < O(n) < O(n log n) < O(n²) < O(2ⁿ).
📝
Application : Comparaison d'efficacité, choix d'algorithmes, optimisation.
Constante
O(1)
Accès direct, affectation
Logarithmique
O(log n)
Recherche dichotomique
Linéaire
O(n)
Parcours de tableau
Linéarithmique
O(n log n)
Tri optimal (fusion, rapide)
Quadratique
O(n²)
Tri naïf, double boucle
Polynomiale
O(nᵏ)
Triple boucle (cubique)
Exponentielle
O(2ⁿ)
Problèmes combinatoires
💡
Conseil : Toujours chercher l'algorithme le plus efficace pour le problème
🔍
Attention : Les constantes sont négligées dans la notation O
Astuce : Pour les boucles imbriquées, multiplier les complexités
📋
Méthode : Compter les opérations élémentaires en fonction de n
Vérification : Tester avec différentes tailles de données
🔄
Optimisation : Utiliser la mémoïsation pour éviter calculs redondants
Méthodes d'analyse :
  • Top-down : Analyser le code ligne par ligne
  • Bottom-up : Comprendre la structure de l'algorithme
  • Théorème maître : Pour les récurrences de la forme T(n) = aT(n/b) + f(n)
  • Arbre récursif : Pour visualiser les appels récursifs
Règles importantes :
  • On néglige les constantes multiplicatives et les termes d'ordre inférieur
  • Seul le terme dominant est conservé dans la notation O
  • La complexité spatiale mesure l'espace mémoire utilisé
  • Un algorithme polynomial est généralement préférable à un exponentiel
Analyse de complexité simple Applications de programmation