Éducation nationale françaiseOption Mathématiques complémentairesTerminale générale29 min de lecture

Algorithmique et programmation

Une version article du chapitre pour comprendre l'essentiel rapidement, vérifier si le niveau correspond, puis basculer vers Wilo pour la pratique guidée et le suivi.

Lecture

6 chapitres

Un parcours éditorialisé et navigable.

Pratique

12 questions

Quiz et cartes mémoire à ouvrir après la lecture.

Objectif

Terminale générale

Format rapide pour vérifier si le chapitre correspond.

Chapitre 1

Introduction à l'Algorithmique

Qu'est-ce qu'un algorithme ?

Un algorithme est une séquence finie et non ambiguë d'instructions qui, une fois exécutées, permettent de résoudre un problème donné ou d'accomplir une tâche spécifique. Imaginez une recette de cuisine : c'est un algorithme pour préparer un plat !

Caractéristiques principales d'un algorithme :

  • Fini : Il doit toujours se terminer après un nombre fini d'étapes. Pas de boucles infinies !
  • Déterministe : Pour les mêmes entrées, il doit toujours produire les mêmes sorties. Le résultat est prévisible.
  • Clair et non ambigu : Chaque instruction doit être comprise sans interprétation possible.
  • Efficace : Il doit résoudre le problème en un temps raisonnable et avec des ressources limitées (même si ce n'est pas toujours le plus rapide ou le plus court).

Exemples d'algorithmes du quotidien :

  • Suivre un itinéraire GPS.
  • Préparer une tasse de café.
  • Trier des cartes par ordre croissant.
  • Rechercher un mot dans un dictionnaire.

Représentation d'un algorithme

Pour exprimer un algorithme, on peut utiliser différentes méthodes :

  • Langage naturel : C'est la manière la plus simple, mais elle peut manquer de précision. Utile pour une première ébauche.
    • Exemple : "Pour trouver le plus grand nombre dans une liste, je parcours chaque nombre, et si je trouve un nombre plus grand que le plus grand que j'ai vu jusqu'à présent, je le retiens."
  • Pseudo-code : C'est un langage intermédiaire entre le langage naturel et un langage de programmation. Il utilise des structures de contrôle (SI... ALORS, TANT QUE, POUR) mais sans se soucier de la syntaxe exacte d'un langage précis. Il est très utilisé pour concevoir des algorithmes.
    • Exemple :
      FONCTION trouver_max(liste_nombres) :
          max_actuel = premier élément de liste_nombres
          POUR CHAQUE nombre DANS liste_nombres :
              SI nombre > max_actuel ALORS :
                  max_actuel = nombre
          RETOURNER max_actuel
      
  • Organigrammes : Ce sont des représentations graphiques de l'algorithme, utilisant des symboles standards pour les opérations, les décisions, etc. Ils sont moins utilisés en Terminale mais la notion est utile.
    • Symboles de base :
      • Ovale : Début/Fin
      • Parallélogramme : Entrée/Sortie (lecture/affichage)
      • Rectangle : Traitement/Opération
      • Losange : Décision (condition SI... ALORS)
      • Flèches : Sens d'exécution

Variables et types de données

En programmation, une variable est un espace de stockage nommé en mémoire qui peut contenir une valeur. Cette valeur peut changer au cours de l'exécution du programme.

Définition d'une variable : On peut voir une variable comme une boîte étiquetée. L'étiquette est le nom de la variable, et la boîte contient une valeur.

Types de données courants : Le type d'une variable détermine la nature des valeurs qu'elle peut stocker et les opérations que l'on peut effectuer dessus.

  • Entier (int) : Nombres entiers (positifs, négatifs, zéro).
    • Exemples : 5, -10, 0
  • Flottant (float) : Nombres décimaux (à virgule).
    • Exemples : 3.14, -0.5, 1.0
  • Chaîne de caractères (str) : Séquences de caractères (texte). Elles sont toujours entourées de guillemets simples ou doubles.
    • Exemples : "Bonjour", 'Python', "123" (c'est du texte, pas un nombre !)
  • Booléen (bool) : Représente une valeur de vérité, soit True (vrai), soit False (faux).
    • Exemples : True, False

Affectation de valeurs : L'opérateur d'affectation est =. Il permet de donner une valeur à une variable.

age = 18             # age est un entier
taille = 1.75        # taille est un flottant
nom = "Alice"        # nom est une chaîne de caractères
est_majeur = True    # est_majeur est un booléen

Opérateurs et expressions

Les opérateurs sont des symboles qui réalisent des opérations sur des opérandes (valeurs ou variables). Une expression est une combinaison d'opérateurs et d'opérandes qui produit une valeur.

Opérateurs arithmétiques : Pour les calculs numériques.

OpérateurDescriptionExemple PythonRésultat
+Addition5 + 38
-Soustraction10 - 46
*Multiplication2 * 612
/Division15 / 35.0
//Division entière15 // 43
%Modulo (reste)15 % 43
**Puissance2 ** 38

Opérateurs de comparaison : Pour comparer des valeurs. Ils retournent un booléen (True ou False).

OpérateurDescriptionExemple PythonRésultat
<mark>Égal à5 </mark> 5True
!=Différent de5 != 3True
<Inférieur à5 < 3False
>Supérieur à5 > 3True
<=Inférieur ou égal à5 <= 5True
>=Supérieur ou égal à5 >= 3True

Opérateurs logiques : Pour combiner des expressions booléennes.

  • and (ET) : True si toutes les conditions sont vraies.
    • Exemple : (age > 18) and (permis <mark> True)
  • or (OU) : True si au moins une des conditions est vraie.
    • Exemple : (jour </mark> "Samedi") or (jour == "Dimanche")
  • not (NON) : Inverse la valeur de vérité.
    • Exemple : not (est_majeur) (si est_majeur est True, not est_majeur est False)

Chapitre 2

Structures de Contrôle Fondamentales

Séquence d'instructions

La séquence d'instructions est la structure la plus simple : les instructions sont exécutées les unes après les autres, dans l'ordre où elles apparaissent dans le code, du haut vers le bas. C'est l'exécution linéaire par défaut.

  • Ordre des opérations : L'ordre est crucial. Changer l'ordre peut modifier le résultat.
    a = 10
    b = 5
    somme = a + b  # Instruction 1
    a = 20         # Instruction 2
    print(somme)   # Affiche 15, car somme a été calculée avant que 'a' ne change
    
  • Blocs d'instructions : En Python, les blocs d'instructions sont définis par l'indentation (espaces ou tabulations au début de la ligne). Toutes les instructions d'un même bloc doivent avoir le même niveau d'indentation.

Structures conditionnelles (Si... Alors... Sinon)

Les structures conditionnelles permettent d'exécuter différentes parties du code en fonction de la véracité d'une condition.

  • Condition simple (if) :
    age = 20
    if age >= 18:
        print("Vous êtes majeur.") # Ce bloc est exécuté si la condition est vraie
    print("Fin du programme.")
    
  • Condition avec alternative (if... else) :
    temperature = 25
    if temperature > 30:
        print("Il fait très chaud !")
    else:
        print("La température est agréable.") # Ce bloc est exécuté si la condition est fausse
    
  • Conditions multiples (if... elif... else) : Pour gérer plusieurs cas. elif est l'abréviation de "else if".
    note = 12
    if note >= 16:
        print("Très bien")
    elif note >= 12: # Cette condition est testée SEULEMENT si la première est fausse
        print("Bien")
    elif note >= 10:
        print("Passable")
    else:
        print("Insuffisant")
    
  • Imbrication de conditions : On peut placer une structure conditionnelle à l'intérieur d'une autre. L'indentation est essentielle.
    heure = 14
    ouvert = True
    if ouvert:
        if heure >= 9 and heure <= 18:
            print("Le magasin est ouvert.")
        else:
            print("Le magasin est fermé (en dehors des heures d'ouverture).")
    else:
        print("Le magasin est fermé.")
    

Boucles bornées (Pour)

Les boucles bornées (ou boucles for) sont utilisées lorsque l'on sait à l'avance combien de fois une suite d'instructions doit être répétée.

  • Itération sur une plage de valeurs : En Python, la fonction range() est très utile pour générer des séquences de nombres.

    • range(n) : génère des nombres de 0 à n-1.
    • range(début, fin) : génère des nombres de début à fin-1.
    • range(début, fin, pas) : génère des nombres de début à fin-1 avec un certain pas.
  • Exemple avec range() :

    # Compter de 0 à 4
    for i in range(5):
        print(i) # Affiche 0, 1, 2, 3, 4
    
    # Compter de 1 à 5
    for j in range(1, 6):
        print(j) # Affiche 1, 2, 3, 4, 5
    
    # Compter de 0 à 10 par pas de 2
    for k in range(0, 11, 2):
        print(k) # Affiche 0, 2, 4, 6, 8, 10
    
  • Compteur de boucle : La variable i (ou j, k dans les exemples ci-dessus) est le compteur de boucle. Elle prend successivement chaque valeur de la séquence générée.

Boucles non bornées (Tant que)

Les boucles non bornées (ou boucles while) sont utilisées lorsque l'on ne sait pas à l'avance combien de fois la boucle doit s'exécuter. La répétition continue tant qu'une certaine condition est vraie.

  • Condition de continuation : La boucle s'exécute tant que la condition spécifiée est True. Dès qu'elle devient False, la boucle s'arrête.
    compteur = 0
    while compteur < 5: # La boucle continue tant que compteur est inférieur à 5
        print(compteur)
        compteur = compteur + 1 # Incrémentation essentielle pour que la condition devienne fausse un jour
    print("Boucle terminée.")
    
  • Risque de boucle infinie : Si la condition de continuation ne devient jamais fausse, la boucle ne s'arrête jamais. C'est une erreur logique courante !
    # Exemple de boucle infinie (NE PAS EXÉCUTER SANS SAVOIR COMMENT L'ARRÊTER !)
    # x = 0
    # while x < 5:
    #     print(x) # x ne change jamais, donc x < 5 est toujours vrai
    
  • Utilisation de while en Python :
    mot_de_passe_correct = "secret"
    mdp_saisi = ""
    while mdp_saisi != mot_de_passe_correct:
        mdp_saisi = input("Veuillez saisir le mot de passe : ")
        if mdp_saisi != mot_de_passe_correct:
            print("Mot de passe incorrect. Réessayez.")
    print("Accès autorisé !")
    

Chapitre 3

Fonctions et Modularité

Définition et appel de fonctions

Une fonction est un bloc de code réutilisable qui effectue une tâche spécifique. Elle permet de découper un programme complexe en parties plus petites et gérables, améliorant la lisibilité et la maintenance.

  • Rôle d'une fonction :

    • Réutilisabilité : Éviter de répéter le même code.
    • Modularité : Diviser un problème en sous-problèmes.
    • Lisibilité : Rendre le code plus facile à comprendre.
    • Maintenance : Simplifier les modifications (un bug corrigé à un seul endroit).
  • Syntaxe de définition (déclaration) en Python : On utilise le mot-clé def, suivi du nom de la fonction, de parenthèses (pour les paramètres) et d'un double point :. Le corps de la fonction est indenté.

    def saluer(): # Définition de la fonction saluer sans paramètres
        print("Bonjour !")
        print("Bienvenue.")
    
  • Appel de fonction : Pour exécuter le code d'une fonction, il faut l'appeler par son nom, suivi de parenthèses.

    saluer() # Appel de la fonction saluer
    # Affiche :
    # Bonjour !
    # Bienvenue.
    

Paramètres et arguments

  • Paramètres : Ce sont les variables définies dans la déclaration de la fonction, entre les parenthèses. Ils agissent comme des placeholders pour les valeurs que la fonction recevra.

  • Arguments : Ce sont les valeurs réelles que l'on passe à la fonction lors de son appel. Ces valeurs sont affectées aux paramètres correspondants.

  • Passage de paramètres :

    def afficher_message(nom): # 'nom' est un paramètre
        print(f"Bonjour, {nom} !")
    
    afficher_message("Alice") # "Alice" est l'argument passé au paramètre 'nom'
    afficher_message("Bob")   # "Bob" est un autre argument
    
  • Arguments par position et par mot-clé :

    • Par position : Les arguments sont appariés aux paramètres dans l'ordre où ils sont définis.
      def addition(a, b):
          return a + b
      resultat = addition(5, 3) # 5 est pour 'a', 3 est pour 'b'
      print(resultat) # Affiche 8
      
    • Par mot-clé : On spécifie le nom du paramètre lors de l'appel. L'ordre n'a alors pas d'importance.
      def afficher_info(nom, age):
          print(f"{nom} a {age} ans.")
      afficher_info(age=30, nom="Carla") # L'ordre des arguments n'importe plus
      # Affiche : Carla a 30 ans.
      
  • Valeurs par défaut des paramètres : On peut définir une valeur par défaut pour un paramètre. Si l'argument correspondant n'est pas fourni lors de l'appel, la valeur par défaut est utilisée.

    def saluer_personne(nom="invité"): # 'invité' est la valeur par défaut pour 'nom'
        print(f"Bonjour, {nom} !")
    
    saluer_personne("David") # Affiche : Bonjour, David !
    saluer_personne()        # Affiche : Bonjour, invité !
    

Valeurs de retour

Une fonction peut calculer un résultat et le renvoyer à l'endroit où elle a été appelée.

  • Instruction return : Utilisée pour spécifier la valeur que la fonction doit renvoyer. Dès qu'un return est exécuté, la fonction s'arrête et renvoie la valeur.
    def carre(nombre):
        return nombre * nombre # La fonction renvoie le carré du nombre
    
    resultat_carre = carre(4) # La valeur 16 est renvoyée et stockée dans resultat_carre
    print(resultat_carre)     # Affiche 16
    print(carre(5))           # Affiche 25
    
  • Fonctions sans retour explicite : Si une fonction ne contient pas d'instruction return, elle renvoie implicitement None.
    def afficher_bonjour():
        print("Bonjour")
    
    valeur = afficher_bonjour()
    print(valeur) # Affiche "Bonjour", puis "None"
    
  • Retour de plusieurs valeurs (tuple) : En Python, une fonction peut renvoyer plusieurs valeurs en les regroupant dans un tuple.
    def obtenir_coordonnees():
        x = 10
        y = 20
        return x, y # Renvoie un tuple (10, 20)
    
    pos_x, pos_y = obtenir_coordonnees() # Déballage du tuple dans deux variables
    print(f"X: {pos_x}, Y: {pos_y}") # Affiche : X: 10, Y: 20
    

Portée des variables (locale et globale)

La portée d'une variable détermine où elle est accessible dans un programme.

  • Variables locales : Une variable définie à l'intérieur d'une fonction est locale à cette fonction. Elle n'est accessible qu'à l'intérieur de celle-ci. Elle est créée lorsque la fonction est appelée et détruite lorsque la fonction se termine.
    def ma_fonction():
        variable_locale = 10 # Variable locale
        print(variable_locale)
    
    ma_fonction() # Affiche 10
    # print(variable_locale) # Générerait une erreur : NameError, variable_locale n'est pas définie ici
    
  • Variables globales : Une variable définie en dehors de toute fonction (au niveau principal du script) est globale. Elle est accessible de partout dans le programme, y compris à l'intérieur des fonctions.
    variable_globale = 100 # Variable globale
    
    def ma_fonction_globale():
        print(variable_globale) # Accès à la variable globale
    
    ma_fonction_globale() # Affiche 100
    print(variable_globale) # Affiche 100
    
  • Impact sur la modularité et la lisibilité :
    • Il est généralement préférable d'utiliser des variables locales et de passer les données nécessaires aux fonctions via des paramètres, et de récupérer les résultats via return. Cela rend les fonctions plus indépendantes (modulaires) et plus faciles à tester.
    • L'utilisation excessive de variables globales peut rendre le code difficile à suivre et à déboguer, car n'importe quelle partie du programme peut modifier une variable globale, ce qui peut avoir des effets inattendus.

Chapitre 4

Structures de Données Simples

Listes (tableaux dynamiques)

Une liste est une collection ordonnée et modifiable d'éléments. Les éléments d'une liste n'ont pas besoin d'être du même type.

  • Définition et création d'une liste : Les listes sont définies par des crochets [] et les éléments sont séparés par des virgules.
    ma_liste = [10, 20, 30, 40]
    noms = ["Alice", "Bob", "Charlie"]
    melange = [1, "deux", 3.0, True]
    liste_vide = []
    
  • Accès aux éléments (indexation) : Les éléments d'une liste sont indicés à partir de 0 pour le premier élément. On utilise les crochets [] pour accéder à un élément.
    • ma_liste[0] : premier élément
    • ma_liste[len(ma_liste) - 1] ou ma_liste[-1] : dernier élément
    fruits = ["pomme", "banane", "cerise"]
    print(fruits[0])  # Affiche "pomme"
    print(fruits[2])  # Affiche "cerise"
    print(fruits[-1]) # Affiche "cerise" (le dernier élément)
    
  • Opérations courantes :
    • Ajout (append(), insert()) :
      fruits.append("datte")      # Ajoute à la fin : ["pomme", "banane", "cerise", "datte"]
      fruits.insert(1, "orange")  # Insère à l'index 1 : ["pomme", "orange", "banane", "cerise", "datte"]
      
    • Suppression (remove(), pop(), del) :
      fruits.remove("banane")     # Supprime la première occurrence de "banane"
      dernier_fruit = fruits.pop() # Supprime et renvoie le dernier élément (par défaut)
      del fruits[0]               # Supprime l'élément à l'index 0
      
    • Modification :
      fruits[0] = "abricot" # Remplace "pomme" par "abricot"
      
    • Longueur (len()) :
      print(len(fruits)) # Affiche le nombre d'éléments
      

Parcours de listes

Parcourir une liste signifie accéder à chacun de ses éléments, généralement pour effectuer une opération sur eux.

  • Boucle for avec index : Utile si on a besoin de l'index de l'élément.
    nombres = [10, 20, 30]
    for i in range(len(nombres)):
        print(f"L'élément à l'index {i} est {nombres[i]}")
    
  • Boucle for sur les éléments : Plus idiomatique en Python, simple et élégant quand on n'a pas besoin de l'index.
    noms = ["Alice", "Bob", "Charlie"]
    for nom in noms:
        print(f"Bonjour, {nom} !")
    
  • Méthodes de parcours (enumerate) : Si on a besoin à la fois de l'index et de l'élément.
    couleurs = ["rouge", "vert", "bleu"]
    for index, couleur in enumerate(couleurs):
        print(f"La couleur {couleur} est à la position {index}.")
    

Chaînes de caractères

Une chaîne de caractères est une séquence ordonnée et immuable de caractères. "Immuable" signifie qu'une fois créée, une chaîne ne peut pas être modifiée. Toute opération qui semble la modifier crée en fait une nouvelle chaîne.

  • Définition et propriétés :
    message = "Hello World!"
    print(message[0])   # Accès par index : Affiche 'H'
    print(len(message)) # Longueur : Affiche 12
    # message[0] = 'h'  # Erreur ! TypeError: 'str' object does not support item assignment (immuabilité)
    
  • Opérations courantes :
    • Concaténation (+) :
      prenom = "Jean"
      nom = "Dupont"
      nom_complet = prenom + " " + nom
      print(nom_complet) # Affiche "Jean Dupont"
      
    • Découpage (slicing) : Permet d'extraire une sous-chaîne. [début:fin:pas]
      • message[0:5] : du début jusqu'à l'index 4 (exclut l'index 5)
      • message[6:] : de l'index 6 jusqu'à la fin
      • message[:5] : du début jusqu'à l'index 4
      • message[::2] : tous les deux caractères
      • message[::-1] : inverse la chaîne
      phrase = "Python est amusant"
      print(phrase[0:6])  # Affiche "Python"
      print(phrase[10:])  # Affiche "amusant"
      print(phrase[::-1]) # Affiche "tnasuma tse nohtyP"
      
  • Méthodes de chaînes : Python offre de nombreuses méthodes intégrées pour manipuler les chaînes.
    • str.upper(), str.lower() : convertir en majuscules/minuscules
    • str.strip() : supprimer les espaces blancs en début et fin
    • str.split(separateur) : diviser une chaîne en une liste de sous-chaînes
    • str.replace(ancien, nouveau) : remplacer des occurrences
    • str.find(sous_chaine) : trouver l'index de la première occurrence (retourne -1 si non trouvé)
    • str.count(sous_chaine) : compter le nombre d'occurrences
    texte = "   Hello Python   "
    print(texte.strip().upper()) # Affiche "HELLO PYTHON"
    mots = texte.strip().split(" ")
    print(mots) # Affiche ['Hello', 'Python']
    

Tuples

Un tuple est une collection ordonnée et immuable d'éléments. La principale différence avec les listes est l'immuabilité : une fois créé, un tuple ne peut pas être modifié (ajout, suppression, modification d'éléments).

  • Définition et propriétés : Les tuples sont définis par des parenthèses () (souvent optionnelles si les virgules sont présentes).

    mon_tuple = (1, 2, 3)
    couleurs_rgb = (255, 0, 0) # Un tuple pour représenter une couleur RGB
    point = 5, 10              # Les parenthèses sont souvent omises
    tuple_un_element = (42,)   # VIRGULE ESSENTIELLE pour un tuple à un seul élément
    
    • Accès par index : Comme les listes. mon_tuple[0] -> 1
    • Longueur : len(mon_tuple) -> 3
    • Immuabilité : On ne peut pas faire mon_tuple[0] = 5.
  • Cas d'utilisation :

    • Lorsque la collection d'éléments ne doit pas changer (ex: coordonnées d'un point, date de naissance).
    • Pour retourner plusieurs valeurs d'une fonction (comme vu précédemment).
    • Comme clés dans des dictionnaires (les listes ne peuvent pas être des clés de dictionnaire car elles sont modifiables).
  • Différences avec les listes :

CaractéristiqueListe ([])Tuple (())
MutabilitéModifiable (mutable)Non modifiable (immuable)
SyntaxeCrochets []Parenthèses ()
PerformanceLégèrement plus lentGénéralement plus rapide
UtilisationCollections d'éléments variablesCollections d'éléments fixes

Chapitre 5

Algorithmes de Traitement et de Recherche

Algorithmes de tri (introduction)

Les algorithmes de tri permettent d'organiser les éléments d'une liste (ou d'un tableau) selon un certain ordre (croissant, décroissant).

  • Principe du tri par sélection :

    1. Trouver le plus petit élément de la partie non triée de la liste.
    2. L'échanger avec le premier élément de la partie non triée.
    3. Répéter l'opération sur la partie restante non triée jusqu'à ce que toute la liste soit triée.
    • Idée : Sélectionner le minimum et le placer au bon endroit.
    def tri_selection(liste):
        n = len(liste)
        for i in range(n):
            # Trouver l'indice du minimum dans la partie non triée
            min_idx = i
            for j in range(i + 1, n):
                if liste[j] < liste[min_idx]:
                    min_idx = j
            # Échanger le minimum trouvé avec l'élément courant
            liste[i], liste[min_idx] = liste[min_idx], liste[i]
        return liste
    
  • Principe du tri par insertion :

    1. On considère que le premier élément est déjà trié.
    2. Pour chaque élément suivant (à partir du deuxième) :
      • On le compare avec les éléments déjà triés, en reculant.
      • On décale les éléments plus grands que lui vers la droite.
      • On insère l'élément à sa place correcte dans la partie déjà triée.
    • Idée : Construire la liste triée un élément à la fois.
    def tri_insertion(liste):
        for i in range(1, len(liste)):
            cle = liste[i] # L'élément à insérer
            j = i - 1
            # Déplacer les éléments plus grands que 'cle' vers la droite
            while j >= 0 and cle < liste[j]:
                liste[j + 1] = liste[j]
                j -= 1
            liste[j + 1] = cle # Insérer 'cle' à sa bonne place
        return liste
    
  • Comparaison de l'efficacité (notions) :

    • L'efficacité d'un algorithme est souvent mesurée par le nombre d'opérations qu'il effectue en fonction de la taille de l'entrée (nn). C'est ce qu'on appelle la complexité algorithmique.
    • Pour les tris par sélection et par insertion, dans le pire des cas, la complexité est en O(n2)O(n^2) (prononcé "grand O de n carré"). Cela signifie que si la taille de la liste double, le temps d'exécution peut quadrupler. Ils sont efficaces pour de petites listes mais deviennent lents pour de grandes listes.

Recherche séquentielle

La recherche séquentielle (ou linéaire) est l'algorithme de recherche le plus simple.

  • Principe : Il consiste à parcourir tous les éléments d'une liste un par un, du début à la fin, jusqu'à ce que l'élément recherché soit trouvé ou que toute la liste ait été parcourue.
    def recherche_sequentielle(liste, element):
        for i in range(len(liste)):
            if liste[i] == element:
                return i # Renvoie l'indice si l'élément est trouvé
        return -1 # Renvoie -1 si l'élément n'est pas trouvé
    
  • Cas favorable et défavorable :
    • Cas favorable (meilleur cas) : L'élément recherché est le premier de la liste. L'algorithme effectue une seule comparaison.
    • Cas défavorable (pire cas) : L'élément recherché est le dernier de la liste, ou il n'est pas présent du tout. L'algorithme doit parcourir toute la liste, effectuant nn comparaisons (où nn est la taille de la liste).
  • Implémentation en Python : (voir ci-dessus)

Recherche dichotomique (sur liste triée)

La recherche dichotomique est un algorithme de recherche beaucoup plus efficace que la recherche séquentielle, mais elle a une précondition essentielle : la liste doit être triée.

  • Principe de division :

    1. On compare l'élément recherché avec l'élément du milieu de la liste.
    2. Si l'élément du milieu est celui recherché, on l'a trouvé.
    3. S'il est plus petit, on sait que l'élément (s'il existe) se trouve dans la première moitié de la liste.
    4. S'il est plus grand, l'élément se trouve dans la deuxième moitié.
    5. On répète le processus sur la moitié pertinente de la liste jusqu'à trouver l'élément ou jusqu'à ce que la sous-liste devienne vide.
    • Idée : Diviser par deux le problème à chaque étape.
  • Précondition : La liste doit impérativement être triée (croissant ou décroissant).

  • Implémentation et efficacité :

    def recherche_dichotomique(liste_triee, element):
        gauche = 0
        droite = len(liste_triee) - 1
    
        while gauche <= droite:
            milieu = (gauche + droite) // 2 # Division entière
            if liste_triee[milieu] == element:
                return milieu # Élément trouvé
            elif liste_triee[milieu] < element:
                gauche = milieu + 1 # L'élément est dans la moitié droite
            else:
                droite = milieu - 1 # L'élément est dans la moitié gauche
        return -1 # Élément non trouvé
    
    • L'efficacité de la recherche dichotomique est en O(logn)O(\log n) (logarithmique). Cela signifie que le nombre d'opérations augmente très lentement avec la taille de la liste. Pour une liste de 1 million d'éléments, il ne faut qu'environ 20 comparaisons au maximum ! C'est beaucoup plus rapide que la recherche séquentielle pour de grandes listes.

Algorithmes sur les chaînes de caractères

Les chaînes de caractères sont des séquences, et on peut y appliquer des algorithmes similaires à ceux sur les listes, en tenant compte de leur immuabilité.

  • Comptage de caractères : Compter le nombre d'occurrences d'un caractère ou d'une sous-chaîne.
    texte = "abracadabra"
    compte_a = texte.count('a') # Utilisation de la méthode intégrée
    print(compte_a) # Affiche 5
    
    # Algorithme manuel :
    compteur = 0
    for char in texte:
        if char == 'b':
            compteur += 1
    print(compteur) # Affiche 2
    
  • Recherche de sous-chaînes : Vérifier si une chaîne contient une autre chaîne.
    phrase = "Le chat et la souris"
    if "chat" in phrase: # Opérateur 'in'
        print("Le mot 'chat' est présent.")
    
    pos = phrase.find("souris") # Méthode find()
    if pos != -1:
        print(f"Le mot 'souris' commence à l'index {pos}.")
    
  • Manipulation de chaînes : (Voir section sur les chaînes de caractères, méthodes replace, split, etc.)
    • Un algorithme peut consister à nettoyer une chaîne (supprimer des caractères indésirables, normaliser la casse), extraire des informations (parser une date), ou formater du texte.
    date_brute = " 25/12/2023 "
    date_nettoyee = date_brute.strip()
    parties_date = date_nettoyee.split('/')
    print(f"Jour : {parties_date[0]}, Mois : {parties_date[1]}, Année : {parties_date[2]}")
    

Chapitre 6

Débogage et Bonnes Pratiques

Types d'erreurs

Comprendre les différents types d'erreurs est la première étape pour les corriger.

  • Erreurs de syntaxe (SyntaxError) : Ce sont des erreurs de grammaire du langage Python. Le programme ne peut même pas démarrer. L'interpréteur Python les détecte avant l'exécution.

    • Exemples : Oubli d'un : après un if ou def, parenthèse non fermée, mot-clé mal orthographié.
    # if True  # Manque le ':'
    #     print("Vrai")
    
    • Le message d'erreur indique souvent la ligne et la nature de l'erreur.
  • Erreurs d'exécution (exceptions) : Ces erreurs se produisent pendant l'exécution du programme. Le code est syntaxiquement correct, mais une opération invalide est tentée.

    • Exemples :
      • NameError : Utilisation d'une variable non définie.
      • TypeError : Opération effectuée sur un type de données incompatible (ex: ajouter un nombre à une chaîne).
      • IndexError : Accès à un index de liste ou de chaîne qui n'existe pas.
      • ZeroDivisionError : Division par zéro.
    # print(x) # NameError: name 'x' is not defined
    # "hello" + 5 # TypeError: can only concatenate str (not "int") to str
    # ma_liste = [1, 2] ; print(ma_liste[2]) # IndexError: list index out of range
    
    • Ces erreurs arrêtent l'exécution du programme et affichent une traceback (pile d'appels) qui indique où l'erreur s'est produite.
  • Erreurs logiques : Ce sont les plus difficiles à détecter. Le programme s'exécute sans erreur, mais il ne produit pas le résultat attendu. Le code fait ce qu'on lui a dit, mais ce n'est pas ce qu'on voulait qu'il fasse.

    • Exemples :
      • Une condition de boucle while n'est jamais fausse (boucle infinie).
      • Un calcul est incorrect (a + b au lieu de a - b).
      • Un algorithme de tri ne trie pas correctement une liste.
    • Pour les trouver, il faut comprendre la logique de l'algorithme et vérifier chaque étape.

Techniques de débogage

Le débogage est le processus de recherche et de correction des erreurs dans un programme.

  • Affichage de variables (print) : La technique la plus simple et la plus courante. Insérer des instructions print() à des points clés du code pour afficher les valeurs des variables et vérifier le flux d'exécution.
    def calculer_moyenne(notes):
        somme = 0
        print(f"Début du calcul. Somme initiale : {somme}")
        for note in notes:
            somme += note
            print(f"Note ajoutée : {note}, Somme actuelle : {somme}")
        moyenne = somme / len(notes)
        print(f"Moyenne calculée : {moyenne}")
        return moyenne
    
  • Débogueur pas à pas (notions) : Les environnements de développement intégrés (IDE) comme PyCharm, VS Code, ou même des outils en ligne, incluent des débogueurs. Ils permettent :
    • De mettre des points d'arrêt (breakpoints) pour suspendre l'exécution à une ligne spécifique.
    • D'exécuter le code pas à pas (instruction par instruction).
    • D'inspecter les valeurs des variables à chaque étape.
    • De suivre la pile d'appels des fonctions.
    • C'est une technique très puissante pour comprendre le déroulement exact du programme et identifier les erreurs logiques.
  • Tests unitaires (introduction) : Écrire de petits tests automatisés pour vérifier que des parties spécifiques (unités) de votre code (souvent des fonctions) fonctionnent comme prévu. Si un test échoue, vous savez exactement quelle partie du code est en faute.
    # Exemple très simple de test "manuel"
    assert carre(2) <mark> 4, "Test 1 échoué : carre(2) devrait être 4"
    assert carre(0) </mark> 0, "Test 2 échoué : carre(0) devrait être 0"
    print("Tous les tests passés !")
    
    (En Python, des modules comme unittest ou pytest sont utilisés pour des tests plus structurés.)

Commentaires et documentation

Un code bien commenté est plus facile à comprendre, à maintenir et à déboguer.

  • Utilité des commentaires :
    • Expliquer la logique complexe : Pourquoi une décision a été prise, pas seulement comment.
    • Clarifier des sections de code : Rendre le code compréhensible pour d'autres développeurs (ou pour vous-même dans 6 mois !).
    • Indiquer des TODOs ou des améliorations futures.
    • En Python, les commentaires commencent par #.
    # Cette fonction calcule la factorielle d'un nombre entier positif
    def factorielle(n):
        if n == 0:
            return 1 # Cas de base pour la récursivité
        else:
            return n * factorielle(n-1) # Appel récursif
    
  • Docstrings en Python : Ce sont des chaînes de caractères multilignes utilisées pour documenter les modules, les classes et les fonctions. Elles sont placées juste après la définition et sont accessibles via help() ou .__doc__.
    def addition(a, b):
        """
        Cette fonction prend deux nombres en entrée et renvoie leur somme.
    
        Args:
            a (int/float): Le premier nombre.
            b (int/float): Le deuxième nombre.
    
        Returns:
            int/float: La somme des deux nombres.
        """
        return a + b
    
    # help(addition) # Affichera la docstring
    
  • Lisibilité du code : Un code lisible est un code bien structuré, avec des noms de variables et fonctions clairs, et une indentation cohérente.

Bonnes pratiques de programmation

Adopter de bonnes pratiques rend votre code plus professionnel, robuste et facile à travailler.

  • Nommage des variables et fonctions :
    • Utiliser des noms descriptifs et explicites. nombre_utilisateurs est mieux que nu.
    • Respecter les conventions :
      • Variables et fonctions : snake_case (ex: mon_compteur, calculer_moyenne).
      • Constantes : ALL_CAPS (ex: TAUX_TVA).
      • Classes : CamelCase (ex: MaClasse).
  • Indentation et formatage du code :
    • Python impose l'indentation. Utiliser 4 espaces par niveau d'indentation (convention PEP 8).
    • Lignes courtes (max 79 caractères selon PEP 8).
    • Espaces autour des opérateurs (a = b + c plutôt que a=b+c).
    • Outils comme Black ou autopep8 peuvent formater le code automatiquement.
  • Modularité et réutilisabilité :
    • Découper le programme en fonctions (ou classes) qui font une chose et la font bien.
    • Éviter la répétition de code (DRY - Don't Repeat Yourself). Si vous copiez-collez du code, c'est probablement le moment de créer une fonction.
    • Les fonctions doivent être aussi indépendantes que possible des variables globales.
  • Gestion des erreurs : Anticiper les erreurs possibles (par exemple, ce que fait le programme si l'utilisateur entre du texte au lieu d'un nombre). Utiliser des blocs try-except pour gérer les exceptions de manière élégante.
    try:
        nombre = int(input("Entrez un nombre : "))
        resultat = 10 / nombre
        print(f"Le résultat est {resultat}")
    except ValueError:
        print("Erreur : Ce n'est pas un nombre valide.")
    except ZeroDivisionError:
        print("Erreur : Division par zéro impossible.")
    except Exception as e: # Capture toutes les autres erreurs
        print(f"Une erreur inattendue est survenue : {e}")
    

Après la lecture

Passe à la pratique avec deux blocs bien visibles

Une fois le cours lu, ouvre soit le quiz pour vérifier la compréhension, soit les flashcards pour mémoriser les idées importantes. Les deux s'ouvrent dans une fenêtre dédiée.

Quiz + Flashcards

Suite naturelle

Tu veux aller plus loin que l'article ?

Retrouve le même chapitre dans Wilo avec la suite des questions, la répétition espacée, les corrigés complets et une progression suivie dans le temps.