Numérique et Sciences Informatiques1ère

Complexité algorithmique élémentaire
Exercices corrigés

Maîtrisez la complexité algorithmique élémentaire : notation O, analyse des algorithmes et comparaison de performances grâce à ces 5 exercices détaillés.

Concepts & Exercices
\(T(n) = O(f(n))\)
Notation de Landau
Constante
O(1)
Temps fixe
Linéaire
O(n)
Proportionnel
Quadratique
O(n²)
Croissance rapide
🎯
Définition : La complexité mesure le nombre d'opérations en fonction de la taille des données.
📏
Notation O : Bornes supérieures asymptotiques des ressources nécessaires.
📋
Types : Complexité en temps, complexité en espace.
Importance : Comparaison des algorithmes et prédiction des performances.
💡
Conseil : Concentrez-vous sur le terme dominant pour l'analyse asymptotique
🔍
Attention : Ignorez les constantes multiplicatives et les termes d'ordre inférieur
Astuce : Les boucles imbriquées augmentent la complexité exponentiellement
📋
Méthode : Comptez les opérations élémentaires en fonction de la taille d'entrée
Exercice 1
Analyse de la complexité d'une opération constante
Exercice 2
Analyse d'un algorithme linéaire
Exercice 3
Analyse d'un algorithme quadratique
Exercice 4
Comparaison de complexités entre algorithmes
Exercice 5
Application à des algorithmes classiques
Corrigé : Exercices 1 à 3
1 Complexité constante
Définition :

Complexité constante O(1) : Le temps d'exécution ne dépend pas de la taille des données.

Opérations élémentaires : Accès à un tableau par index, opérations arithmétiques.

Méthode d'analyse de la complexité :
  1. Identifier les opérations élémentaires (affectation, comparaison, calcul)
  2. Compter le nombre d'opérations en fonction de la taille d'entrée n
  3. Identifier le terme dominant (celui qui croît le plus rapidement)
  4. Exprimer la complexité en notation O en ignorant les constantes
Opération
Affectation
Exemple
x = 5
Complexité
O(1)
Étape 1 : Analyse de l'opération

Instruction : x = 5

Cette instruction réalise une seule opération d'affectation

Étape 2 : Comptage des opérations

Nombre d'opérations : 1 (indépendant de toute taille d'entrée)

Étape 3 : Expression en notation O

T(n) = 1 → O(1) (constante)

// Exemples d'opérations en O(1) x = 5 // Affectation y = a + b // Opération arithmétique tableau[3] = 10 // Accès direct à un tableau resultat = tableau[i] // Accès par index (O(1))
Complexité = O(1)
Réponse finale :

L'opération x = 5 a une complexité constante O(1) car elle s'exécute en un temps fixe.

Règles appliquées :

Complexité constante : Temps d'exécution indépendant de la taille des données

Opérations élémentaires : Affectations, calculs arithmétiques simples

Accès direct : Accès à un tableau par index est en O(1)

2 Complexité linéaire
Définition :

Complexité linéaire O(n) : Le temps d'exécution est proportionnel à la taille des données.

Caractéristique : Une boucle qui parcourt n éléments exécutant une opération O(1).

Algorithme
Somme tableau
Code
Pour i de 0 à n-1
Complexité
O(n)
Étape 1 : Analyse de l'algorithme

Algorithme de somme d'un tableau de n éléments


somme = 0
pour i de 0 à n-1:
  somme = somme + tableau[i]
retourner somme
                
Étape 2 : Comptage des opérations

Initialisation : 1 opération (O(1))

Boucle : n itérations

Par itération : 1 addition + 1 accès tableau + 1 affectation = 3 opérations O(1)

Total : 1 + n×3 = 3n + 1 opérations

Étape 3 : Expression en notation O

T(n) = 3n + 1 → O(n) (terme dominant : n)

Taille (n) Opérations Temps relatif
10 31
100 301 10×
1000 3001 100×
10000 30001 1000×
// Code Python équivalent def somme_tableau(tableau): somme = 0 # O(1) for i in range(len(tableau)): # n fois somme += tableau[i] # O(1) par itération return somme # O(1) # Complexité totale : O(n)
Complexité = O(n)
Réponse finale :

L'algorithme de somme d'un tableau a une complexité linéaire O(n).

Règles appliquées :

Complexité linéaire : Une boucle simple parcourant n éléments

Terme dominant : Dans 3n+1, c'est n qui domine pour les grandes valeurs

Proportionnalité : Le temps double quand la taille double

3 Complexité quadratique
Définition :

Complexité quadratique O(n²) : Le temps d'exécution est proportionnel au carré de la taille des données.

Caractéristique : Boucles imbriquées où chaque boucle parcourt n éléments.

Algorithme
Tri par sélection
Boucles
i=0→n-1, j=i+1→n-1
Complexité
O(n²)
Étape 1 : Analyse de l'algorithme

Algorithme de tri par sélection


pour i de 0 à n-2:
  min = i
  pour j de i+1 à n-1:
    si tableau[j] < tableau[min]:
      min = j
  échanger tableau[i] et tableau[min]
                
Étape 2 : Comptage des opérations

Boucle extérieure : (n-1) itérations

Pour chaque i, boucle intérieure : (n-1-i) itérations

Total d'itérations de la boucle intérieure : Σ(i=0 à n-2)(n-1-i) = Σ(k=1 à n-1)k = (n-1)n/2

Étape 3 : Expression en notation O

T(n) = (n-1)n/2 = (n²-n)/2 = n²/2 - n/2 → O(n²) (terme dominant : n²)

Étape 4 : Analyse détaillée

Pour n=10 : (10-1)×10/2 = 45 comparaisons

Pour n=100 : (100-1)×100/2 = 4950 comparaisons

Quand n est multiplié par 10, le nombre d'opérations est multiplié par ~100

Taille (n) Opérations Temps relatif
10 45
20 190 4.2×
50 1225 27.2×
100 4950 110×
// Code Python équivalent def tri_selection(tableau): n = len(tableau) for i in range(n-1): # O(n) min_idx = i for j in range(i+1, n): # O(n) pour chaque i if tableau[j] < tableau[min_idx]: min_idx = j tableau[i], tableau[min_idx] = tableau[min_idx], tableau[i] # Complexité totale : O(n²)
Complexité = O(n²)
Réponse finale :

L'algorithme de tri par sélection a une complexité quadratique O(n²).

Règles appliquées :

Complexité quadratique : Boucles imbriquées parcourant n éléments

Croissance rapide : Le temps augmente exponentiellement avec la taille

Optimisation : Chercher des algorithmes plus efficaces pour de grandes données

Corrigé : Exercices 4 à 5
4 Comparaison de complexités
Définition :

Comparaison d'algorithmes : Analyse des performances selon la taille des données.

Hiérarchie : O(1) < O(log n) < O(n) < O(n log n) < O(n²) < O(2ⁿ).

Algorithmes
Recherche O(n) vs O(1)
Taille
n = 1000
Opérations
1000 vs 1
Étape 1 : Hiérarchie des complexités

O(1) : Constante (meilleure)

O(log n) : Logarithmique

O(n) : Linéaire

O(n log n) : Linéarithmique

O(n²) : Quadratique

O(2ⁿ) : Exponentielle (pire)

Étape 2 : Comparaison numérique

Pour n = 1000 :

O(1) = 1 opération

O(log n) ≈ 10 opérations

O(n) = 1000 opérations

O(n²) = 1,000,000 opérations

Étape 3 : Impact sur les performances

Un algorithme O(n²) devient impraticable pour de grandes valeurs de n

Un algorithme O(n) est acceptable pour des données de taille modérée

Un algorithme O(1) est optimal en termes de performance

Étape 4 : Facteurs influençant le choix

• Taille attendue des données

• Contraintes de temps réel

• Complexité de mise en œuvre

• Mémoire disponible

Complexité n=10 n=100 n=1000 n=10000
O(1) 1 1 1 1
O(log n) ~3 ~7 ~10 ~13
O(n) 10 100 1000 10000
O(n log n) ~30 ~700 ~10000 ~130000
O(n²) 100 10000 1000000 100000000
O(1) < O(log n) < O(n) < O(n log n) < O(n²)
Réponse finale :

La hiérarchie des complexités détermine les performances relatives des algorithmes.

Règles appliquées :

Hiérarchie : Comparer les ordres de grandeur des complexités

Impact : Une différence de complexité peut entraîner des gains énormes

Choix : Équilibrer performance, simplicité et besoins spécifiques

5 Applications à des algorithmes
Définition :

Algorithmes classiques : Analyse de la complexité de structures et algorithmes courants.

Pratique : Comprendre la performance des algorithmes utilisés quotidiennement.

Algorithme
Recherche dichotomique
Structure
Tableau trié
Complexité
O(log n)
Étape 1 : Recherche linéaire

Complexité : O(n)

Algorithme : Parcourir le tableau élément par élément

Applicable à : Tableaux non triés

Étape 2 : Recherche dichotomique

Complexité : O(log n)

Algorithme : Diviser l'espace de recherche par 2 à chaque étape

Condition : Le tableau doit être trié

Étape 3 : Tri par insertion

Complexité : O(n²) dans le pire cas

Algorithme : Insérer chaque élément à sa place dans la partie triée

Avantage : Efficace pour de petites tailles

Étape 4 : Tri fusion

Complexité : O(n log n)

Algorithme : Diviser pour régner - diviser, trier, fusionner

Avantage : Garantie O(n log n) dans tous les cas

Algorithme Type Meilleur cas Pire cas Utilisation
Recherche linéaire Recherche O(1) O(n) Tableau non trié
Recherche dichotomique Recherche O(1) O(log n) Tableau trié
Tri par insertion Tri O(n) O(n²) Petites données
Tri fusion Tri O(n log n) O(n log n) Grandes données
Tri rapide Tri O(n log n) O(n²) Général
// Recherche dichotomique en Python def recherche_dichotomique(tableau, cible): debut = 0 fin = len(tableau) - 1 while debut <= fin: milieu = (debut + fin) // 2 if tableau[milieu] == cible: return milieu elif tableau[milieu] < cible: debut = milieu + 1 else: fin = milieu - 1 return -1 # Non trouvé # Complexité : O(log n)
O(1)
O(log n)
O(n)
O(n log n)
O(n²)
Recherche dichotomique = O(log n) vs O(n)
Réponse finale :

La connaissance de la complexité permet de choisir le bon algorithme pour chaque situation.

Règles appliquées :

Contexte : Choisir l'algorithme en fonction des contraintes

Structure : La complexité dépend souvent de la structure de données

Compromis : Équilibre entre complexité, lisibilité et besoins spécifiques

Cours bien détaillé
\(T(n) = O(f(n)) \Leftrightarrow \exists c > 0, n_0 \geq 0 : \forall n \geq n_0, T(n) \leq c \cdot f(n)\)
Définition formelle de la notation O
🎯
Définition : La complexité mesure le nombre d'opérations en fonction de la taille des données.
📏
Notation O : Bornes supérieures asymptotiques des ressources nécessaires.
📋
Types : Complexité en temps, complexité en espace.
Importance : Comparaison des algorithmes et prédiction des performances.
💡
Conseil : Concentrez-vous sur le terme dominant pour l'analyse asymptotique
🔍
Attention : Ignorez les constantes multiplicatives et les termes d'ordre inférieur
Astuce : Les boucles imbriquées augmentent la complexité exponentiellement
📋
Méthode : Comptez les opérations élémentaires en fonction de la taille d'entrée
Vérification : Testez avec différentes tailles pour observer la tendance
🔄
Amélioration : Cherchez des algorithmes avec des complexités inférieures
Hiérarchie des complexités courantes :
  • O(1) - Constante : Temps fixe, indépendant de la taille d'entrée
  • O(log n) - Logarithmique : Temps proportionnel au logarithme de la taille
  • O(n) - Linéaire : Temps proportionnel à la taille d'entrée
  • O(n log n) - Linéarithmique : Fréquent dans les algorithmes de tri efficaces
  • O(n²) - Quadratique : Courant dans les algorithmes avec boucles imbriquées
  • O(2ⁿ) - Exponentielle : Algorithmes récursifs sans optimisation
Règles importantes :
  • La complexité mesure le comportement asymptotique pour des grandes tailles d'entrée
  • Les constantes multiplicatives sont ignorées dans la notation O
  • Le terme dominant détermine la complexité globale
  • Les algorithmes avec des complexités inférieures sont préférables
Meilleure
O(1)
Constante
Courante
O(n)
Linéaire
Pire
O(2ⁿ)
Exponentielle
\(\lim_{n \to \infty} \frac{T(n)}{f(n)} = c\)
Relation asymptotique
Points clés à retenir :
  • • La complexité algorithmique mesure l'efficacité des algorithmes en fonction de la taille des données
  • • La notation O exprime les bornes supérieures du comportement asymptotique
  • • La hiérarchie des complexités détermine la performance relative des algorithmes
  • • Le choix de l'algorithme dépend du contexte, des contraintes et des tailles de données
  • • Comprendre la complexité permet d'anticiper les performances et d'optimiser les programmes
Complexité algorithmique élémentaire Algorithmes classiques