Vue d'ensemble
La plupart des algorithmes que tu écriras en prépa se ramènent à une poignée de patrons : des schémas de code que l'on reconnaît, que l'on adapte, et que l'on finit par écrire de tête. Parcourir une liste pour en faire la somme, trouver le plus grand élément, compter ceux qui vérifient une condition, chercher si une valeur y figure : tous suivent la même trame, celle de l'accumulateur — une variable que l'on initialise, que l'on met à jour à chaque tour de boucle, et que l'on renvoie à la fin. Une fois ce réflexe acquis, tu ne « réinventes » plus un algorithme à chaque exercice : tu reconnais le patron, tu recopies la structure, tu adaptes la condition. Cette fiche décortique chacun de ces motifs ligne par ligne, avec les tables de trace qui montrent ce que fait vraiment Python à chaque tour, et les pièges qui coûtent le plus de points.
return doit sortir de la boucle ou attendre la fin, et savoir traiter le
cas de la liste vide sans faire planter le programme.
Prérequis
- Savoir écrire une boucle
for x in liste:et une bouclefor i in range(len(liste)): - Connaître les tests de comparaison
<,>,==et le typebool - Savoir écrire une fonction avec
defetreturn
Tu bloques dès qu'un exo demande de « parcourir une liste » ? Ces patrons se travaillent à la main, un par un, jusqu'à ce qu'ils deviennent des réflexes. Nos mentors alumni X · Centrale · Mines t'entraînent sur des mini-algorithmes ciblés pour que tu arrives en prépa avec la boîte à outils complète.
Trouver un mentor →1. Le motif de l'accumulateur (le patron universel)
Presque tous les patrons de cette fiche sont des variantes d'un même squelette. Le comprendre une fois, c'est les comprendre tous.
Un accumulateur est une variable qui « accumule » le résultat au fil du parcours. Le schéma est toujours le même en quatre temps : (1) initialiser l'accumulateur avant la boucle, (2) parcourir la liste, (3) mettre à jour l'accumulateur à chaque élément, (4) renvoyer l'accumulateur après la boucle.
- Initialiser un accumulateur (à 0, à
liste[0], àFalse… selon le but). - Parcourir la liste avec un
for. - Mettre à jour l'accumulateur en fonction de l'élément courant.
- Renvoyer l'accumulateur une fois la boucle terminée.
def somme(liste):
total = 0 # 1. initialiser l accumulateur
for x in liste: # 2. parcourir
total = total + x # 3. mettre a jour
return total # 4. renvoyer
print(somme([4, 7, 2, 9, 3])) # 25total = 0Initialisation AVANT la boucle. On part de 0 (le résultat d'une somme vide). Cette ligne est hors de la boucle : elle ne s'exécute qu'une fois. Puis x prend successivement chaque valeur : 4, 7, 2, 9, 3.total = total + xMise à jour. À chaque tour, on ajoute l'élément courant au total. La droite est évaluée avec l'ancien total, puis le résultat est rangé dans total.return totalAPRÈS la boucle. On renvoie le total une fois tous les éléments vus. Attention à l'indentation : ce return est aligné avec le for, pas à l'intérieur. Pour passer d'un patron à l'autre, tu ne changeras que l'initialisation, la mise à jour et parfois la condition : le squelette ne bouge pas.2. Somme et moyenne
La somme est le patron d'accumulateur le plus simple : on part de 0 et on ajoute. La moyenne s'en déduit en divisant par le nombre d'éléments.
def moyenne(liste):
total = 0
for x in liste:
total = total + x
return total / len(liste)
print(moyenne([4, 7, 2, 9, 3])) # 5.0total = 0Même départ que la somme. On accumule d'abord la somme totale : ici 4+7+2+9+3 = 25.len(liste)Le nombre d'éléments. len renvoie la longueur de la liste : 5 ici.return total / len(liste)Division finale. 25 / 5 = 5.0. On utilise / (division réelle) : la moyenne est un float, même si elle tombe juste.len sur une liste vide. Si liste est vide,
len(liste) vaut 0 et total / 0 déclenche une ZeroDivisionError.
Une moyenne n'a de toute façon pas de sens sur zéro élément : protège-toi en testant le cas vide
avant la division : if len(liste) == 0: return None en tête de fonction règle le
problème.
sum existe déjà. Python fournit sum(liste) qui fait la
somme pour toi. En prépa, on te demande souvent de savoir la réécrire à la main (c'est le
patron d'accumulateur) même si la fonction toute prête existe.
3. Maximum et minimum
Trouver le plus grand élément d'une liste est un patron d'accumulateur… mais avec un piège d'initialisation redoutable. La règle d'or : on initialise l'accumulateur au premier élément de la liste, jamais à une valeur arbitraire.
def maximum(liste):
m = liste[0] # on part du PREMIER element
for x in liste:
if x > m:
m = x # on garde le plus grand vu jusqu ici
return m
print(maximum([4, 7, 2, 9, 3])) # 9m = liste[0]Initialisation au premier élément. On suppose provisoirement que le premier élément (4) est le maximum. C'est le point crucial : jamais m = 0 ! On repasse ensuite même sur ce premier élément, mais 4 > 4 est faux, donc rien ne change.if x > m:Comparaison. Si l'élément courant dépasse le maximum provisoire, c'est lui le nouveau champion.m = xMise à jour. On remplace le maximum provisoire. Sans cette ligne, on comparerait sans jamais retenir : erreur classique.return mAprès la boucle. Une fois tous les éléments vus, m contient le plus grand.Traçons l'exécution sur [4, 7, 2, 9, 3], tour par tour :
Élément x | Test x > m | Action | m après le tour |
|---|---|---|---|
| — (init) | — | m = liste[0] | 4 |
| 4 | 4 > 4 → faux | rien | 4 |
| 7 | 7 > 4 → vrai | m = 7 | 7 |
| 2 | 2 > 7 → faux | rien | 7 |
| 9 | 9 > 7 → vrai | m = 9 | 9 |
| 3 | 3 > 9 → faux | rien | 9 |
m = 0 puis que tous les éléments sont négatifs, aucun ne dépassera 0 : la
fonction renverra 0, une valeur qui n'est même pas dans la liste ! Sur
[-4, -7, -2], le vrai maximum est -2, mais l'initialisation à 0 donnerait
faussement 0. Initialise toujours au premier élément.
m = liste[0] et on change
juste le sens de la comparaison : if x < m: au lieu de if x > m:. Sur
[4, 7, 2, 9, 3], minimum renverrait 2.
4. Compter
Compter, c'est un accumulateur entier : on part de 0 et on ajoute 1 chaque fois qu'un élément vérifie une condition. Deux usages fréquents : compter les occurrences d'une valeur, ou compter les éléments qui satisfont un test.
def compte_occurrences(liste, valeur):
compteur = 0
for x in liste:
if x == valeur: # est-ce l element cherche ?
compteur = compteur + 1
return compteur
print(compte_occurrences([3, 1, 3, 7, 3], 3)) # 3compteur = 0On part de zéro. Avant de parcourir, on n'a encore rien compté.if x == valeur:Le test. On compare avec == (égalité), pas = (affectation).compteur = compteur + 1Incrémentation. On ajoute 1 seulement quand le test est vrai. Sur [3, 1, 3, 7, 3], la valeur 3 apparaît aux positions 0, 2 et 4 : le compteur finit à 3.if x % 2 == 0: à la
place de if x == valeur: : sur [4, 7, 2, 9, 3], on obtient 2 (les éléments
4 et 2). Le squelette du patron, lui, ne change pas.
compteur += 1. L'écriture compteur += 1 est un
raccourci pour compteur = compteur + 1 : exactement le même effet, en plus court.
5. Rechercher un élément (recherche séquentielle)
Chercher si une valeur figure dans une liste, c'est la recherche séquentielle : on
parcourt du début à la fin. Nouveauté par rapport aux patrons précédents : dès qu'on trouve, on peut
arrêter tout de suite avec un return à l'intérieur de la boucle.
def contient(liste, valeur):
for x in liste:
if x == valeur:
return True # trouve : on sort immediatement
return False # boucle finie sans trouver : absent
print(contient([4, 7, 2, 9, 3], 9)) # True
print(contient([4, 7, 2, 9, 3], 5)) # Falseif x == valeur:On teste chaque élément. Dès qu'un élément égale la valeur cherchée, la recherche est un succès.return TrueSortie anticipée. Ce return est dans la boucle : il interrompt tout et renvoie True sans examiner la suite. Inutile de continuer, on a la réponse.return FalseAprès la boucle, aligné avec le for. On n'arrive ici que si la boucle s'est terminée sans jamais trouver : la valeur est absente. La position (hors de la boucle) est essentielle.i ; la convention est de
renvoyer -1 quand la valeur est absente.
def indice_de(liste, valeur):
for i in range(len(liste)):
if liste[i] == valeur:
return i # premiere position trouvee
return -1 # absent
print(indice_de([4, 7, 2, 9, 3], 9)) # 3
print(indice_de([4, 7, 2, 9, 3], 5)) # -16. Combiner les patrons
Les vrais exercices mélangent souvent plusieurs patrons. Exemple canonique : trouver non pas le maximum, mais l'indice du maximum. On fusionne le patron « maximum » et le patron « parcours par indice ».
def indice_du_max(liste):
i_max = 0 # on suppose que le max est en position 0
for i in range(len(liste)):
if liste[i] > liste[i_max]:
i_max = i # on garde la position du plus grand
return i_max
print(indice_du_max([4, 7, 2, 9, 3])) # 3 (car liste[3] == 9)i_max = 0On stocke une POSITION, pas une valeur. On part de l'indice 0 (comme m = liste[0], mais version indice). Ici aussi : jamais initialisé « au hasard ».if liste[i] > liste[i_max]:Comparaison des valeurs via leurs indices. On compare l'élément courant liste[i] au meilleur trouvé liste[i_max]. Ce sont bien des valeurs qu'on compare, mais on retient l'indice.i_max = iMise à jour de la position. On mémorise où se trouve le nouveau maximum, pas sa valeur.return i_maxRésultat. Sur [4, 7, 2, 9, 3], le maximum 9 est en position 3, donc la fonction renvoie 3. Si tu veux la valeur plutôt que la position, il suffit d'écrire liste[i_max].Reconnaître le bon patron en quelques secondes, ça se travaille. Nos mentors alumni X · Centrale · Mines t'entraînent à décomposer un énoncé en patrons connus (accumuler, compter, chercher, comparer) pour que tu ne restes plus jamais bloqué·e devant une liste. Un stage ciblé avant la rentrée et l'informatique devient un terrain d'entraînement.
Découvrir les stages →7. Exercices d'application
À faire de tête, en s'appuyant sur les patrons, avant d'ouvrir le corrigé.
Écris une fonction indice_du_max(liste) qui renvoie la position du plus grand
élément (on suppose la liste non vide). Sur [8, 3, 8, 1], plusieurs réponses sont
acceptables : quelle position ta fonction renvoie-t-elle, et pourquoi ?
Voir la correction détaillée
indice_du_max de la section 6 : initialiser i_max = 0, parcourir les indices avec for i in range(len(liste)):, faire i_max = i dès que liste[i] > liste[i_max], puis return i_max.[8, 3, 8, 1] : le test est strict (>), donc le second 8 (position 2) ne remplace pas le premier (position 0). La fonction renvoie 0, la position du premier maximum. Avec >=, on obtiendrait la dernière position.
Écris une fonction nb_au_dessus_moyenne(liste) qui renvoie le nombre d'éléments
strictement supérieurs à la moyenne de la liste (supposée non vide). Combien
vaut-elle sur [4, 7, 2, 9, 3] ?
Voir la correction détaillée
def nb_au_dessus_moyenne(liste):
moyenne = sum(liste) / len(liste)
compteur = 0
for x in liste:
if x > moyenne:
compteur = compteur + 1
return compteur[4, 7, 2, 9, 3] : la moyenne vaut 25 / 5 = 5.0. Les éléments strictement supérieurs à 5.0 sont 7 et 9, donc la fonction renvoie 2.8. Erreurs classiques (les pièges qui coûtent des points)
Ces cinq erreurs reviennent chez presque tous les débutants sur les patrons de parcours. Les repérer à l'avance, c'est éviter de perdre bêtement des points en TP comme en concours.
m = 0 renvoie 0, une valeur absente de la liste. Initialise
toujours au premier élément : m = liste[0].
len sur une liste vide. total / len(liste)
plante avec ZeroDivisionError quand la liste est vide. Teste
if len(liste) == 0: avant de calculer une moyenne.
return placé trop tôt. Dans une somme ou un comptage, mettre
return dans la boucle interrompt le parcours au premier tour : tu renvoies un
résultat partiel (par ex. la somme du seul premier élément). Le return du résultat
global doit être après la boucle. À l'inverse, dans une recherche, le
return True anticipé est voulu : tout dépend du patron.
liste[0] sur une liste vide lève une
IndexError. Les patrons maximum, minimum et moyenne supposent au moins un élément : soit
tu le garantis, soit tu ajoutes un test if len(liste) == 0: en début de fonction.
if x > m: puis
oublier la ligne m = x : la comparaison ne sert alors à rien, m ne change
jamais et la fonction renvoie systématiquement le premier élément. La mise à jour de l'accumulateur
est le cœur du patron.
9. Pour aller plus loin
Ces patrons sont les briques de base des algorithmes plus ambitieux que tu rencontreras juste après. Tous réinvestissent le parcours de liste, la comparaison et l'accumulateur que tu viens de revoir :
- Recherche par dichotomie — sur une liste triée, on coupe l'intervalle en deux à chaque étape : bien plus rapide que la recherche séquentielle de la section 5.
- Tris par insertion et par sélection — le tri par sélection cherche à répétition le minimum du reste de la liste : c'est le patron « minimum » utilisé en boucle.
- Complexité temporelle — pour comparer ces algorithmes, on compte le nombre d'opérations : parcourir une liste de taille
n, c'est de l'ordre dencomparaisons.
Récap final — Ce qu'il faut absolument retenir
Tu dois pouvoir répondre « oui » sans hésiter à chaque point.
- Sais-tu réciter le motif de l'accumulateur en quatre temps (initialiser, parcourir, mettre à jour, renvoyer) ?
- Sais-tu écrire la somme d'une liste avec
total = 0puistotal = total + x? - Sais-tu que la moyenne vaut
total / len(liste)et que c'est unfloat? - Sais-tu qu'une liste vide fait planter la division de la moyenne (
ZeroDivisionError) ? - Sais-tu qu'un maximum s'initialise à
liste[0], JAMAIS à 0 ? - Sais-tu expliquer pourquoi initialiser le max à 0 est faux quand tous les éléments sont négatifs ?
- Sais-tu passer du maximum au minimum en changeant juste
>en<? - Sais-tu compter avec
compteur = 0puiscompteur = compteur + 1sous condition ? - Sais-tu écrire une recherche séquentielle qui renvoie
True(ou l'indice) dès qu'elle trouve ? - Sais-tu que le
returnfinal (Falseou-1) doit être APRÈS la boucle ? - Sais-tu que sur une liste triée, la dichotomie remplace avantageusement la recherche séquentielle ?
- Sais-tu combiner deux patrons, par exemple pour renvoyer l'indice du maximum ?