Révision U22 — Algorithmique appliquée

Retour aux projets

Cette page rassemble une révision complète du programme d'algorithmique appliquée (épreuve U22) : cours concis, exemples exécutés et exercices auto-corrigés pour s'entraîner avant le CCF Python 3.

CCF fin de 1re année Python 3 Coefficient 2 Évaluation par compétences

1 Variables, types & conventions

Variable et affectation

Une variable est un nom qui désigne une valeur stockée en mémoire. On lui donne une valeur avec l'opérateur d'affectation = : le nom se place à gauche, la valeur à droite.

age = 20        # on range 20 dans la variable age
message = "Salut"

Une valeur écrite directement dans le code (comme 20 ou "Salut") s'appelle un littéral.

Les quatre types de base

Chaque valeur possède un type. La fonction type() permet de le connaître.

  • int : nombre entier (7, -3) ;
  • float : nombre à virgule flottante (19.99, le séparateur est le point) ;
  • bool : booléen, True ou False (avec une majuscule) ;
  • str : chaîne de caractères, entre guillemets ("Aubert").
nb = 7
prix = 19.99
actif = True
nom = "Aubert"
print(type(nb))
print(type(prix))
print(type(actif))
print(type(nom))
Sortie
<class 'int'>
<class 'float'>
<class 'bool'>
<class 'str'>

Échanger deux variables

Pour échanger le contenu de deux variables, on utilise une variable temporaire qui met une valeur de côté le temps de l'échange.

a = 5
b = 8
temp = a     # on met la valeur de a de côté
a = b        # a reçoit la valeur de b
b = temp     # b reçoit l'ancienne valeur de a
print(a, b)
Sortie
8 5

Conventions d'écriture (BTS SIO)

Chaque programme ou fonction commence par un en-tête normalisé en commentaires, qui décrit son rôle :

# Nom : moyenne_notes
# Rôle : calcule la moyenne d'une liste de notes
# Entrées : notes (liste de réels)
# Sortie : moyenne (réel)
# Variables locales : total (réel), n (entier)

Deux autres règles importantes :

  • l'indentation est significative en Python : le décalage (4 espaces) délimite les blocs, ce n'est pas de la décoration ;
  • on ajoute des commentaires avec # pour expliquer le code.
Piège — = n'est pas ==. Un seul signe = affecte une valeur (x = 3) ; le double == compare deux valeurs (x == 3). Les confondre est l'erreur la plus fréquente.
Piège — la division / renvoie toujours un float. Même quand le résultat « tombe juste », 7 / 2 vaut 3.5 et son type est float.
À retenir : choisir des noms de variables explicites (prix_ht, nb_eleves) plutôt que x ou a rend le code lisible et est valorisé à l'examen.
QCM●○○

Quel est le type de 19.99 ?

On écrit prix = 19.99. Quel est le type de la variable prix ?

Voir la solution

Réponse : float. Tout littéral comportant un point décimal est un nombre à virgule flottante, même si la partie décimale est .00. Un entier comme 19 serait un int ; entre guillemets, "19.99" serait un str.

QCM●○○

= ou == ?

Je veux ranger la valeur 10 dans une variable compteur. Quelle ligne est correcte ?

Voir la solution

Réponse : compteur = 10. L'affectation utilise un seul =, avec le nom à gauche et la valeur à droite. == sert à comparer (il ne range rien). 10 = compteur est invalide (on ne peut pas affecter à un littéral).

Sortie●●○

Prévoir la sortie d'un échange

Que produit ce programme ?

x = 3
y = 10
temp = x
x = y
y = temp
print("x =", x)
print("y =", y)
Voir la solution

Sortie attendue :

x = 10
y = 3

La variable temp mémorise l'ancienne valeur de x (3). Ensuite x prend la valeur de y (10), puis y reprend la valeur mise de côté dans temp (3). Les deux valeurs ont bien été échangées.

Trace●●○

Suivre les variables ligne à ligne

Complétez la table de trace. Une cellule sans valeur (variable pas encore créée) se laisse vide.

a = 4      # L1
b = 9      # L2
c = a      # L3
a = b      # L4
b = c      # L5
Ligneabc
L14
L249
L349
L494
L594
Voir la solution

À la ligne L1, seule a existe (valeur 4) ; b et c n'existent pas encore. L3 copie a dans c : c vaut 4. L4 écrase a avec la valeur de b : a vaut 9. L5 met dans b la valeur mémorisée par c : b vaut 4. C'est un échange de a et b à l'aide de la variable temporaire c.

Rédaction●●●

Échanger deux variables (en-tête normalisé)

Un logiciel de gestion affiche deux stocks : le dépôt gauche contient 12 articles, le dépôt droite en contient 30. Écrivez un programme qui échange le contenu des deux dépôts puis affiche les nouvelles valeurs. Respectez l'en-tête normalisé.

Voir la solution

On utilise une variable temporaire pour ne perdre aucune valeur pendant l'échange :

# Nom : echanger
# Rôle : échange les valeurs de deux variables puis les affiche
# Entrées : aucune (valeurs fixées dans le code)
# Sortie : affichage des deux valeurs échangées
# Variables locales : gauche (entier), droite (entier), temp (entier)

gauche = 12
droite = 30
temp = gauche
gauche = droite
droite = temp
print("gauche =", gauche)
print("droite =", droite)
Sortie
gauche = 30
droite = 12

Sans la variable temp, écrire directement gauche = droite puis droite = gauche perdrait la valeur de gauche : les deux dépôts finiraient à 30.

2 Entrées / sorties & conversions

Afficher avec print()

print() affiche une ou plusieurs valeurs à l'écran. Séparés par des virgules, les arguments sont affichés espacés. Deux options utiles : sep (séparateur entre les arguments) et end (ce qui est ajouté à la fin, un retour à la ligne par défaut).

print("Note", ":", 15)      # arguments séparés par un espace
print("A", "B", "C", sep="-")  # séparateur personnalisé
print("sans retour", end=" | ")  # pas de retour à la ligne
print("suite")
Sortie
Note : 15
A-B-C
sans retour | suite

Lire au clavier avec input()

input("message") affiche le message puis attend une saisie de l'utilisateur. Point capital : input() renvoie TOUJOURS une chaîne de caractères (str), même si l'on tape des chiffres.

age = input("Votre âge : ")
print(type(age))               # c'est une chaîne !
age_entier = int(age)          # conversion en entier
print("L'an prochain :", age_entier + 1)
Sortie
Votre âge : <class 'str'>
L'an prochain : 21

Convertir : int(), float(), str()

On transforme une valeur d'un type vers un autre :

  • int("20")20 (chaîne vers entier) ;
  • float("19.99")19.99 (chaîne vers réel) ;
  • str(42)"42" (nombre vers chaîne).

Si la chaîne ne représente pas un nombre valide, la conversion échoue : int("abc") déclenche une erreur ValueError.

Piège classique — "2" + "3" donne "23", pas 5. Sur des chaînes, + concatène (colle bout à bout). Sur des nombres, + additionne. Comme input() renvoie des chaînes, il faut convertir avec int() ou float() AVANT tout calcul.
print("2" + "3")   # concaténation de deux chaînes
print(2 + 3)       # addition de deux entiers
Sortie
23
5
À retenir : schéma type d'une saisie numérique → n = int(input("…")) : on lit, puis on convertit sur la même ligne.
QCM●○○

Quel type renvoie input() ?

Je tape 7 au clavier après reponse = input("Nombre : "). Quel est le type de reponse ?

Voir la solution

Réponse : str. input() renvoie toujours une chaîne, quel que soit ce qui est saisi. Même en tapant 7, on obtient la chaîne "7". Pour calculer avec, il faut convertir : int(reponse).

Interprétation●●○

Que fait ce code ?

Que vaut l'affichage de ce programme ?

a = "4"
b = "5"
print(a + b)
Voir la solution

Réponse : 45. a et b sont des chaînes (guillemets) : + les concatène. Pour obtenir 9, il aurait fallu convertir : int(a) + int(b).

Sortie●●○

Prévoir la sortie avec sep

Que produit ce programme ?

prenom = "Fabien"
nom = "Aubert"
print(prenom, nom, sep="_")
print("Total", 3 + 4)
Voir la solution

Sortie attendue :

Fabien_Aubert
Total 7

Le premier print colle prenom et nom avec un tiret bas (sep="_"). Le second additionne d'abord 3 + 4 (nombres, donc 7) et l'affiche après le mot Total, séparé par l'espace par défaut.

Compléter●●○

Compléter une saisie numérique

Ce programme doit lire un nombre entier puis afficher son double. Complétez la conversion manquante.

valeur = (input("Un nombre : "))
print("Le double :", valeur * 2)
Voir la solution

Réponse : int. Sans conversion, input() renvoie une chaîne et valeur * 2 répéterait le texte (par exemple "5" * 2 donnerait "55"). int(...) transforme la saisie en entier, ce qui permet la multiplication arithmétique.

Débogage●●●

Une somme qui se transforme en collage

Un stagiaire veut additionner deux nombres saisis. Avec les entrées 5 puis 7, son programme affiche Somme : 57 au lieu de Somme : 12. Corrigez les deux lignes de saisie.

Code erroné :

a = input("Premier nombre : ")
b = input("Deuxième nombre : ")
print("Somme :", a + b)
a = (input("Premier nombre : "))
b = (input("Deuxième nombre : "))
print("Somme :", a + b)
Voir la solution

Réponse : envelopper chaque input() dans int(...). Comme input() renvoie des chaînes, "5" + "7" concatène en "57". Avec int(input(...)), on manipule les entiers 5 et 7, donc 5 + 7 vaut bien 12. On aurait pu utiliser float si les nombres pouvaient être à virgule.

Rédaction●●●

Calcul d'un prix TTC

Dans un logiciel de caisse, on saisit un prix hors taxes (HT) au clavier. Écrivez un programme qui lit ce prix et affiche le prix toutes taxes comprises (TTC), avec une TVA de 20 % (multiplication par 1,2). Respectez l'en-tête normalisé.

Voir la solution

Le prix pouvant être à virgule, on convertit la saisie avec float :

# Nom : calcul_ttc
# Rôle : lit un prix HT au clavier et affiche le prix TTC (TVA 20 %)
# Entrées : prix_ht saisi au clavier (chaîne convertie en réel)
# Sortie : affichage du prix TTC
# Variables locales : prix_ht (réel), prix_ttc (réel)

prix_ht = float(input("Prix HT en euros : "))
prix_ttc = prix_ht * 1.2
print("Prix TTC :", prix_ttc, "€")
Sortie
Prix HT en euros : Prix TTC : 120.0 €

Ici la saisie était 100. On utilise float (et non int) car un prix comme 19.99 comporte des décimales. Attention : sur certaines valeurs, l'affichage flottant peut faire apparaître de nombreuses décimales (par exemple 19.99 * 1.2 affiche 23.987999999999996) — c'est le comportement normal des réels en machine.

3 Conditions

Comparer des valeurs

Une condition est une expression qui vaut True ou False. On la construit avec les opérateurs de comparaison :

  • == égal, != différent ;
  • < inférieur, <= inférieur ou égal ;
  • > supérieur, >= supérieur ou égal.

if / elif / else

On exécute un bloc seulement si une condition est vraie. elif (« sinon si ») teste une autre condition, else couvre tous les cas restants. Le bloc de chaque branche est indenté.

note = 13
if note >= 16:
    print("Très bien")
elif note >= 12:
    print("Assez bien")
elif note >= 10:
    print("Passable")
else:
    print("Insuffisant")
Sortie
Assez bien

Avec note = 13, la première condition (>= 16) est fausse ; la deuxième (>= 12) est vraie, donc on affiche « Assez bien » et on quitte le bloc : les branches suivantes ne sont pas testées.

Conditions composées : and, or, not

On combine plusieurs conditions. and exige que les deux soient vraies, or qu'au moins une le soit, not inverse.

ABA and BA or B
TrueTrueTrueTrue
TrueFalseFalseTrue
FalseTrueFalseTrue
FalseFalseFalseFalse
Anot A
TrueFalse
FalseTrue

Encadrement a <= x <= b

Python autorise d'écrire un encadrement directement, plus lisible que x >= a and x <= b :

temperature = 22
if 18 <= temperature <= 25:
    print("Température confortable")
else:
    print("Température inconfortable")
Sortie
Température confortable
Piège — elif n'est pas la même chose que plusieurs if. Dans une chaîne if / elif, une seule branche s'exécute (la première vraie). Des if successifs indépendants sont testés chacun, donc plusieurs peuvent s'exécuter.
Piège — condition toujours vraie / toujours fausse. if age > 0 or age < 100: est toujours vrai (tout nombre vérifie l'une des deux). Pour dire « entre 0 et 100 », il faut and : if age > 0 and age < 100:.
À retenir : l'ordre des branches compte. Placez les seuils du plus exigeant au moins exigeant (>= 16 avant >= 12), sinon la première branche « attrape » tout.
QCM●○○

Quel opérateur teste l'égalité ?

Pour vérifier si code vaut exactement 1234, quelle condition écrire ?

Voir la solution

Réponse : if code == 1234:. L'égalité se teste avec le double ==. Un seul = est une affectation (interdite dans un if). =! et >< n'existent pas ; l'inégalité s'écrit !=.

QCM●●○

Table de vérité de and

Que vaut (3 > 2) and (5 < 1) ?

Voir la solution

Réponse : False. 3 > 2 est True, mais 5 < 1 est False. Avec and, il faut que les deux soient vraies ; comme l'une est fausse, le résultat est False. Avec or, le résultat aurait été True.

Interprétation●●○

elif ou if successifs ?

Qu'affiche ce programme ?

x = 5
if x > 0:
    print("positif")
if x > 3:
    print("grand")
Voir la solution

Réponse : positif puis grand. Ce sont deux if indépendants, testés chacun de leur côté. Comme 5 > 0 et 5 > 3 sont tous deux vrais, les deux messages s'affichent. Avec un elif à la place du second if, seul positif serait affiché (la première branche vraie arrête la chaîne).

Trace●●●

Quelle branche pour chaque stock ?

Pour chaque valeur de stock, indiquez la branche empruntée (if, elif ou else) et la valeur finale de etat.

if stock == 0:
    etat = "Rupture"
elif stock < 5:
    etat = "Bas"
else:
    etat = "OK"
stockBrancheetat
0
3
12
Voir la solution

Pour stock = 0 : la condition stock == 0 est vraie → branche if, etat = "Rupture". Pour stock = 3 : == 0 est faux, mais < 5 est vrai → branche elif, etat = "Bas". Pour stock = 12 : les deux conditions sont fausses → branche else, etat = "OK". Une seule branche est retenue à chaque fois.

Compléter●●○

Vérifier qu'une note est valide

Une note doit être comprise entre 0 et 20 inclus. Complétez l'encadrement.

note = 14
if 0  note  20:
    print("Note valide")
else:
    print("Note invalide")
Voir la solution

Réponse : <= des deux côtés, soit if 0 <= note <= 20:. On utilise <= (et non <) car les bornes 0 et 20 sont incluses. Cet encadrement remplace avantageusement note >= 0 and note <= 20.

Rédaction●●●

Message d'alerte d'un capteur

Un capteur de température de salle serveur remonte une valeur en degrés. Écrivez un programme qui lit cette température au clavier et affiche : « ALERTE : surchauffe critique » si elle atteint 80 °C, « Attention : température élevée » à partir de 60 °C, sinon « Température normale ». Respectez l'en-tête normalisé.

Voir la solution

On enchaîne if / elif / else du seuil le plus haut au plus bas, pour que chaque plage soit correctement séparée :

# Nom : etat_capteur
# Rôle : affiche un message d'alerte selon la valeur d'un capteur de température
# Entrées : temperature saisie au clavier (chaîne convertie en entier)
# Sortie : affichage du niveau d'alerte
# Variables locales : temperature (entier)

temperature = int(input("Température mesurée (°C) : "))
if temperature >= 80:
    print("ALERTE : surchauffe critique")
elif temperature >= 60:
    print("Attention : température élevée")
else:
    print("Température normale")
Sortie
Température mesurée (°C) : ALERTE : surchauffe critique

Ici la saisie était 85, donc la première branche s'active. L'ordre est essentiel : si l'on testait >= 60 avant >= 80, une valeur de 90 déclencherait à tort le message « élevée » au lieu de « critique ».

4 Boucles

Une boucle répète un bloc d'instructions. C'est la section la plus importante de l'épreuve : compter, additionner, parcourir des données passe presque toujours par une boucle. Deux outils : for (on connaît d'avance le nombre de tours) et while (on répète tant qu'une condition reste vraie).

La boucle for et range

range engendre une suite d'entiers. La borne de fin est toujours exclue.

  • range(n) : de 0 à n-1 (soit n valeurs).
  • range(debut, fin) : de debut à fin-1.
  • range(debut, fin, pas) : ajoute pas à chaque tour ; un pas négatif descend.
# range à 1, 2 et 3 arguments
for i in range(3):
    print("A", i)
for i in range(2, 5):
    print("B", i)
for i in range(10, 0, -2):
    print("C", i)
Sortie
A 0
A 1
A 2
B 2
B 3
B 4
C 10
C 8
C 6
C 4
C 2
Piège classique (off-by-one) : la fin est exclue. range(1, 5) donne 1, 2, 3, 4 — pas 5. Pour parcourir de 1 à 5 inclus, il faut range(1, 6).

Parcourir une liste ou une chaîne

Le for peut aussi parcourir directement les éléments d'une liste ou les caractères d'une chaîne (le détail des listes et des chaînes est vu aux sections 6 et 8).

notes = [12, 8, 15]
for note in notes:
    print(note)
for lettre in "SIO":
    print(lettre)
Sortie
12
8
15
S
I
O

La boucle while

while répète tant que sa condition est vraie : c'est une condition de poursuite. La boucle s'arrête quand cette condition devient fausse (c'est la condition d'arrêt). Pour que la boucle termine, il faut qu'une variable de la condition évolue à chaque tour et finisse par la rendre fausse.

compteur = 1
while compteur <= 3:      # condition de POURSUITE
    print("tour", compteur)
    compteur = compteur + 1  # fait évoluer la condition
print("fini")
Sortie
tour 1
tour 2
tour 3
fini
Boucle infinie : si on oublie compteur = compteur + 1, la condition reste toujours vraie et le programme ne s'arrête jamais. Vérifiez toujours qu'une variable de la condition change à chaque tour.

Non-entrée dans la boucle

Si la condition d'un while est fausse dès le départ, le corps de la boucle n'est jamais exécuté (zéro tour). Même chose pour un range vide.

x = 10
while x < 5:                 # faux dès le début
    print("jamais affiché")
print("terminé")
Sortie
terminé

Boucle à sentinelle

On saisit des données jusqu'à une valeur spéciale (la sentinelle) qui signale la fin. La première saisie se fait avant la boucle, la suivante à la fin de chaque tour.

total = 0
saisie = input("Valeur (fin pour arrêter) : ")
while saisie != "fin":
    total = total + int(saisie)
    saisie = input("Valeur (fin pour arrêter) : ")
print("Somme :", total)
Sortie
Valeur (fin pour arrêter) : Valeur (fin pour arrêter) : Valeur (fin pour arrêter) : Valeur (fin pour arrêter) : Somme : 10
Compteurs et accumulateurs : un accumulateur (ici total) s'initialise avant la boucle et se met à jour à chaque tour (total = total + …). Un compteur fait pareil en ajoutant 1. On les initialise à 0 pour une somme/un décompte, à 1 pour un produit.

Boucles imbriquées

Une boucle peut en contenir une autre. À chaque tour de la boucle externe, la boucle interne se déroule entièrement — classique pour une table de multiplication.

for i in range(1, 3):        # 2 lignes
    for j in range(1, 4):    # 3 colonnes
        print(i, "x", j, "=", i * j)
Sortie
1 x 1 = 1
1 x 2 = 2
1 x 3 = 3
2 x 1 = 2
2 x 2 = 4
2 x 3 = 6
Sortie●○○

Accumulateur : somme de 1 à 5

Prévoyez la sortie exacte de ce programme.

somme = 0
for i in range(1, 6):
    somme = somme + i
print(somme)
Voir la solution

15. range(1, 6) donne les valeurs 1, 2, 3, 4, 5 (la borne 6 est exclue). L'accumulateur somme part de 0, puis vaut successivement 1, 3, 6, 10 et enfin 15. Le print est hors de la boucle : il n'affiche qu'une fois, à la fin.

QCM●○○

Combien de tours ?

Combien de fois le corps de cette boucle est-il exécuté ?

for i in range(3, 12, 2):
    print(i)
Voir la solution

5 fois. Avec un pas de 2 en partant de 3 et en s'arrêtant avant 12, on obtient les valeurs 3, 5, 7, 9, 11, soit 5 tours. Le distracteur « 9 fois » vient d'un oubli du pas (12 − 3) ; « 4 fois » et « 6 fois » sont des erreurs de off-by-one sur la borne exclue.

Trace●●○

Trace d'un for avec accumulateur (produit)

Complétez la table de trace de ce calcul de produit (factorielle de 4). Indiquez la valeur de produit après l'exécution du corps de la boucle à chaque tour.

produit = 1
for i in range(1, 5):
    produit = produit * i
Touriproduit (après)
Départ1
11
22
33
44
Voir la solution

On part de produit = 1. À chaque tour on multiplie par i :

  • Tour 1 : 1 × 1 = 1
  • Tour 2 : 1 × 2 = 2
  • Tour 3 : 2 × 3 = 6
  • Tour 4 : 6 × 4 = 24

Le produit final vaut 24 (c'est 4! = 4 × 3 × 2 × 1). Noter l'initialisation à 1 et non à 0 : un produit qui démarre à 0 resterait toujours 0.

Trace●●○

Trace d'un while

On divise n par 2 (division entière) jusqu'à atteindre 0, en comptant les étapes. Complétez la trace en donnant, à chaque tour, les valeurs après exécution du corps.

n = 20
etapes = 0
while n > 0:
    n = n // 2
    etapes = etapes + 1
Tourn (après)etapes (après)
Départ200
1
2
3
4
5
Voir la solution

La division entière // arrondit vers le bas : 20//2 = 10, 10//2 = 5, 5//2 = 2, 2//2 = 1, 1//2 = 0. Quand n atteint 0, la condition n > 0 devient fausse et la boucle s'arrête. Elle a fait 5 tours.

Débogage●●○

Boucle infinie : trouver la cause

Ce programme devait afficher les nombres de 1 à 3, mais il ne s'arrête jamais. Pourquoi ?

i = 1
while i <= 3:
    print(i)
    # ... suite du code
Voir la solution

La variable i vaut toujours 1 : rien ne la fait évoluer dans la boucle, donc i <= 3 reste éternellement vraie. Il manque une instruction pour incrémenter i. Correction : ajouter i = i + 1 à la fin du corps de la boucle. On obtient alors :

i = 1
while i <= 3:
    print(i)
    i = i + 1
Sortie
1
2
3
Compléter●●○

Non-entrée : compléter la condition d'une sentinelle

Ce programme lit des mots au clavier et les affiche jusqu'à ce que l'utilisateur tape stop. Complétez la condition de poursuite pour que le mot stop arrête la boucle (et ne soit pas affiché).

mot = input("Mot : ")
while mot  :
    print(mot)
    mot = input("Mot : ")
print("Fini")
Voir la solution

Réponse : != "stop". La boucle continue tant que le mot est différent de la sentinelle. Si l'utilisateur tape stop dès la première saisie, la condition est fausse d'entrée et la boucle n'affiche rien (cas de non-entrée) — c'est le comportement voulu. Attention à lire la première saisie avant le while, sinon mot n'existerait pas encore.

Rédaction●●●

Compter les tickets urgents (sentinelle)

Au guichet d'assistance, on saisit la priorité de chaque ticket (urgent, normal, bas…) jusqu'à taper fin. Écrivez un programme qui compte le nombre de tickets urgent et l'affiche à la fin.

Voir la solution

On utilise un compteur nb initialisé à 0 et une boucle à sentinelle : première saisie avant la boucle, saisie suivante en fin de tour, condition d'arrêt sur "fin".

# Nom : compter_urgents
# Rôle : compte les tickets de priorité "urgent" saisis jusqu'à "fin"
# Entrées : saisies clavier (chaînes), sentinelle "fin"
# Sortie : affiche le nombre de tickets urgents
# Variables locales : nb (entier), saisie (chaîne)
nb = 0
saisie = input("Priorité du ticket (fin pour terminer) : ")
while saisie != "fin":
    if saisie == "urgent":
        nb = nb + 1
    saisie = input("Priorité du ticket (fin pour terminer) : ")
print("Tickets urgents :", nb)
Sortie (saisies : urgent, normal, urgent, bas, urgent, fin)
Priorité du ticket (fin pour terminer) : Priorité du ticket (fin pour terminer) : Priorité du ticket (fin pour terminer) : Priorité du ticket (fin pour terminer) : Priorité du ticket (fin pour terminer) : Priorité du ticket (fin pour terminer) : Tickets urgents : 3

Le texte du input apparaît autant de fois qu'il y a de saisies (6 fois), y compris pour la sentinelle fin qui déclenche la sortie.

5 Fonctions

Une fonction est un bloc de code nommé, réutilisable, qui prend éventuellement des paramètres et peut renvoyer un résultat. Elle évite de recopier le même code et clarifie l'algorithme.

Définir et appeler

On définit avec def, on récupère le résultat avec return. La fonction ne s'exécute qu'au moment de l'appel.

# Nom : aire_rectangle
# Rôle : calcule l'aire d'un rectangle
# Entrées : largeur (réel), hauteur (réel)
# Sortie : aire (réel)
# Variables locales : aucune
def aire_rectangle(largeur, hauteur):
    return largeur * hauteur

resultat = aire_rectangle(4, 3)   # appel + récupération
print("Aire :", resultat)
print("Directement :", aire_rectangle(5, 2))
Sortie
Aire : 12
Directement : 10
En-tête normalisé BTS SIO : avant chaque fonction, on documente en commentaires Nom / Rôle / Entrées / Sortie / Variables locales. C'est attendu dans les rédactions de l'épreuve.

Piège majeur : returnprint

Une fonction qui print affiche une valeur à l'écran ; une fonction qui return renvoie une valeur exploitable dans la suite du programme. Une fonction sans return renvoie None : on ne peut alors ni la stocker utilement, ni la réutiliser dans un calcul.

def double_affiche(n):
    print(n * 2)      # affiche, mais ne renvoie rien

def double_renvoie(n):
    return n * 2      # renvoie une valeur

a = double_affiche(5)   # affiche 10, mais a vaut None
print("a =", a)
b = double_renvoie(5)   # n'affiche rien, mais b vaut 10
print("b =", b)
print("b + 1 =", b + 1) # on peut réutiliser b
Sortie
10
a = None
b = 10
b + 1 = 11
À retenir : si vous voulez réutiliser un résultat (le stocker, le tester, le calculer), utilisez return. print ne sert qu'à montrer à l'écran. Faire b + 1 avec une fonction qui print planterait (None + 1).

Fonction booléenne et plusieurs return

Une fonction peut renvoyer True/False : très pratique dans un if. Un return exécuté quitte immédiatement la fonction (on peut donc en avoir plusieurs).

# Nom : est_pair
# Rôle : indique si un entier est pair
# Entrées : n (entier)
# Sortie : booléen (True si pair, False sinon)
# Variables locales : aucune
def est_pair(n):
    if n % 2 == 0:
        return True
    else:
        return False

print(est_pair(4))
print(est_pair(7))
if est_pair(10):
    print("10 est pair")
Sortie
True
False
10 est pair

Portée : variables locales et globales

Une variable créée dans une fonction est locale : elle n'existe que le temps de l'appel et disparaît ensuite. Une variable définie au niveau principal est globale. Une fonction peut lire une globale sans problème ; si une locale porte le même nom, elle masque la globale sans la modifier.

x = 100

def f():
    x = 5             # variable LOCALE, distincte de la globale
    print("dans f, x =", x)

f()
print("dehors, x =", x)   # la globale n'a pas changé
Sortie
dans f, x = 5
dehors, x = 100
Piège UnboundLocalError : si une fonction modifie une variable qui existe aussi au global (ex. compteur = compteur + 1), Python la traite comme locale et refuse de lire sa valeur avant affectation. La bonne pratique en U22 : passer la valeur en paramètre et renvoyer le nouveau résultat avec return, plutôt que de modifier une globale.
Sortie●○○

Fonction booléenne

Prévoyez la sortie exacte.

def est_majeur(age):
    return age >= 18

print(est_majeur(20))
print(est_majeur(16))
Voir la solution

True puis False. La fonction renvoie directement le résultat de la comparaison age >= 18 : un booléen. Avec 20 on obtient True, avec 16 False. En Python, l'affichage d'un booléen utilise la majuscule : True / False.

Sortie●●○

return vs print

Prévoyez la sortie exacte. Faites attention à ce que vaut r.

def tva(prix):
    return prix * 0.20

def afficher_tva(prix):
    print(prix * 0.20)

montant = tva(100)
print("TVA récupérée :", montant)
afficher_tva(100)
r = afficher_tva(100)
print("r =", r)
Voir la solution

tva(100) renvoie 20.0, stocké dans montant puis affiché : TVA récupérée : 20.0. afficher_tva(100) affiche 20.0 mais ne renvoie rien. Appelé une 2e fois, il affiche encore 20.0. Comme il n'a pas de return, r reçoit None — d'où r = None. C'est le cœur du piège : print montre, return renvoie.

QCM●●○

Portée des variables

Que vaut x à la dernière ligne affichée ?

x = 100

def modifier():
    x = 5
    print("dans modifier :", x)

modifier()
print("après appel :", x)
Voir la solution

100. Le x = 5 écrit à l'intérieur de modifier crée une variable locale qui masque la globale le temps de l'appel, puis disparaît. La globale x vaut toujours 100 après l'appel. Le distracteur (a) est l'erreur classique : croire qu'affecter dans une fonction change la globale.

Compléter●●○

Plusieurs return : maximum

Complétez la fonction pour qu'elle renvoie la plus grande des deux valeurs.

def maximum(a, b):
    if a > b:
         a
     b

print(maximum(3, 8))
print(maximum(10, 4))
Voir la solution

Il faut return a dans le if et return b ensuite. Dès qu'un return s'exécute, la fonction s'arrête : si a > b, on renvoie a et on ne va jamais jusqu'au return b. Sinon on tombe sur return b. Sortie : 8 puis 10. Attention à utiliser return et non print, sinon la fonction renverrait None.

Débogage●●○

Oubli du return

On veut afficher le carré de 5 augmenté de 1 (soit 26), mais ce code provoque une erreur. Pourquoi ?

def carre(n):
    print(n * n)     # affiche au lieu de renvoyer

resultat = carre(5)
print(resultat + 1)
Voir la solution

Comme carre utilise print et non return, elle renvoie None. Donc resultat vaut None, et resultat + 1 déclenche une TypeError. Il faut renvoyer la valeur :

def carre(n):
    return n * n

resultat = carre(5)
print(resultat + 1)
Sortie
26
Rédaction●●●

Moyenne de notes (en-tête normalisé)

Écrivez une fonction moyenne_notes(notes) qui reçoit une liste de notes et renvoie leur moyenne. Précédez-la de l'en-tête normalisé BTS SIO. Testez-la sur [12, 8, 16] et [10, 10, 10, 10].

Voir la solution

On accumule le total et on compte le nombre de notes dans une boucle, puis on renvoie le quotient. La division / donne un résultat réel.

# Nom : moyenne_notes
# Rôle : calcule la moyenne d'une liste de notes
# Entrées : notes (liste de réels)
# Sortie : moyenne (réel)
# Variables locales : total (réel), n (entier)
def moyenne_notes(notes):
    total = 0
    n = 0
    for note in notes:
        total = total + note
        n = n + 1
    return total / n

print(moyenne_notes([12, 8, 16]))
print(moyenne_notes([10, 10, 10, 10]))
Sortie
12.0
10.0

On obtient 12.0 ((12+8+16)/3) et 10.0. Le résultat porte un .0 car la division / renvoie toujours un réel. On utilise return (et non print) pour que la moyenne soit réutilisable ailleurs dans le programme.

6 Tableaux 1D (listes)

Une liste range plusieurs valeurs dans une seule variable, sous forme d'une suite ordonnée. C'est le tableau à une dimension (1D) du programme : un relevé de températures, une série de notes, un stock d'articles…

Créer une liste

vide = []                     # liste vide
notes = [12, 8, 15, 17]       # liste litterale (4 valeurs)
zeros = [0] * 5               # [0, 0, 0, 0, 0] : 5 cases a 0

Longueur et accès par indice

Les cases sont numérotées à partir de 0. Pour une liste de len(t) éléments, les indices valides vont donc de 0 à len(t) - 1. Un indice hors de ces bornes déclenche une erreur IndexError. Les indices négatifs comptent depuis la fin : t[-1] est le dernier élément.

temperatures = [18, 21, 19, 23, 20]
print(len(temperatures))
print(temperatures[0])
print(temperatures[4])
print(temperatures[-1])
Sortie
5
18
20
20
Piège : une liste de 5 éléments s'indexe de 0 à 4. Écrire temperatures[5] provoque IndexError: list index out of range. Le dernier indice vaut toujours len(t) - 1.

Modifier, ajouter

On remplace une case avec t[i] = … et on ajoute une valeur à la fin avec t.append(…).

notes = [12, 8, 15]
notes[1] = 10          # correction de la 2e note
notes.append(17)       # ajout d'une note
print(notes)
print(len(notes))
Sortie
[12, 10, 15, 17]
4

Parcourir : par indice ou par élément ?

Deux façons de parcourir, à choisir selon le besoin :

  • Par élément (for x in t) : quand on ne veut que les valeurs (somme, recherche, comptage). Plus simple, à préférer par défaut.
  • Par indice (for i in range(len(t))) : quand on a besoin de la position (afficher le numéro, modifier une case, comparer t[i] à t[i+1]).
stock = [4, 0, 7, 2]
# Parcours par indice : on a besoin de la position
for i in range(len(stock)):
    print("Article", i, "->", stock[i])
# Parcours par element : on veut juste les valeurs
total = 0
for q in stock:
    total = total + q
print("Total :", total)
Sortie
Article 0 -> 4
Article 1 -> 0
Article 2 -> 7
Article 3 -> 2
Total : 13

Recherche séquentielle, min, somme (aperçu)

Chercher une valeur, calculer un minimum ou une somme se fait en parcourant la liste avec un accumulateur (un total, un compteur, un booléen de présence). Ces algorithmes classiques — recherche, minimum/maximum, moyenne — sont détaillés en section 11 (Algorithmes classiques).

Trier : t.sort() trie la liste sur place (ordre croissant). Le fonctionnement d'un tri écrit « à la main » est traité en section 11.
QCM●○○

Accès par indice

Soit t = [5, 9, 3, 8]. Que vaut t[2] ?

Voir la solution

Les indices commencent à 0 : t[0]=5, t[1]=9, t[2]=3, t[3]=8. L'indice 2 désigne donc le troisième élément, soit 3. Piège fréquent : confondre l'indice (2) et le rang (3e).

Interprétation●○○

Que fait ce code ?

Que fait ce fragment ?

t = [3, 1, 4, 1, 5]
r = t[0]
for x in t:
    if x > r:
        r = x
print(r)
Voir la solution

On part de r = t[0] puis on garde dans r tout élément plus grand que la valeur courante : c'est la recherche du maximum. Ici le maximum vaut 5. Pour le minimum, on remplacerait > par <.

Sortie●○○

Prévoir la sortie

Quelle est la sortie exacte ?

capteurs = [10, 25, 25, 40]
print(capteurs[2])
print(len(capteurs))
capteurs.append(55)
print(capteurs)
Voir la solution

capteurs[2] vaut 25 (3e case). len(capteurs) vaut 4 avant l'ajout. append(55) ajoute la valeur en fin, la liste devient [10, 25, 25, 40, 55].

25
4
[10, 25, 25, 40, 55]
Sortie●●○

Comptage par accumulateur

On compte les tickets d'assistance résolus ("ok"). Quelle est la sortie ?

mots = ["ok", "err", "ok", "ok"]
compte = 0
for m in mots:
    if m == "ok":
        compte = compte + 1
print(compte)
Voir la solution

Le compteur compte part de 0 et s'incrémente à chaque "ok". Il y en a trois (positions 0, 2 et 3), donc la sortie est 3. C'est le schéma de comptage par accumulateur : on initialise à 0, on ajoute 1 quand la condition est vraie.

Trace●●○

Somme d'une liste

Complétez la table de trace de ce calcul de somme pour t = [5, 2, 8].

t = [5, 2, 8]
somme = 0
for i in range(len(t)):
    valeur = t[i]
    somme = somme + valeur
    # on note l'etat ici, a chaque tour
ivaleur (t[i])somme
0
0
1
2
Voir la solution

somme vaut 0 avant la boucle. À chaque tour on lit valeur = t[i] et on l'ajoute :

  • i=0 : valeur=5, somme = 0+5 = 5
  • i=1 : valeur=2, somme = 5+2 = 7
  • i=2 : valeur=8, somme = 7+8 = 15

Résultat final : 15.

Débogage●●○

Corriger l'IndexError

Ce code plante avec IndexError: list index out of range. Corrigez la ligne de la boucle pour parcourir exactement les 3 cases.

t = [3, 6, 9]
for i in range():
    print(t[i])
Voir la solution

Le code fautif utilisait range(len(t) + 1), soit range(4) : à l'itération i = 3, t[3] n'existe pas (indices valides 0 à 2) d'où l'IndexError. La bonne borne est range(len(t)) : elle produit les indices 0, 1, 2, exactement les cases existantes.

t = [3, 6, 9]
for i in range(len(t)):
    print(t[i])
Sortie
3
6
9
Rédaction●●○

Moyenne d'un relevé

Écrivez un programme qui calcule et affiche la moyenne d'un relevé de températures [18, 21, 19, 23, 20].

Voir la solution

On accumule le total dans une variable partant de 0, puis on divise par le nombre d'éléments (len). On parcourt par élément car seules les valeurs comptent.

# Nom : moyenne_temperatures
# Role : calcule la moyenne d'un releve de temperatures
# Entrees : releve (liste de reels)
# Sortie : moyenne (reel)
# Variables locales : total (reel), n (entier)

releve = [18, 21, 19, 23, 20]
total = 0
for t in releve:
    total = total + t
n = len(releve)
moyenne = total / n
print("Moyenne :", moyenne)
Sortie
Moyenne : 20.2
Rédaction●●●

Chercher une valeur

Écrivez un programme qui indique (par un booléen) si la valeur 30 est présente dans le relevé [10, 25, 30, 25, 12]. C'est une recherche séquentielle.

Voir la solution

On utilise un accumulateur booléen present initialisé à False. Dès qu'on rencontre la valeur cherchée, on le passe à True. On parcourt par élément.

# Nom : contient_valeur
# Role : indique si une valeur est presente dans un releve
# Entrees : releve (liste d'entiers), cible (entier)
# Sortie : present (booleen)
# Variables locales : present (booleen)

releve = [10, 25, 30, 25, 12]
cible = 30
present = False
for v in releve:
    if v == cible:
        present = True
print(present)
Sortie
True

Astuce : en Python on peut aussi écrire directement cible in releve, mais l'épreuve attend souvent l'algorithme détaillé.

7 Tableaux 2D & traitement d'image

Un tableau à deux dimensions (une matrice) est une liste de listes : chaque élément de la liste extérieure est lui-même une liste (une ligne). C'est l'outil du CCF pour représenter une grille ou une image.

Accès m[i][j] : ligne puis colonne

On accède à un élément par deux indices : m[i][j] lit la ligne i puis la colonne j (les deux à partir de 0). Le nombre de lignes est len(m), le nombre de colonnes est len(m[0]).

m = [[1, 2, 3],
     [4, 5, 6]]
print(m[0][2])          # ligne 0, colonne 2
print(m[1][0])          # ligne 1, colonne 0
print(len(m))           # nombre de lignes
print(len(m[0]))        # nombre de colonnes
Sortie
3
4
2
3
Piège d'ordre : c'est toujours m[ligne][colonne], jamais l'inverse. m[1][0] (ligne 1, colonne 0) n'est pas m[0][1] (ligne 0, colonne 1).

Double boucle de parcours

Pour visiter toutes les cases, on imbrique deux boucles : la boucle extérieure sur les lignes, l'intérieure sur les colonnes.

m = [[1, 2, 3],
     [4, 5, 6],
     [7, 8, 9]]
total = 0
for i in range(len(m)):
    for j in range(len(m[0])):
        total = total + m[i][j]
print("Somme totale :", total)
# Somme par ligne
for i in range(len(m)):
    s = 0
    for j in range(len(m[0])):
        s = s + m[i][j]
    print("Ligne", i, ":", s)
Sortie
Somme totale : 45
Ligne 0 : 6
Ligne 1 : 15
Ligne 2 : 24
Somme par ligne : on remet l'accumulateur s à 0 à l'intérieur de la boucle des lignes (juste avant la boucle des colonnes), sinon on cumulerait toutes les lignes ensemble.

Application phare : image en niveaux de gris

Une image en niveaux de gris est une matrice de pixels : chaque valeur est un entier de 0 (noir) à 255 (blanc). Les traitements classiques modifient chaque pixel :

  • Éclaircir de n : min(255, v + n) — on ajoute, mais on écrête à 255 pour ne pas dépasser.
  • Assombrir de n : max(0, v - n) — on soustrait, écrêtage à 0.
  • Négatif : 255 - v — le noir devient blanc et inversement.
  • Contraste (seuillage) : 0 si v < seuil, sinon 255 — on obtient une image en noir et blanc pur.
# Image 3x3 en niveaux de gris (0=noir ... 255=blanc)
img = [[100, 250, 30],
       [ 60, 200, 255],
       [  0, 128, 180]]
n = 40
# Eclaircir : +n avec ecretage a 255
clair = []
for i in range(len(img)):
    ligne = []
    for j in range(len(img[0])):
        ligne.append(min(255, img[i][j] + n))
    clair.append(ligne)
print(clair)
Sortie
[[140, 255, 70], [100, 240, 255], [40, 168, 220]]
Écrêtage indispensable : sans min(255, …), un pixel à 250 éclairci de 40 vaudrait 290 — impossible pour un niveau de gris. min (plafond 255) et max (plancher 0) garantissent de rester dans 0..255.
QCM●○○

Accès à un pixel

Soit la matrice ci-dessous. Que vaut m[1][2] ?

m = [[7, 4, 1],
     [2, 9, 5],
     [6, 3, 8]]
Voir la solution

m[1] est la deuxième ligne [2, 9, 5]. Sa colonne d'indice 2 est 5. Piège : ne pas lire colonne d'abord — c'est bien ligne (1) puis colonne (2).

QCM●○○

Dimensions d'une matrice

Pour m = [[1, 2, 3, 4], [5, 6, 7, 8]], que valent len(m) et len(m[0]) ?

Voir la solution

len(m) compte les éléments de la liste extérieure : les lignes, ici 2. len(m[0]) compte les éléments de la première ligne : les colonnes, ici 4. Cette matrice a donc 2 lignes et 4 colonnes.

Sortie●●○

Négatif d'une image

Quelle est la sortie exacte de ce calcul du négatif ?

img = [[10, 20],
       [30, 40]]
# Negatif : 255 - v
for i in range(len(img)):
    for j in range(len(img[0])):
        img[i][j] = 255 - img[i][j]
print(img)
Voir la solution

Chaque pixel v devient 255 - v : 10→245, 20→235, 30→225, 40→215. La matrice modifiée sur place vaut [[245, 235], [225, 215]].

Compléter●●○

Double boucle du négatif

Complétez la double boucle qui transforme img en son négatif (chaque pixel devient 255 - v).

img = [[0, 255, 128],
       [64, 200, 32]]
for i in range(len(img)):
    for j in range(len()):
        img[i][j] = 
print(img)
Voir la solution

La borne des colonnes est len(img[0]) (nombre de colonnes de la première ligne). Le négatif d'un pixel est 255 - img[i][j]. On réaffecte la case pour modifier l'image sur place.

img = [[0, 255, 128],
       [64, 200, 32]]
for i in range(len(img)):
    for j in range(len(img[0])):
        img[i][j] = 255 - img[i][j]
print(img)
Sortie
[[255, 0, 127], [191, 55, 223]]
Compléter●●●

Contraste par seuillage

Complétez le seuillage : un pixel devient 0 s'il est inférieur au seuil, sinon 255.

img = [[100, 130, 50],
       [200, 30, 128]]
seuil = 128
for i in range(len(img)):
    for j in range(len(img[0])):
        if img[i][j]  seuil:
            img[i][j] = 
        else:
            img[i][j] = 
print(img)
Voir la solution

Le test est img[i][j] < seuil : en dessous du seuil on met 0 (noir), sinon 255 (blanc). Attention, un pixel égal au seuil (128) n'est pas « inférieur » : il passe donc à 255.

img = [[100, 130, 50],
       [200, 30, 128]]
seuil = 128
for i in range(len(img)):
    for j in range(len(img[0])):
        if img[i][j] < seuil:
            img[i][j] = 0
        else:
            img[i][j] = 255
print(img)
Sortie
[[0, 255, 0], [255, 0, 255]]
Rédaction●●○

Luminosité moyenne d'une image

Écrivez un programme qui calcule la luminosité moyenne (moyenne de tous les pixels) d'une image 3×3 en niveaux de gris.

Voir la solution

On accumule la somme de tous les pixels et on compte le nombre de pixels avec la double boucle, puis on divise. Le nombre de pixels vaut aussi len(img) * len(img[0]).

# Nom : moyenne_pixels
# Role : calcule la luminosite moyenne d'une image en niveaux de gris
# Entrees : img (matrice d'entiers 0..255)
# Sortie : moyenne (reel)
# Variables locales : total (entier), nb (entier)

img = [[100, 150, 200],
       [ 50,  80, 120],
       [  0, 255, 128]]
total = 0
nb = 0
for i in range(len(img)):
    for j in range(len(img[0])):
        total = total + img[i][j]
        nb = nb + 1
moyenne = total / nb
print("Luminosite moyenne :", moyenne)
Sortie
Luminosite moyenne : 120.33333333333333
Rédaction●●●

Assombrir une image

Écrivez un programme qui assombrit une image de n = 60 : chaque pixel devient v - n, sans jamais descendre sous 0 (écrêtage). Construisez une nouvelle matrice res.

Voir la solution

On construit une nouvelle matrice ligne par ligne. Pour chaque pixel, max(0, v - n) soustrait n tout en gardant un plancher de 0 (un pixel à 30 assombri de 60 donnerait -30, écrêté à 0).

# Nom : assombrir
# Role : assombrit une image en niveaux de gris (soustrait n, ecretage a 0)
# Entrees : img (matrice d'entiers 0..255), n (entier)
# Sortie : res (nouvelle matrice assombrie)
# Variables locales : res (matrice), ligne (liste)

img = [[50, 10, 200],
       [30, 90, 255]]
n = 60
res = []
for i in range(len(img)):
    ligne = []
    for j in range(len(img[0])):
        ligne.append(max(0, img[i][j] - n))
    res.append(ligne)
print(res)
Sortie
[[0, 0, 140], [0, 30, 195]]

8 Chaînes de caractères

Une chaîne (type str) est une suite ordonnée de caractères. Elle se manipule comme une liste en lecture : longueur, accès par indice, parcours — mais elle est immuable (on ne peut pas modifier un caractère en place).

Longueur, indexation, parcours

len(s) donne le nombre de caractères ; s[i] lit le caractère d'indice i (de 0 à len(s) - 1) ; s[-1] est le dernier. On parcourt caractère par caractère avec for c in s, ou par indice avec for i in range(len(s)).

mot = "Python"
print(len(mot))
print(mot[0])
print(mot[5])
print(mot[-1])
Sortie
6
P
n
n

Concaténation, répétition, comparaison

+ assemble deux chaînes (concaténation), * répète une chaîne. Les opérateurs ==, <, > comparent les chaînes (ordre alphabétique, majuscules avant minuscules dans le code des caractères).

prenom = "Ada"
nom = "Lovelace"
identite = prenom + " " + nom     # concatenation
ligne = "-" * 10                  # repetition
print(identite)
print(ligne)
print(len(identite))
Sortie
Ada Lovelace
----------
12
Immuabilité : écrire s[0] = "x" déclenche TypeError: 'str' object does not support item assignment. Pour « modifier » une chaîne, on en construit une nouvelle en accumulant les caractères.

Compter, construire une nouvelle chaîne

Deux schémas d'accumulateur reviennent sans cesse : compter un caractère (accumulateur entier partant de 0) et construire une chaîne (accumulateur chaîne partant de "", la chaîne vide).

mot = "assistance"
# Compter le nombre de 's'
compte = 0
for c in mot:
    if c == "s":
        compte = compte + 1
print("Nombre de 's' :", compte)
# Construire une nouvelle chaine : le mot a l'envers
inverse = ""
for c in mot:
    inverse = c + inverse         # on empile a gauche
print("Inverse :", inverse)
Sortie
Nombre de 's' : 3
Inverse : ecnatsissa
Inverser par accumulation : inverse = c + inverse place chaque nouveau caractère devant les précédents. En fin de boucle, la chaîne est retournée.

Palindrome (itératif)

Un mot est un palindrome s'il se lit pareil à l'endroit et à l'envers (radar, kayak). On compare le caractère d'indice i à son symétrique d'indice len(s) - 1 - i : dès qu'une paire diffère, ce n'est pas un palindrome.

Symétrie des indices : dans un mot de n caractères, l'indice 0 se compare à n-1, l'indice 1 à n-2, etc. C'est la clé du test de palindrome.
QCM●○○

Indexation d'une chaîne

Soit s = "reseau". Que vaut s[3] ?

Voir la solution

On numérote à partir de 0 : s[0]="r", s[1]="e", s[2]="s", s[3]="e", s[4]="a", s[5]="u". L'indice 3 vaut donc "e".

Interprétation●●○

Que fait ce code ?

Que construit ce fragment dans r ?

s = "abc"
r = ""
for c in s:
    r = r + c + c
print(r)
Voir la solution

À chaque tour on ajoute le caractère deux fois à la fin de r : après 'a' → "aa", après 'b' → "aabb", après 'c' → "aabbcc". C'est une construction de chaîne par accumulation, chaque caractère étant dupliqué.

Sortie●○○

Concaténation et répétition

Quelle est la sortie exacte ?

s = "SIO"
print(s[0])
print(s * 3)
print(len(s) + 1)
Voir la solution

s[0] est le premier caractère S. s * 3 répète la chaîne : SIOSIOSIO. len(s) vaut 3, donc len(s) + 1 vaut 4.

S
SIOSIOSIO
4
Sortie●●○

Compter les voyelles

On compte les voyelles d'un mot de passe. Quelle est la sortie ?

mdp = "Abc123"
voyelles = "aeiouyAEIOUY"
compte = 0
for c in mdp:
    if c in voyelles:
        compte = compte + 1
print(compte)
Voir la solution

On teste chaque caractère avec c in voyelles. Dans "Abc123", seule la lettre A figure dans la liste des voyelles (les lettres b, c et les chiffres n'y sont pas). La sortie est donc 1.

Trace●●○

Inversion d'une chaîne

Complétez la table de trace de la construction de l'inverse pour mot = "abc".

mot = "abc"
inverse = ""
for i in range(len(mot)):
    c = mot[i]
    inverse = c + inverse
    # etat note ici a chaque tour
ic (mot[i])inverse
"" (vide)
0
1
2
Voir la solution

inverse part de "". À chaque tour on place le nouveau caractère devant :

  • i=0 : c='a', inverse = "a" + "" = "a"
  • i=1 : c='b', inverse = "b" + "a" = "ba"
  • i=2 : c='c', inverse = "c" + "ba" = "cba"

Résultat final : "cba".

Compléter●●●

Test du palindrome

Complétez le test de palindrome : on compare le caractère i à son symétrique. Quel est l'indice du caractère symétrique de i dans un mot de longueur n ?

def est_palindrome(mot):
    n = len(mot)
    for i in range(n):
        if mot[i] != mot[]:
            return False
    return True

print(est_palindrome("radar"))
print(est_palindrome("python"))
Voir la solution

Le symétrique de l'indice i est n - 1 - i : pour i=0 c'est n-1 (dernier caractère), pour i=1 c'est n-2, etc. Dès qu'une paire diffère, on renvoie False ; si la boucle se termine sans écart, le mot est un palindrome.

def est_palindrome(mot):
    n = len(mot)
    for i in range(n):
        if mot[i] != mot[n - 1 - i]:
            return False
    return True

print(est_palindrome("radar"))
print(est_palindrome("python"))
print(est_palindrome("ressasser"))
Sortie
True
False
True
Rédaction●●○

Compter les voyelles d'un mot de passe

Écrivez un programme qui compte le nombre de voyelles du mot de passe "Motdepasse2024" (voyelles majuscules et minuscules).

Voir la solution

On place toutes les voyelles acceptées dans une chaîne de référence, puis on parcourt le mot de passe caractère par caractère : chaque caractère présent dans cette référence incrémente le compteur.

# Nom : compter_voyelles
# Role : compte le nombre de voyelles dans un mot de passe
# Entrees : mdp (chaine)
# Sortie : nb (entier)
# Variables locales : voyelles (chaine), nb (entier)

mdp = "Motdepasse2024"
voyelles = "aeiouyAEIOUY"
nb = 0
for c in mdp:
    if c in voyelles:
        nb = nb + 1
print("Nombre de voyelles :", nb)
Sortie
Nombre de voyelles : 4

Les voyelles de « Motdepasse2024 » sont o, e, a, e — soit 4.

Rédaction●●●

Compter un caractère

Écrivez un programme qui compte le nombre d'occurrences du caractère "a" dans le mot "banane".

Voir la solution

Schéma de comptage classique : un accumulateur entier part de 0 et s'incrémente chaque fois que le caractère courant vaut la cible. On parcourt par élément.

# Nom : compter_caractere
# Role : compte les occurrences d'un caractere dans une chaine
# Entrees : texte (chaine), cible (chaine d'un caractere)
# Sortie : nb (entier)
# Variables locales : nb (entier)

texte = "banane"
cible = "a"
nb = 0
for c in texte:
    if c == cible:
        nb = nb + 1
print(nb)
Sortie
2

9 Arithmétique & bases

Division euclidienne : quotient // et reste %

Diviser un entier a par un entier b (non nul), c'est trouver un quotient q et un reste r tels que a = b × q + r, avec 0 ≤ r < b. En Python, // donne le quotient entier et % le reste (« modulo »).

a = 47
b = 5
q = a // b   # quotient entier
r = a % b    # reste
print("quotient :", q)
print("reste    :", r)
print("verif a = b*q + r ->", b * q + r)
Sortie
quotient : 9
reste    : 2
verif a = b*q + r -> 47

Parité, divisibilité, congruence

Un nombre est pair si son reste dans la division par 2 est nul : n % 2 == 0. Plus généralement, n est divisible par k si n % k == 0 (le reste est nul).

for n in [10, 7, 12]:
    if n % 2 == 0:
        print(n, ": pair")
    else:
        print(n, ": impair")
print("15 divisible par 3 ?", 15 % 3 == 0)
print("15 divisible par 4 ?", 15 % 4 == 0)
Sortie
10 : pair
7 : impair
12 : pair
15 divisible par 3 ? True
15 divisible par 4 ? False

Application : secondes → heures / minutes / secondes

// et % se combinent pour décomposer une durée. On divise par 3600 pour les heures, puis on répartit le reste en minutes et secondes.

total = 3725
heures = total // 3600
reste = total % 3600
minutes = reste // 60
secondes = reste % 60
print(heures, "h", minutes, "min", secondes, "s")
Sortie
1 h 2 min 5 s

Arrondi round(x, n) et piège des flottants

round(x, n) arrondit x à n décimales. Attention : les nombres à virgule (flottants) sont stockés en binaire, donc de façon approchée. Certaines égalités « évidentes » sont fausses.

print(round(3.14159, 2))
print(round(2.5), round(3.5))   # arrondi au pair le plus proche
print(0.1 + 0.2)
print(0.1 + 0.2 == 0.3)
Sortie
3.14
2 4
0.30000000000000004
False
Piège des flottants. 0.1 + 0.2 ne vaut pas exactement 0.3 mais 0.30000000000000004. On ne teste donc jamais l'égalité de deux flottants avec == ; on compare plutôt un écart : abs(x - y) < 0.0001. Notez aussi que round(2.5) vaut 2 (Python arrondit au pair le plus proche).

Conversion décimal → binaire (divisions successives par 2)

On divise le nombre par 2, on note le reste (0 ou 1), on recommence avec le quotient jusqu'à obtenir 0. Le nombre binaire se lit en remontant les restes (du dernier au premier). Exemple avec 13 :

DivisionQuotientReste
13 ÷ 261
6 ÷ 230
3 ÷ 211
1 ÷ 201

En lisant les restes de bas en haut : 1101. La même méthode s'écrit en boucle while (on empile chaque reste à gauche) :

n = 13
bits = ""
while n > 0:
    bits = str(n % 2) + bits   # on empile le reste a gauche
    n = n // 2
print("13 en binaire :", bits)
print("verif bin() :", bin(13))
Sortie
13 en binaire : 1101
verif bin() : 0b1101

Conversion binaire → décimal (poids des puissances de 2)

Chaque bit a un poids : de droite à gauche, 2⁰, 2¹, 2², 2³… Pour 1101 : 1×8 + 1×4 + 0×2 + 1×1 = 13. En boucle, on multiplie l'accumulateur par 2 et on ajoute le chiffre courant (méthode de Horner) :

binaire = "1101"
valeur = 0
for chiffre in binaire:
    valeur = valeur * 2 + int(chiffre)   # methode de Horner
print("1101 en decimal :", valeur)
print("verif int() :", int("1101", 2))
Sortie
1101 en decimal : 13
verif int() : 13

Hexadécimal (base 16)

La base 16 utilise 16 chiffres : 0 à 9 puis A=10, B=11, C=12, D=13, E=14, F=15. Un chiffre hexadécimal code exactement 4 bits (un « quartet »), donc un octet (8 bits) tient sur 2 chiffres hexa. Exemple : 1010 1010 en binaire = AA en hexa = 170 en décimal.

print("hex de 255 :", hex(255))
print("hex de 26  :", hex(26))
print("0xFF en decimal :", int("FF", 16))
print("0x1A en decimal :", int("1A", 16))
Sortie
hex de 255 : 0xff
hex de 26  : 0x1a
0xFF en decimal : 255
0x1A en decimal : 26
Fonctions de contrôle. bin(n), hex(n) et int("1101", 2) convertissent instantanément. À l'épreuve, on attend la méthode manuelle (divisions successives, poids des puissances) et son écriture en boucle : ces fonctions ne servent qu'à vérifier votre résultat. Le préfixe 0b annonce du binaire, 0x de l'hexadécimal.

QCM●○○

Reste d'une division

Que vaut l'expression 47 % 5 en Python ?

Voir la solution

La réponse est 2. L'opérateur % donne le reste de la division entière, pas le quotient. Ici 47 = 5 × 9 + 2, donc 47 // 5 vaut 9 (le quotient) et 47 % 5 vaut 2 (le reste). La réponse 9.4 correspondrait à la division réelle 47 / 5.

QCM●○○

Du binaire au décimal

Le nombre 1011 écrit en base 2 vaut combien en base 10 ?

Voir la solution

La réponse est 11. On additionne les poids des bits à 1, de droite à gauche : 1×2³ + 0×2² + 1×2¹ + 1×2⁰ = 8 + 0 + 2 + 1 = 11. On peut vérifier avec int("1011", 2) qui renvoie 11.

QCM●●○

Égalité de flottants

Que produit print(0.1 + 0.2 == 0.3) ?

Voir la solution

La réponse est False. En binaire, 0.1 et 0.2 n'ont pas de représentation exacte : leur somme donne 0.30000000000000004, légèrement différent de 0.3. C'est pour cela qu'on ne compare jamais deux flottants avec ==, mais via un écart : abs((0.1 + 0.2) - 0.3) < 0.0001.

Sortie●●○

Conversion pas à pas

Ce programme convertit 22 en binaire par divisions successives. Écrivez sa sortie exacte.

n = 22
bits = ""
while n > 0:
    print("n =", n, "reste =", n % 2)
    bits = str(n % 2) + bits
    n = n // 2
print("resultat :", bits)
Voir la solution

À chaque tour on affiche n et son reste par 2, avant de diviser. Les quotients successifs sont 22, 11, 5, 2, 1 (puis 0 : la boucle s'arrête). Les restes lus de bas en haut donnent 10110, soit 22 en binaire (16 + 4 + 2).

n = 22 reste = 0
n = 11 reste = 1
n = 5 reste = 1
n = 2 reste = 0
n = 1 reste = 1
resultat : 10110
Compléter●●○

Détecter les multiples de 3

Complétez la condition pour n'afficher que les entiers de 1 à 20 divisibles par 3.

for i in range(1, 21):
    if i  3   0:
        print(i)
Voir la solution

La condition « i est divisible par 3 » s'écrit i % 3 == 0 : le reste de la division de i par 3 doit être nul. On complète donc par % puis ==. Le programme affiche alors 3, 6, 9, 12, 15, 18.

Rédaction●●●

Convertir un octet saisi

Un technicien saisit un octet binaire (8 chiffres 0/1, par exemple issu d'un masque réseau). Rédigez un programme qui lit cette chaîne au clavier et affiche sa valeur décimale, sans utiliser int(octet, 2).

Voir la solution

On parcourt les chiffres de gauche à droite. À chaque chiffre, on multiplie l'accumulateur par 2 (décalage d'un rang) et on ajoute le nouveau chiffre : c'est la méthode de Horner, qui applique directement les poids des puissances de 2.

# Nom : octet_vers_decimal
# Role : convertit un octet binaire (8 bits) saisi au clavier en decimal
# Entrees : octet (chaine de 0 et 1)
# Sortie : affiche la valeur decimale
# Variables locales : valeur (entier), chiffre (chaine)
octet = input("Entrez un octet binaire : ")
valeur = 0
for chiffre in octet:
    valeur = valeur * 2 + int(chiffre)
print("Valeur decimale :", valeur)
Sortie (saisie : 10011010)
Entrez un octet binaire : Valeur decimale : 154

Ici 10011010 vaut 128 + 16 + 8 + 2 = 154. Le prompt de input() apparaît, mais la valeur tapée n'est pas ré-affichée par le programme.

Débogage●●●

Binaire → décimal qui se trompe

Cette fonction devrait convertir "10110" (base 2) en décimal (22 attendu), mais elle renvoie autre chose. Où est l'erreur ?

binaire = "10110"
valeur = 0
for chiffre in binaire:
    valeur = valeur + int(chiffre)   # ligne suspecte
print(valeur)
Voir la solution

La version fautive se contente d'additionner les chiffres : elle renvoie 1+0+1+1+0 = 3 au lieu de 22. Elle ignore le poids de chaque position. Il faut multiplier l'accumulateur par 2 à chaque tour : valeur = valeur * 2 + int(chiffre). La version corrigée renvoie bien :

binaire = "10110"
valeur = 0
for chiffre in binaire:
    valeur = valeur * 2 + int(chiffre)
print(valeur)
print("verif :", int("10110", 2))
Sortie
22
verif : 22

10 Suites numériques

Suite arithmétique : on ajoute toujours la même raison

Une suite arithmétique passe d'un terme au suivant en ajoutant une constante r appelée raison : u(n+1) = u(n) + r. Le terme général se calcule directement : u(n) = u(0) + n × r. En Python, on part de u(0) et on répète l'addition dans une boucle.

# Suite arithmetique : u(0)=5, raison r=3
u = 5
r = 3
for n in range(6):
    print("u(" + str(n) + ") =", u)
    u = u + r
Sortie
u(0) = 5
u(1) = 8
u(2) = 11
u(3) = 14
u(4) = 17
u(5) = 20

Suite géométrique : on multiplie toujours par la même raison

Une suite géométrique passe d'un terme au suivant en multipliant par une constante q (la raison) : u(n+1) = u(n) × q. Le terme général vaut u(n) = u(0) × qⁿ. C'est le modèle de la croissance (intérêts, population…).

# Suite geometrique : u(0)=2, raison q=3
u = 2
q = 3
for n in range(6):
    print("u(" + str(n) + ") =", u)
    u = u * q
Sortie
u(0) = 2
u(1) = 6
u(2) = 18
u(3) = 54
u(4) = 162
u(5) = 486
Reconnaître la nature d'une suite. Si l'écart entre deux termes consécutifs est constant → arithmétique (raison = cet écart). Si le rapport entre deux termes consécutifs est constant → géométrique (raison = ce rapport).

Somme des premiers termes (accumulateur)

Pour additionner plusieurs termes, on utilise une variable accumulateur somme initialisée à 0. À chaque tour, on ajoute le terme courant à somme, puis on calcule le terme suivant.

# Somme des premiers termes d'une suite arithmetique (u0=5, r=3)
u = 5
r = 3
somme = 0
for n in range(6):   # termes u(0) a u(5)
    somme = somme + u
    u = u + r
print("somme des 6 premiers termes :", somme)
Sortie
somme des 6 premiers termes : 75

Suites récurrentes : le terme suivant dépend du précédent

Une suite peut être définie par une relation de récurrence quelconque, par exemple u(n+1) = 2 × u(n) + 1. On applique simplement la formule à chaque tour, en repartant du terme calculé au tour précédent.

# Suite recurrente : u(0)=1, u(n+1)=2*u(n)+1
u = 1
for n in range(6):
    print("u(" + str(n) + ") =", u)
    u = 2 * u + 1
Sortie
u(0) = 1
u(1) = 3
u(2) = 7
u(3) = 15
u(4) = 31
u(5) = 63

Fibonacci en itératif (deux variables)

La suite de Fibonacci additionne les deux termes précédents : chaque terme est la somme des deux d'avant (0, 1, 1, 2, 3, 5, 8…). On la calcule sans récursivité, avec deux variables a et b mises à jour simultanément par affectation multiple : a, b = b, a + b.

# Fibonacci ITERATIF (deux variables a, b)
a = 0
b = 1
for i in range(8):
    print(a, end=" ")
    a, b = b, a + b
print()
Sortie
0 1 1 2 3 5 8 13 
Récursivité : hors programme. Fibonacci et le PGCD s'écrivent souvent de façon récursive (une fonction qui s'appelle elle-même), mais la récursivité n'est pas attendue à l'épreuve U22. On traite donc ces suites en itératif (avec une boucle). L'affectation multiple a, b = b, a + b évalue d'abord la partie droite, ce qui évite d'écraser a avant de s'en servir.

Recherche de seuil avec while

Question typique : « à partir de quel rang la suite dépasse-t-elle une valeur ? ». On ne connaît pas le nombre de tours à l'avance → on utilise une boucle while qui tourne tant que le seuil n'est pas franchi, en comptant les tours. Exemple : une épargne à 5 % par an, à partir de quelle année dépasse-t-on 1500 € ?

# Epargne qui rapporte 5% par an, capital de depart 1000
# A partir de quelle annee depasse-t-on 1500 ?
capital = 1000.0
annee = 0
while capital <= 1500:
    capital = capital * 1.05
    annee = annee + 1
print("annee :", annee)
print("capital :", round(capital, 2))
Sortie
annee : 9
capital : 1551.33
while ou for ? On prend for quand le nombre de tours est connu (calculer les 10 premiers termes). On prend while quand on répète jusqu'à une condition (dépasser un seuil, doubler un capital) : le nombre de tours est justement le résultat cherché.

QCM●○○

Nature de la suite

Une suite commence par 3, 6, 12, 24, 48. De quelle nature est-elle ?

Voir la solution

La réponse est géométrique de raison 2. L'écart entre les termes n'est pas constant (3, 6, 12, 24…), mais le rapport l'est : 6/3 = 12/6 = 24/12 = 2. On multiplie donc chaque terme par 2 : c'est une suite géométrique de raison q = 2.

QCM●○○

Terme général

Pour une suite arithmétique de premier terme u(0) = 4 et de raison r = 5, que vaut u(6) ?

Voir la solution

La réponse est 34. On applique la formule du terme général u(n) = u(0) + n × r, donc u(6) = 4 + 6 × 5 = 4 + 30 = 34. Attention à ne pas confondre : u(6) est le septième terme (on part de u(0)), pas le sixième.

Sortie●●○

Suite récurrente

La suite est définie par u(0) = 3 et u(n+1) = 2 × u(n) − 1. Écrivez la sortie exacte du programme.

u = 3
for n in range(5):
    print(u)
    u = 2 * u - 1
Voir la solution

On affiche u avant de le mettre à jour. Départ : 3. Puis 2×3−1 = 5, 2×5−1 = 9, 2×9−1 = 17, 2×17−1 = 33. La boucle fait 5 tours, donc 5 lignes.

3
5
9
17
33
Trace●●○

Fibonacci pas à pas

On calcule Fibonacci en itératif. Complétez la table de trace : à chaque tour, l'instruction a, b = b, a + b met à jour les deux variables simultanément (la partie droite est évaluée avant l'affectation).

a = 0
b = 1
for i in range(1, 6):
    a, b = b, a + b
Étapeab
départ01
tour 1
tour 2
tour 3
tour 4
tour 5
Voir la solution

À chaque tour, le nouveau a prend l'ancien b, et le nouveau b prend l'ancienne somme a + b. Comme la partie droite est calculée d'abord, les deux valeurs de départ du tour servent ensemble. Le déroulé donne (a, b) : (1, 1), puis (1, 2), (2, 3), (3, 5), (5, 8). On retrouve bien la suite 0, 1, 1, 2, 3, 5, 8.

depart : a = 0 b = 1
tour 1 -> a = 1 b = 1
tour 2 -> a = 1 b = 2
tour 3 -> a = 2 b = 3
tour 4 -> a = 3 b = 5
tour 5 -> a = 5 b = 8
Compléter●●○

Fibonacci d'un rang saisi

Complétez le cœur du calcul itératif pour obtenir le n-ième terme de Fibonacci (ici n = 10, attendu 55). Il faut mettre à jour a et b en une seule affectation.

n = 10
a = 0
b = 1
for i in range(n):
    a, b = , 
print("Fibonacci(" + str(n) + ") =", a)
Voir la solution

L'affectation multiple a, b = b, a + b fait avancer la suite : le nouveau a devient l'ancien b, le nouveau b devient la somme des deux. Après n tours, a contient le n-ième terme. Pour n = 10, la sortie est Fibonacci(10) = 55.

Rédaction●●●

Combien d'années pour doubler ?

Un capital est placé à un taux annuel fixe (croissance géométrique). Rédigez un programme qui lit le capital de départ et le taux (en %) au clavier, puis affiche le nombre d'années nécessaires pour que le capital ait au moins doublé. Utilisez une boucle while (le nombre de tours est inconnu).

Voir la solution

On ne connaît pas d'avance combien d'années seront nécessaires : c'est le cas typique du while. On répète « appliquer le taux, compter une année » tant que le capital courant reste inférieur à l'objectif (le double du capital de départ). Le compteur annee donne la réponse.

# Nom : annees_pour_doubler
# Role : nombre d'annees pour qu'un capital double avec un taux annuel fixe
# Entrees : capital (reel), taux (reel, en pourcentage)
# Sortie : affiche le nombre d'annees
# Variables locales : objectif (reel), courant (reel), annee (entier)
capital = float(input("Capital de depart : "))
taux = float(input("Taux annuel (%) : "))
objectif = 2 * capital
courant = capital
annee = 0
while courant < objectif:
    courant = courant * (1 + taux / 100)
    annee = annee + 1
print("Nombre d'annees :", annee)
Sortie (saisie : 1000 puis 5)
Capital de depart : Taux annuel (%) : Nombre d'annees : 15

À 5 % par an, un capital met 15 ans à doubler (1,05¹⁵ ≈ 2,08). Les deux prompts de input() s'affichent l'un après l'autre ; les valeurs tapées, elles, n'apparaissent pas dans la sortie.

Débogage●●●

Somme qui déborde d'un terme

Ce programme doit additionner les 4 premiers termes de la suite arithmétique u(0) = 2, r = 5 (soit 2 + 7 + 12 + 17 = 38). Mais il renvoie autre chose. Où est l'erreur ?

u = 2
r = 5
somme = 0
for n in range(4):
    u = u + r          # ligne suspecte
    somme = somme + u
print("somme :", somme)
Voir la solution

L'ordre est inversé : la version fautive calcule d'abord le terme suivant, puis l'ajoute. Elle additionne donc 7 + 12 + 17 + 22 = 58 et oublie le premier terme u(0) = 2. Il faut ajouter u à la somme avant de le faire avancer :

# Version CORRIGEE : somme des termes u(0)..u(3), suite arithmetique u0=2 r=5
u = 2
r = 5
somme = 0
for n in range(4):   # u(0) a u(3)
    somme = somme + u
    u = u + r
print("somme :", somme)
Sortie
somme : 38

11 Algorithmes classiques

Min, max, somme, moyenne « à la main »

Pour trouver le plus petit ou le plus grand d'une liste sans fonction toute faite, on initialise avec le premier élément, puis on compare chaque suivant. La somme, elle, s'initialise à 0 (accumulateur), et la moyenne = somme / nombre d'éléments.

# min / max / somme / moyenne "a la main"
notes = [12, 8, 15, 9, 17]
mini = notes[0]
maxi = notes[0]
somme = 0
for note in notes:
    if note < mini:
        mini = note
    if note > maxi:
        maxi = note
    somme = somme + note
moyenne = somme / len(notes)
print("min :", mini)
print("max :", maxi)
print("somme :", somme)
print("moyenne :", moyenne)
Sortie
min : 8
max : 17
somme : 61
moyenne : 12.2

Coût : un seul parcours de la liste, soit n − 1 comparaisons pour le min (idem pour le max).

Piège de l'initialisation. Ne jamais initialiser le min (ou le max) à 0 : sur une liste de températures négatives, mini = 0 resterait 0 alors qu'aucune valeur ne vaut 0. On initialise toujours avec le premier élément de la liste (notes[0]), une valeur qui existe vraiment.

PGCD par l'algorithme d'Euclide

Le plus grand commun diviseur de deux entiers se calcule par l'algorithme d'Euclide : on remplace le couple (a, b) par (b, a % b) tant que b n'est pas nul. Quand b atteint 0, a contient le PGCD.

# PGCD par l'algorithme d'Euclide (modulo)
a = 48
b = 36
while b != 0:
    a, b = b, a % b
print("PGCD(48, 36) =", a)
import math
print("verif :", math.gcd(48, 36))
Sortie
PGCD(48, 36) = 12
verif : 12
Deux versions. La version par modulo ci-dessus (a, b = b, a % b) est la plus rapide. Il existe une version historique par soustractions : tant que a ≠ b, on retire le plus petit au plus grand. Elle donne le même résultat mais fait beaucoup plus de tours. La récursivité (fonction qui s'appelle elle-même) est hors programme U22 : on reste en itératif.

Test de primalité

Un nombre est premier s'il n'est divisible que par 1 et par lui-même (et s'il est ≥ 2). Version naïve : on teste tous les diviseurs de 2 à n − 1. Version améliorée : il suffit de tester jusqu'à la racine carrée, c'est-à-dire tant que i × i ≤ n (au-delà, un diviseur aurait déjà été trouvé en dessous).

# Test de primalite ameliore : i*i <= n
def est_premier(n):
    if n < 2:
        return False
    i = 2
    while i * i <= n:
        if n % i == 0:
            return False
        i = i + 1
    return True

for n in [1, 2, 7, 9, 13, 15, 29]:
    print(n, est_premier(n))
Sortie
1 False
2 True
7 True
9 False
13 True
15 False
29 True

Coût : la version naïve fait jusqu'à n − 2 tests de divisibilité, la version améliorée seulement jusqu'à √n (bien moins pour les grands nombres).

Tri à bulles

Le tri à bulles compare des éléments voisins et les échange s'ils sont dans le mauvais ordre. À chaque passe, le plus grand élément « remonte » à sa place finale à droite. On observe l'état de la liste après chaque passe :

# Tri a bulles : etat de la liste apres CHAQUE passe
t = [5, 3, 8, 1, 2]
n = len(t)
for passe in range(n - 1):
    for i in range(n - 1 - passe):
        if t[i] > t[i + 1]:
            t[i], t[i + 1] = t[i + 1], t[i]
    print("apres passe", passe + 1, ":", t)
Sortie
apres passe 1 : [3, 5, 1, 2, 8]
apres passe 2 : [3, 1, 2, 5, 8]
apres passe 3 : [1, 2, 3, 5, 8]
apres passe 4 : [1, 2, 3, 5, 8]

Coût : de l'ordre de comparaisons (deux boucles imbriquées).

Tri par sélection

Le tri par sélection cherche le minimum du reste de la liste et l'échange avec le premier élément non trié. À chaque passe, une case de plus est définitivement placée à gauche :

# Tri par selection : etat de la liste apres CHAQUE passe
t = [5, 3, 8, 1, 2]
n = len(t)
for i in range(n - 1):
    imin = i
    for j in range(i + 1, n):
        if t[j] < t[imin]:
            imin = j
    t[i], t[imin] = t[imin], t[i]
    print("apres passe", i + 1, ":", t)
Sortie
apres passe 1 : [1, 3, 8, 5, 2]
apres passe 2 : [1, 2, 8, 5, 3]
apres passe 3 : [1, 2, 3, 5, 8]
apres passe 4 : [1, 2, 3, 5, 8]

Coût : également de l'ordre de comparaisons, mais avec beaucoup moins d'échanges que le tri à bulles (un seul par passe).

Recherche séquentielle vs recherche dichotomique

La recherche séquentielle parcourt la liste du début à la fin jusqu'à trouver la cible : simple, mais lente sur de grandes listes (jusqu'à n comparaisons).

# Recherche sequentielle
t = [7, 3, 9, 1, 4, 8]
cible = 4
trouve = -1
for i in range(len(t)):
    if t[i] == cible:
        trouve = i
        break
print("indice :", trouve)
Sortie
indice : 4

La recherche dichotomique exige un tableau trié. On regarde l'élément du milieu : si c'est la cible, c'est fini ; si la cible est plus grande, on cherche à droite (on avance debut) ; sinon à gauche (on recule fin). À chaque étape, on divise l'intervalle par deux.

# Recherche dichotomique (tableau TRIE) avec trace des bornes
t = [2, 5, 8, 12, 16, 23, 38, 45, 56, 72, 91]
cible = 45
debut = 0
fin = len(t) - 1
trouve = -1
while debut <= fin:
    milieu = (debut + fin) // 2
    print("debut =", debut, "fin =", fin, "milieu =", milieu, "-> t[milieu] =", t[milieu])
    if t[milieu] == cible:
        trouve = milieu
        break
    elif t[milieu] < cible:
        debut = milieu + 1
    else:
        fin = milieu - 1
print("indice trouve :", trouve)
Sortie
debut = 0 fin = 10 milieu = 5 -> t[milieu] = 23
debut = 6 fin = 10 milieu = 8 -> t[milieu] = 56
debut = 6 fin = 7 milieu = 6 -> t[milieu] = 38
debut = 7 fin = 7 milieu = 7 -> t[milieu] = 45
indice trouve : 7
La force de la dichotomie. Sur 11 éléments, elle a trouvé la cible en 4 comparaisons au lieu de 8 pour la recherche séquentielle. Sur une liste de 1000 éléments, elle en fait au plus ≈ 10 (log₂ 1000), contre 1000 pour la séquentielle. Condition obligatoire : le tableau doit être trié.

QCM●○○

Initialiser le minimum

On cherche la plus petite valeur d'une liste t non vide. Par quoi initialiser correctement la variable mini ?

Voir la solution

La bonne initialisation est mini = t[0] : une valeur qui appartient vraiment à la liste. Avec mini = 0, une liste de nombres négatifs (températures, soldes…) renverrait 0 à tort. Avec une grande constante comme 1000000, on risque de la dépasser un jour. Partir du premier élément est sûr dans tous les cas.

QCM●●○

Comparaisons de la dichotomie

Sur la liste triée [2, 5, 8, 12, 16, 23, 38, 45, 56, 72, 91] (11 éléments), combien de comparaisons d'éléments (accès à t[milieu]) la recherche dichotomique fait-elle pour trouver la cible 45 ?

Voir la solution

La réponse est 4. En reprenant la trace du cours, la boucle examine successivement t[5]=23, t[8]=56, t[6]=38, puis t[7]=45 : cible trouvée au 4ᵉ examen. La recherche séquentielle, elle, aurait comparé les 8 premiers éléments avant d'atteindre 45. La dichotomie divise l'intervalle par deux à chaque tour, d'où sa rapidité.

Trace●●○

Euclide pas à pas

On calcule PGCD(84, 30) par Euclide. À chaque tour, a prend l'ancien b et b prend l'ancien reste a % b. Complétez la table jusqu'à ce que b vaille 0.

a = 84
b = 30
while b != 0:
    a, b = b, a % b
Étapeab
départ8430
tour 1
tour 2
tour 3
Voir la solution

Tour 1 : 84 % 30 = 24, donc (a, b) devient (30, 24). Tour 2 : 30 % 24 = 6 → (24, 6). Tour 3 : 24 % 6 = 0 → (6, 0). b vaut 0 : la boucle s'arrête et a = 6 est le PGCD.

depart : a = 84 b = 30
tour 1 -> a = 30 b = 24
tour 2 -> a = 24 b = 6
tour 3 -> a = 6 b = 0
PGCD = 6
Trace●●●

Dichotomie : les bornes

On recherche la cible 27 par dichotomie dans [3, 7, 11, 15, 19, 23, 27, 31, 35] (indices 0 à 8). Complétez, pour chaque tour, les valeurs de debut, fin et milieu (avec milieu = (debut + fin) // 2).

t = [3, 7, 11, 15, 19, 23, 27, 31, 35]
cible = 27
debut = 0
fin = len(t) - 1
while debut <= fin:
    milieu = (debut + fin) // 2
    if t[milieu] == cible:
        break
    elif t[milieu] < cible:
        debut = milieu + 1
    else:
        fin = milieu - 1
Tourdebutfinmilieu
tour 1
tour 2
Voir la solution

Tour 1 : debut = 0, fin = 8, milieu = (0+8)//2 = 4t[4] = 19 < 27, donc la cible est à droite : debut = 5. Tour 2 : debut = 5, fin = 8, milieu = (5+8)//2 = 6t[6] = 27 : trouvé, on s'arrête. Deux tours suffisent.

tour 1 : debut = 0 fin = 8 milieu = 4 t[milieu] = 19
tour 2 : debut = 5 fin = 8 milieu = 6 t[milieu] = 27
trouve a l'indice 6
Sortie●●○

Tours du test de primalité

Ce programme teste si 37 est premier avec le critère i * i <= n. Quelle est sa sortie exacte ? (37 est premier : aucune ligne « divisible » n'est affichée.)

n = 37
i = 2
tours = 0
while i * i <= n:
    tours = tours + 1
    if n % i == 0:
        print(n, "divisible par", i)
    i = i + 1
print("nombre de tours :", tours)
Voir la solution

La boucle tourne tant que i * i <= 37, soit pour i = 2, 3, 4, 5, 6 (car 6×6 = 36 ≤ 37, mais 7×7 = 49 > 37) : 5 tours. Aucun de ces i ne divise 37, donc aucune ligne « divisible » n'apparaît. 37 est bien premier.

nombre de tours : 5
Compléter●●○

Le cœur du tri à bulles

Complétez le tri à bulles : il faut comparer deux voisins et les échanger s'ils sont dans le mauvais ordre. La liste [4, 2, 7, 1] doit être triée en [1, 2, 4, 7].

t = [4, 2, 7, 1]
n = len(t)
for passe in range(n - 1):
    for i in range(n - 1 - passe):
        if t[i]  t[i + 1]:
            t[i], t[i + 1] = 
print(t)
Voir la solution

On échange quand l'élément de gauche est plus grand que son voisin de droite (t[i] > t[i + 1]). L'échange en Python s'écrit d'une traite t[i], t[i + 1] = t[i + 1], t[i] : les deux valeurs sont permutées simultanément, sans variable temporaire. Le programme affiche [1, 2, 4, 7].

Compléter●●●

Mise à jour des bornes

Complétez la mise à jour des bornes de la recherche dichotomique. Si le milieu est trop petit, on cherche à droite ; sinon à gauche. Cible 13 dans [1, 4, 6, 9, 13, 20, 25] (attendu : indice 4).

t = [1, 4, 6, 9, 13, 20, 25]
cible = 13
debut = 0
fin = len(t) - 1
trouve = -1
while debut <= fin:
    milieu = (debut + fin) // 2
    if t[milieu] == cible:
        trouve = milieu
        break
    elif t[milieu] < cible:
        debut = 
    else:
        fin = 
print("indice :", trouve)
Voir la solution

Quand t[milieu] < cible, la cible est à droite : on écarte la moitié gauche en posant debut = milieu + 1. Sinon (t[milieu] > cible), elle est à gauche : fin = milieu - 1. Le +1 et le -1 sont essentiels : ils excluent le milieu déjà testé et garantissent que l'intervalle rétrécit à chaque tour (sinon la boucle tournerait indéfiniment). Ici le programme affiche indice : 4.

Rédaction●●●

Plus haute température d'un relevé

Un capteur a enregistré un relevé de températures (liste non vide, valeurs pouvant être négatives). Rédigez une fonction qui retourne la plus haute température, sans utiliser max(). Attention à l'initialisation.

Voir la solution

On initialise maxi avec le premier élément du relevé (jamais 0 : ici toutes les valeurs sont négatives, 0 fausserait le résultat). On parcourt ensuite la liste et on remplace maxi dès qu'on trouve plus grand.

# Nom : temperature_max
# Role : trouve la plus haute temperature d'un releve
# Entrees : releve (liste de reels non vide)
# Sortie : retourne la temperature maximale
# Variables locales : maxi (reel), t (reel)
def temperature_max(releve):
    maxi = releve[0]        # initialisation au 1er element, jamais a 0
    for t in releve:
        if t > maxi:
            maxi = t
    return maxi

releve = [-3.0, -8.0, -1.0, -5.0]
print("Temperature max :", temperature_max(releve))
Sortie
Temperature max : -1.0

La plus haute température est −1.0 °C. Si on avait initialisé maxi = 0, la fonction aurait renvoyé 0, une température qui n'a jamais été relevée.

Rédaction●●●

PGCD par Euclide

Rédigez une fonction pgcd(a, b) qui calcule le plus grand commun diviseur de deux entiers strictement positifs par l'algorithme d'Euclide (version modulo, en itératif — pas de récursivité).

Voir la solution

On répète a, b = b, a % b tant que b n'est pas nul. À la sortie de la boucle, a contient le PGCD. L'affectation multiple évite d'avoir à sauvegarder l'ancien b dans une variable temporaire.

# Nom : pgcd
# Role : calcule le PGCD de deux entiers par l'algorithme d'Euclide
# Entrees : a (entier > 0), b (entier > 0)
# Sortie : retourne le PGCD
# Variables locales : (a et b sont modifies au fil des tours)
def pgcd(a, b):
    while b != 0:
        a, b = b, a % b
    return a

print("PGCD(60, 48) =", pgcd(60, 48))
print("PGCD(17, 5) =", pgcd(17, 5))
Sortie
PGCD(60, 48) = 12
PGCD(17, 5) = 1

PGCD(60, 48) = 12 ; PGCD(17, 5) = 1 (17 et 5 sont premiers entre eux). La récursivité donnerait le même résultat mais n'est pas attendue à l'épreuve.

Débogage●●●

Dichotomie qui boucle sans fin

Cette recherche dichotomique tourne indéfiniment quand la cible n'est pas au milieu. Quelle correction règle le problème ?

t = [1, 4, 6, 9, 13, 20, 25]
cible = 20
debut = 0
fin = len(t) - 1
while debut <= fin:
    milieu = (debut + fin) // 2
    if t[milieu] == cible:
        break
    elif t[milieu] < cible:
        debut = milieu       # ligne suspecte
    else:
        fin = milieu         # ligne suspecte
Voir la solution

Sans le +1 ni le -1, quand l'intervalle se réduit à deux cases, milieu retombe toujours sur le même indice : debut (ou fin) ne bouge plus, la condition debut <= fin reste vraie et la boucle ne s'arrête jamais. Il faut exclure le milieu déjà testé : debut = milieu + 1 et fin = milieu - 1. La liste est déjà triée (réponse c inutile), et / 2 donnerait un flottant invalide comme indice (réponse d fausse).

# Version CORRIGEE : bornes qui excluent le milieu deja teste
t = [1, 4, 6, 9, 13, 20, 25]
cible = 20
debut = 0
fin = len(t) - 1
trouve = -1
while debut <= fin:
    milieu = (debut + fin) // 2
    if t[milieu] == cible:
        trouve = milieu
        break
    elif t[milieu] < cible:
        debut = milieu + 1
    else:
        fin = milieu - 1
print("indice :", trouve)
Sortie
indice : 5