Exercices#
Les bases de la programmation Python
Mode d’emploi
-
Niveau : ★ échauffement, ★★ classique (niveau bac), ★★★ défi ; les coups de pouce et les corrigés se déplient sous chaque exercice.
-
▶ À programmer et tester en Python (console ou éditeur en ligne). Le symbole
>>>figure une saisie dans la console. -
Réflexe pour chaque fonction : (1) que reçoit-elle (paramètres, préconditions) et que renvoie-t-elle ? (2) l’ai-je testée sur des cas simples et des cas limites (0, égalité, mot vide…) ?
-
Usage de l’IA, indiqué sur chaque exercice : aucune ; en appui (déboguer, reformuler, vérifier ; la réponse finale est écrite et comprise par vous) ; l’exercice s’appuie sur une réponse d’IA. Voir la charte d’usage de l’IA.
Remarque
La tortue (turtle) fait l’objet d’un complément séparé, après ce chapitre : elle n’est pas reprise ici.
Variables, affectation, types#
★ Exercice 1 — Lecture de trace #
• essentiel
Sans machine, donner ce qu’affiche ce programme. Détailler l’évolution de chaque variable.
x = 5
y = 3
x = x + y
y = x - y
x = x - y
print(x, y)
Que réalise, au final, ce programme sur x et y ?0
Coup de pouce
Dresser un tableau avec une colonne par variable et une ligne par instruction. Pour chaque affectation, calculer d’abord la partie droite avec les valeurs du moment, puis ranger le résultat dans la variable de gauche.
Corrigé
Remarque
Plusieurs écritures sont souvent possibles : on donne une version simple.
On suit chaque affectation (on évalue la droite, on range à gauche) :
| instruction | x |
y |
|---|---|---|
x = 5 |
5 | — |
y = 3 |
5 | 3 |
x = x + y |
8 | 3 |
y = x - y |
8 | 5 |
x = x - y |
3 | 5 |
L’affichage est donc 3 5. Ce programme échange les contenus de x et y sans variable temporaire, en n’utilisant que des additions et des soustractions.
★ Exercice 2 — Échanger deux variables #
▶ Deux variables a et b contiennent respectivement 1 et 2. Écrire les instructions qui échangent leurs contenus (en utilisant une variable temporaire), de sorte qu’à la fin a vaut 2 et b vaut 1. Pourquoi une variable temporaire est-elle nécessaire ?0
Coup de pouce
Essayer d’abord a = b puis b = a : que valent a et b à la fin ? Quelle valeur aurait-il fallu mettre de côté avant la première affectation ?
Corrigé
Avec une variable temporaire :
a = 1
b = 2
temp = a # on met a l'abri la valeur de a
a = b # a recoit b
b = temp # b recoit l'ancienne valeur de a
print(a, b) # 2 1
La variable temporaire est nécessaire : sans elle, a = b écraserait la valeur de a, qui serait perdue avant d’avoir pu être rangée dans b.
▷ Erreur fréquente. écrire seulement a = b puis b = a : après la première ligne, a vaut déjà 2, et les deux variables finissent à 2.
★ Exercice 3 — Convertisseur #
▶
-
Écrire une fonction
celsius_en_fahrenheit(c)qui convertit une température en degrés Celsius vers des degrés Fahrenheit (formule : \(F = \frac{9}{5}\,C + 32\)). -
Écrire une fonction
prix_ttc(ht)qui renvoie le prix TTC d’un article à partir de son prix hors taxe, avec une TVA de 20 %.0Coup de pouce
Suivre le gabarit de fonction du cours : écrire le calcul avec le paramètre (
couht), puis le renvoyer avecreturn. Ajouter 20 % d’un prix revient à le multiplier par quel nombre ?
>>> celsius_en_fahrenheit(100)
212.0
>>> prix_ttc(50)
60.0
Corrigé
On suit le gabarit du cours : le calcul s’écrit avec le paramètre et on le renvoie. Ajouter 20 % revient à multiplier par \(1 + \frac{20}{100} = 1{,}20\).
def celsius_en_fahrenheit(c):
return 9 / 5 * c + 32
def prix_ttc(ht):
return ht * 1.20
On teste dans la console :
>>> celsius_en_fahrenheit(100)
212.0
>>> celsius_en_fahrenheit(0)
32.0
>>> prix_ttc(50)
60.0
★ Exercice 4 — Rectangle : périmètre et aire #
▶ Écrire deux fonctions perimetre(largeur, hauteur) et aire(largeur, hauteur) qui renvoient respectivement le périmètre et l’aire d’un rectangle.0
Coup de pouce
Faire d’abord le calcul à la main pour le rectangle de 3 sur 4 de l’exemple, en notant les opérations effectuées ; les écrire ensuite avec les paramètres largeur et hauteur.
>>> perimetre(3, 4)
14
>>> aire(3, 4)
12
Corrigé
À la main, pour 3 sur 4 : périmètre \(3 + 4 + 3 + 4 = 2 \times (3 + 4) = 14\) et aire \(3 \times 4 = 12\) ; on écrit les mêmes opérations avec les paramètres.
def perimetre(largeur, hauteur):
return 2 * (largeur + hauteur)
def aire(largeur, hauteur):
return largeur * hauteur
On teste dans la console :
>>> perimetre(3, 4)
14
>>> aire(3, 4)
12
★★ Exercice 5 — Le bon usage de // et % #
• essentiel
▶ Écrire trois fonctions unites(n), dizaines(n) et centaines(n) qui prennent un entier de trois chiffres au plus et renvoient respectivement son chiffre des unités, des dizaines et des centaines. On n’utilisera que les opérateurs // (quotient) et % (reste).0
Coup de pouce
Que donnent 347 % 10 et 347 // 10 ? Comment isoler ensuite le chiffre des dizaines à partir de 34 ?
>>> unites(347)
7
>>> dizaines(347)
4
>>> centaines(347)
3
>>> dizaines(60)
6
Corrigé
n % 10 isole le dernier chiffre ; n // 10 « décale » le nombre d’un cran vers la droite.
def unites(n):
return n % 10
def dizaines(n):
return (n // 10) % 10
def centaines(n):
return (n // 100) % 10
On teste dans la console :
>>> unites(347)
7
>>> dizaines(347)
4
>>> centaines(347)
3
>>> unites(60)
0
>>> dizaines(60)
6
>>> centaines(60)
0
>>> unites(7)
7
>>> dizaines(7)
0
>>> centaines(7)
0
Les conditionnelles#
★ Exercice 6 — Pair ou impair #
▶ Écrire une fonction est_pair(n) qui renvoie True si l’entier n est pair, False sinon. Vérifier le résultat sur quelques valeurs, sans oublier le cas n = 0.0
Coup de pouce
Quel est le reste de la division par 2 d’un nombre pair ? d’un nombre impair ? Le paragraphe « Un test très utile : la divisibilité » du cours traduit cela en Python.
Corrigé
Un entier est pair quand le reste de sa division par 2 est nul (test de divisibilité du cours) : la condition est n % 2 == 0.
def est_pair(n):
return n % 2 == 0
On teste dans la console :
>>> est_pair(4)
True
>>> est_pair(7)
False
>>> est_pair(0)
True
Remarquer qu’on renvoie directement le booléen n % 2 == 0 : inutile d’écrire if ...: return True else: return False.
▷ Erreur fréquente. écrire n % 2 = 0 : le signe = est l’affectation, la comparaison s’écrit ==. Python répond par une SyntaxError.
★ Exercice 7 — Le plus grand des deux #
▶ Écrire une fonction maximum2(a, b) qui renvoie le plus grand des deux nombres a et b, sans utiliser la fonction max. Ne pas oublier le cas d’égalité.0
Coup de pouce
Comparer a et b avec un if : combien de cas faut-il distinguer ? En cas d’égalité, peu importe lequel des deux on renvoie.
Corrigé
Deux cas suffisent : si a > b, on renvoie a ; sinon (b plus grand ou égal), on renvoie b.
def maximum2(a, b):
if a > b:
return a
return b # couvre aussi le cas a == b
On teste dans la console :
>>> maximum2(3, 5)
5
>>> maximum2(5, 3)
5
>>> maximum2(4, 4)
4
▷ Erreur fréquente. écrire if a > b: return a puis if b > a: return b : pour a == b, aucune condition n’est vraie et la fonction renvoie None. Le test d’égalité maximum2(4, 4) démasque l’oubli.
★ Exercice 8 — Le signe d’un nombre #
• essentiel
▶ Écrire signe(x) qui renvoie 1 si x est strictement positif, -1 s’il est strictement négatif, et 0 s’il est nul. (On utilisera if, elif, else.)0
Coup de pouce
Écrire d’abord en français les trois cas et leur condition, puis les traduire un par un. Tester ensuite avec 12, -3 et 0.
Corrigé
Trois cas qui s’excluent : if pour le premier, elif pour le deuxième, else pour « tout le reste » (ici x nul).
def signe(x):
if x > 0:
return 1
elif x < 0:
return -1
else:
return 0
On teste dans la console :
>>> signe(12)
1
>>> signe(-3)
-1
>>> signe(0)
0
★★ Exercice 9 — Mention au bac #
• essentiel
▶ Écrire une fonction mention(note) qui renvoie la mention correspondant à une note sur 20 : « Très bien » (\(\geqslant 16\)), « Bien » (\(\geqslant 14\)), « Assez bien » (\(\geqslant 12\)), « Admis » (\(\geqslant 10\), sans mention), « Refusé » sinon.
-
Pourquoi l’ordre des tests est-il important ? Que se passerait-il si on testait
note >= 10en premier ? -
Que renvoie votre fonction pour
note = 25(valeur impossible) ? On verra plus loin comment interdire proprement une telle valeur avec une précondition.0Coup de pouce
Reprendre l’exemple du cours sur
elif: Python exécute le premier bloc dont la condition est vraie. Tester à la main la note18avec votre ordre de tests.
Corrigé
On teste les seuils du plus haut au plus bas avec if/elif, et le else final recueille les notes inférieures à 10.
def mention(note):
if note >= 16:
return "Tres bien"
elif note >= 14:
return "Bien"
elif note >= 12:
return "Assez bien"
elif note >= 10:
return "Admis"
else:
return "Refuse"
1. L’ordre va du plus exigeant au moins exigeant, car Python exécute le premier elif vrai. Si on testait note >= 10 en premier, une note de 18 renverrait « Admis » : toutes les bonnes notes seraient mal classées.
2. mention(25) renvoie « Tres bien » (car \(25 \geqslant 16\)) : la fonction accepte sans broncher une note impossible. C’est justement ce qu’une précondition permettra d’interdire (section Les fonctions du cours).
★★ Exercice 10 — Année bissextile #
▶ Écrire une fonction est_bissextile(annee) qui renvoie un booléen, sachant qu’une année est bissextile si elle est divisible par 400, ou divisible par 4 sans l’être par 100.0
Coup de pouce
« Divisible par 4 » se traduit par annee % 4 == 0. Traduire ensuite la phrase mot à mot avec or et and, en plaçant des parenthèses.
>>> est_bissextile(2000)
True
>>> est_bissextile(1900)
False
>>> est_bissextile(2024)
True
Corrigé
On traduit la phrase mot à mot : « divisible par 400 » donne annee % 400 == 0, « ou divisible par 4 et pas par 100 » donne or (... and ...). Un seul booléen suffit :
def est_bissextile(annee):
return annee % 400 == 0 or (annee % 4 == 0 and annee % 100 != 0)
On teste dans la console :
>>> est_bissextile(2000)
True
>>> est_bissextile(1900)
False
>>> est_bissextile(2024)
True
>>> est_bissextile(2023)
False
★★ Exercice 11 — Le plus grand des trois #
▶ Écrire maximum3(a, b, c) qui renvoie le plus grand de trois nombres, sans utiliser max.0
Coup de pouce
Retenir un « champion provisoire » m, égal à a au départ, puis le comparer successivement à b et à c.
Corrigé
On garde un « champion provisoire » que l’on met à jour :
def maximum3(a, b, c):
m = a
if b > m:
m = b
if c > m:
m = c
return m
On teste dans la console :
>>> maximum3(3, 9, 5)
9
>>> maximum3(3, 5, 9)
9
>>> maximum3(4, 4, 4)
4
★★ Exercice 12 — Quel type de triangle ? #
▶ On donne les longueurs a, b, c des trois côtés d’un triangle (supposées valides, donc strictement positives). Écrire type_triangle(a, b, c) qui renvoie "equilateral" (trois côtés égaux), "isocele" (exactement deux côtés égaux) ou "scalene" (aucun côté égal).0
Coup de pouce
Dans quel ordre tester les cas ? Si l’on teste d’abord « trois côtés égaux », que reste-t-il à distinguer ensuite ?
>>> type_triangle(3, 3, 3)
'equilateral'
>>> type_triangle(3, 4, 5)
'scalene'
Corrigé
On teste l’égalité des trois côtés, puis d’exactement deux (grâce à l’ordre : si les trois étaient égaux, on serait déjà sorti).
def type_triangle(a, b, c):
if a == b and b == c:
return "equilateral"
elif a == b or b == c or a == c:
return "isocele"
else:
return "scalene"
On teste dans la console :
>>> type_triangle(3, 3, 3)
'equilateral'
>>> type_triangle(3, 3, 5)
'isocele'
>>> type_triangle(3, 4, 5)
'scalene'
★★ Exercice 13 — Réduction par paliers #
▶ Un magasin applique une réduction selon le montant : 0 % en dessous de 100 €, 5 % de 100 à moins de 300 €, 10 % à partir de 300 €. Écrire prix_reduit(montant) qui renvoie le montant après réduction.0
Coup de pouce
Choisir d’abord le taux (0, 5 ou 10) avec if/elif/else, puis faire le calcul une seule fois à la fin. Attention aux bornes : 300 relève de quel palier ?
>>> prix_reduit(50)
50.0
>>> prix_reduit(200)
190.0
>>> prix_reduit(300)
270.0
Corrigé
On choisit d’abord le taux avec if/elif/else (bornes : < 100, puis < 300, donc 300 est au palier de 10 %), puis on calcule une seule fois le prix réduit.
def prix_reduit(montant):
if montant < 100:
taux = 0
elif montant < 300:
taux = 5
else:
taux = 10
return montant * (1 - taux / 100)
On teste dans la console :
>>> prix_reduit(50)
50.0
>>> prix_reduit(200)
190.0
>>> prix_reduit(300)
270.0
Les boucles bornées (for)#
★ Exercice 14 — Remettre l’accumulateur dans l’ordre #
Les lignes de la fonction somme_entre(a, b), qui renvoie \(a + (a+1) + \dots + b\), ont été mélangées et ont perdu leur indentation.
total = total + i
return total
def somme_entre(a, b):
total = 0
for i in range(a, b + 1):
-
Recopier les lignes dans le bon ordre, avec la bonne indentation.0
Coup de pouce
Une fonction commence par sa ligne
def. Se demander ensuite ce qui doit être fait une seule fois avant la boucle, à chaque tour, et une seule fois après. -
▶ Vérifier que
somme_entre(3, 6)renvoie18. -
Que renverrait la fonction si la ligne
total = 0était placée à l’intérieur de la boucle ?
Corrigé
1. On initialise l’accumulateur avant la boucle, on le met à jour dans la boucle (un cran d’indentation de plus), on le renvoie après la boucle :
def somme_entre(a, b):
total = 0
for i in range(a, b + 1):
total = total + i
return total
On teste dans la console :
>>> somme_entre(3, 6) # 3 + 4 + 5 + 6
18
2. somme_entre(3, 6) renvoie bien 18.
3. L’accumulateur serait remis à 0 à chaque tour : à la fin, total ne contiendrait que la dernière valeur ajoutée, b. Ici, la fonction renverrait 6 au lieu de 18.
★ Exercice 15 — Sommes #
• essentiel
▶
-
Écrire
somme(n)qui renvoie \(1 + 2 + \dots + n\) à l’aide d’un accumulateur. Vérifier quesomme(100)vaut5050. -
Écrire
somme_carres(n)qui renvoie \(1^2 + 2^2 + \dots + n^2\).0Coup de pouce
Reprendre l’exemple du cours « somme des entiers de 1 à 100 » en remplaçant 100 par
n: attention à la borne exclue durange. Pour la question 2, seule la quantité ajoutée à chaque tour change.
Corrigé
Motif de l’accumulateur : total vaut 0 avant la boucle, on lui ajoute i (puis i * i) à chaque tour, et on le renvoie après la boucle. Pour aller jusqu’à n inclus, on écrit range(1, n + 1).
def somme(n):
total = 0
for i in range(1, n + 1):
total = total + i
return total
def somme_carres(n):
total = 0
for i in range(1, n + 1):
total = total + i * i
return total
On teste dans la console :
>>> somme(100)
5050
>>> somme_carres(3) # 1 + 4 + 9
14
▷ Erreur fréquente. écrire range(1, n) : la borne n est exclue, et somme(100) renverrait 4950 au lieu de 5050.
★ Exercice 16 — Table de multiplication #
▶ Écrire table(n) qui affiche, une par ligne, les dix lignes de la table de multiplication de n, sous la forme n x i = .... Rappel : print(a, "x", b) affiche les valeurs séparées par des espaces.0
Coup de pouce
Quelles valeurs la variable de boucle i doit-elle prendre ? Écrire le range correspondant (borne de droite exclue), puis un seul print dans la boucle.
>>> table(7)
7 x 1 = 7
7 x 2 = 14
7 x 3 = 21
...
7 x 10 = 70
Corrigé
La variable i doit prendre les valeurs 1 à 10, d’où range(1, 11) (borne exclue). La fonction affiche et ne renvoie rien : on l’appelle et on contrôle l’affichage à l’écran.
def table(n):
for i in range(1, 11):
print(n, "x", i, "=", n * i)
table(7) # affiche 7 x 1 = 7, puis 7 x 2 = 14, ... jusqu'a 7 x 10 = 70
★★ Exercice 17 — Construire une chaîne #
▶ Écrire ligne_etoiles(n) qui renvoie une chaîne formée de n étoiles, en partant d’une chaîne vide "" que l’on complète à chaque tour (chaine = chaine + "*"). C’est le même motif d’accumulateur que ci-dessus, appliqué à du texte.0
Coup de pouce
Combien de tours de boucle faut-il ? Quelle valeur la chaîne doit-elle avoir avant le premier tour ?
>>> ligne_etoiles(5)
'*****'
Corrigé
On part d’une chaîne vide et on ajoute une étoile à chaque tour (accumulateur de texte, exactement comme l’accumulateur numérique ci-dessus).
def ligne_etoiles(n):
chaine = ""
for i in range(n):
chaine = chaine + "*"
return chaine
On teste dans la console :
>>> ligne_etoiles(5)
'*****'
>>> ligne_etoiles(0)
''
★★ Exercice 18 — Factorielle #
▶ Une course réunit n coureurs. Combien d’ordres d’arrivée sont possibles ? Le vainqueur peut être l’un des n coureurs, le deuxième l’un des n - 1 restants, et ainsi de suite : il y a \(n \times (n-1) \times \dots \times 2 \times 1\) ordres possibles. Ce produit \(1 \times 2 \times \dots \times n\) s’appelle la factorielle de n, avec la convention \(0! = 1\) (sans coureur, un seul classement : le classement vide). Écrire factorielle(n) avec un accumulateur. Tester factorielle(0) et factorielle(5).0
Coup de pouce
C’est le motif de la somme du cours, avec une multiplication. Mais si l’accumulateur part de 0, que vaut le produit à la fin ? Par quelle valeur faut-il l’initialiser ?
Corrigé
L’accumulateur d’un produit s’initialise à 1. La boucle range(2, n+1) ne fait aucun tour si n vaut 0 ou 1, ce qui donne bien 1.
def factorielle(n):
produit = 1
for i in range(2, n + 1):
produit = produit * i
return produit
On teste dans la console :
>>> factorielle(0)
1
>>> factorielle(1)
1
>>> factorielle(5)
120
Avec 5 coureurs, il y a donc 120 ordres d’arrivée possibles ; avec 10 coureurs, déjà factorielle(10) \(=\) 3628800.
▷ Erreur fréquente. initialiser produit = 0, par habitude de la somme : tous les produits valent alors 0.
★★ Exercice 19 — Compter les voyelles #
• essentiel
▶ Écrire compter_voyelles(mot) qui renvoie le nombre de voyelles d’une chaîne (on comptera aussi le y). Rappel : on parcourt une chaîne caractère par caractère avec for lettre in mot: et on teste if lettre in "aeiouy":.0
Coup de pouce
C’est un compteur : une variable qui part de 0 et augmente de 1 chaque fois que la condition est vraie.
>>> compter_voyelles("bonjour")
3
Corrigé
Motif du compteur : n part de 0, on parcourt le mot lettre par lettre et on ajoute 1 seulement quand la lettre est une voyelle.
def compter_voyelles(mot):
n = 0
for lettre in mot:
if lettre in "aeiouyAEIOUY":
n = n + 1
return n
On teste dans la console :
>>> compter_voyelles("bonjour")
3
>>> compter_voyelles("")
0
▷ Erreur fréquente. indenter return n au niveau du if (dans la boucle) : la fonction s’arrête dès la première lettre et compter_voyelles("bonjour") renvoie 0.
★★ Exercice 20 — Diviseurs et nombres parfaits #
▶
-
Écrire
somme_diviseurs(n)qui renvoie la somme de tous les diviseurs den(y compris1etn). -
Un entier est parfait si la somme de ses diviseurs vaut
2*n. Écrireest_parfait(n). -
Afficher tous les nombres parfaits strictement inférieurs à
100.0Coup de pouce
Un diviseur
ddenvérifien % d == 0. Quelles valeurs dedfaut-il essayer ? Pour la question 3, réutiliserest_parfaitdans une boucle.
>>> somme_diviseurs(6)
12
>>> est_parfait(28)
True
Corrigé
Accumulateur sous condition : on essaie tous les d de 1 à n et on n’ajoute que ceux qui vérifient n % d == 0. est_parfait réutilise ensuite somme_diviseurs.
def somme_diviseurs(n):
total = 0
for d in range(1, n + 1):
if n % d == 0:
total = total + d
return total
def est_parfait(n):
return somme_diviseurs(n) == 2 * n
for k in range(1, 100):
if est_parfait(k):
print(k) # affiche 6 puis 28
On teste dans la console :
>>> somme_diviseurs(6)
12
>>> est_parfait(28)
True
>>> est_parfait(12)
False
Les nombres parfaits inférieurs à 100 sont 6 et 28.
★★ Exercice 21 — Compter les occurrences d’une lettre #
▶ Écrire occurrences(caractere, mot) qui renvoie le nombre de fois que caractere apparaît dans mot.0
Coup de pouce
Même schéma que « Compter les voyelles » : seule la condition testée sur chaque lettre change.
>>> occurrences("a", "banana")
3
Corrigé
Même compteur que « Compter les voyelles » ; seule la condition change : lettre == caractere.
def occurrences(caractere, mot):
n = 0
for lettre in mot:
if lettre == caractere:
n = n + 1
return n
On teste dans la console :
>>> occurrences("a", "banana")
3
>>> occurrences("z", "banana")
0
★★ Exercice 22 — Puissance sans ** #
▶ Écrire puissance(a, n) qui renvoie \(a^n\) à l’aide d’une boucle (accumulateur de produit), sans utiliser l’opérateur **. On rappelle que \(a^0 = 1\).0
Coup de pouce
\(a^n = a \times a \times \dots \times a\) (\(n\) facteurs) : combien de tours de boucle, et quelle valeur initiale pour l’accumulateur ?
>>> puissance(2, 10)
1024
>>> puissance(5, 0)
1
Corrigé
L’accumulateur d’un produit s’initialise à 1. La boucle fait n tours ; si n vaut 0, elle n’en fait aucun et renvoie 1.
def puissance(a, n):
resultat = 1
for i in range(n):
resultat = resultat * a
return resultat
On teste dans la console :
>>> puissance(2, 10)
1024
>>> puissance(5, 0)
1
>>> puissance(3, 3)
27
★★★ Exercice 23 — FizzBuzz #
≈ 15 min
▶ Écrire fizzbuzz(n) qui affiche, un par ligne, les entiers de 1 à n, en affichant à la place Fizz pour chaque multiple de 3, Buzz pour chaque multiple de 5, et FizzBuzz pour chaque multiple de 3 et de 5.0
Coup de pouce
Dans quel ordre tester les cas ? Pour 15, lequel des tests i % 3 == 0 et i % 5 == 0 serait capté en premier ?
0
Coup de pouce 2 (début de solution)
for i in range(1, n + 1):
if i % 3 == 0 and i % 5 == 0:
print("FizzBuzz")
elif ...
>>> fizzbuzz(5)
1
2
Fizz
4
Buzz
Corrigé
Le piège : il faut tester le cas « multiple de 3 et de 5 » (c’est-à-dire multiple de 15) en premier, sinon il serait capté par % 3 et ne donnerait jamais « FizzBuzz ».
def fizzbuzz(n):
for i in range(1, n + 1):
if i % 15 == 0:
print("FizzBuzz")
elif i % 3 == 0:
print("Fizz")
elif i % 5 == 0:
print("Buzz")
else:
print(i)
fizzbuzz(15) # la derniere ligne affichee doit etre FizzBuzz
★★ Exercice 24 — Un triangle d’étoiles #
▶ Écrire triangle(n) qui affiche un triangle de n lignes : la ligne numéro i contient i étoiles.0
Coup de pouce
Une ligne affichée par tour de boucle : quelles valeurs i doit-il prendre ? Pour construire la ligne, réutiliser ligne_etoiles de l’exercice « Construire une chaîne », ou tester "*" * 3 dans la console.
>>> triangle(3)
*
**
***
Corrigé
"*" * i construit une chaîne de i étoiles ; on affiche une ligne par tour de boucle.
def triangle(n):
for i in range(1, n + 1):
print("*" * i)
triangle(3) # affiche *, puis **, puis ***
Variante sans "*" * i, en réutilisant ligne_etoiles : print(ligne_etoiles(i)) à chaque tour.
Les boucles non bornées (while)#
★ Exercice 25 — Compte à rebours #
• essentiel
▶ Écrire compte_a_rebours(n) qui affiche n, n-1, …, 1 à l’aide d’une boucle while. Justifier que cette boucle se termine en exhibant un variant.0
Coup de pouce
Tant que quelle condition faut-il encore afficher n ? Ne pas oublier de modifier n dans la boucle, sinon elle ne s’arrête jamais. Pour le variant : quelle quantité entière positive diminue à chaque tour ?
Corrigé
def compte_a_rebours(n):
while n > 0:
print(n)
n = n - 1
Terminaison. La variable n est un variant : elle est positive tant qu’on entre dans la boucle (n > 0) et diminue de 1 à chaque tour. Une suite d’entiers positifs qui décroît strictement atteint forcément 0, ce qui rend la condition fausse : la boucle se termine.
▷ Erreur fréquente. oublier n = n - 1 : n ne change plus, la condition reste vraie et la boucle est infinie (Ctrl + C pour l’arrêter).
★★ Exercice 26 — Décortiquer un entier #
• essentiel
▶ À l’aide de n % 10 (dernier chiffre) et n // 10 (on retire le dernier chiffre) :
-
écrire
nombre_de_chiffres(n)qui renvoie combien de chiffres possèden; -
écrire
somme_chiffres(n)qui renvoie la somme des chiffres den.0Coup de pouce
Tant que
nn’est pas nul, on traite le dernier chiffre puis on le retire. Attention au casn = 0: combien de chiffres possède-t-il ?
>>> nombre_de_chiffres(347)
3
>>> somme_chiffres(347)
14
Corrigé
On ne sait pas d’avance combien de chiffres a n : boucle while n > 0. À chaque tour, on traite le dernier chiffre (n % 10), puis on le retire (n = n // 10).
def nombre_de_chiffres(n):
if n == 0:
return 1
compteur = 0
while n > 0:
n = n // 10
compteur = compteur + 1
return compteur
def somme_chiffres(n):
total = 0
while n > 0:
total = total + n % 10
n = n // 10
return total
On teste dans la console :
>>> nombre_de_chiffres(347)
3
>>> nombre_de_chiffres(0)
1
>>> somme_chiffres(347)
14
Dans les deux fonctions, n est un variant : c’est un entier positif, et l’affectation n = n // 10 le fait décroître strictement à chaque tour (car n \(\geqslant 1\) dans la boucle), jusqu’à 0.
▷ Erreur fréquente. oublier le cas n = 0 : la boucle ne fait aucun tour et nombre_de_chiffres(0) renverrait 0 au lieu de 1.
★★ Exercice 27 — Racine entière #
▶ Avec n dalles carrées, on veut carreler la plus grande terrasse carrée possible : k dalles de côté, donc \(k \times k\) dalles, sans dépasser les n dalles disponibles. Ce plus grand entier k tel que \(k \times k \leqslant n\) s’appelle la racine entière de n (avec n \(\geqslant 0\)). Écrire racine_entiere(n) avec une boucle while, sans utiliser ** ni de racine carrée toute faite. Justifier la terminaison.0
Coup de pouce
Partir de k = 0 et augmenter k tant que le suivant, k + 1, convient encore. Pour la terminaison, chercher une quantité entière positive qui diminue.
>>> racine_entiere(15)
3
>>> racine_entiere(16)
4
Corrigé
On part de k = 0 et on augmente k tant que le carré du suivant ne dépasse pas n ; en sortie, k convient et k + 1 ne convient plus.
def racine_entiere(n):
k = 0
while (k + 1) * (k + 1) <= n:
k = k + 1
return k
On teste dans la console :
>>> racine_entiere(15)
3
>>> racine_entiere(16)
4
>>> racine_entiere(0)
0
Avec 15 dalles, la plus grande terrasse carrée fait \(3 \times 3\) (9 dalles) ; avec 16 dalles, \(4 \times 4\). Terminaison. La quantité n - k*k décroît strictement (k augmente de 1 à chaque tour) et reste positive tant qu’on boucle : la boucle s’arrête donc.
★★ Exercice 28 — Dépasser un seuil #
▶ Léa remplit une tirelire : le jour 1 elle y met 1 €, le jour 2 elle y met 2 €, le jour 3 elle y met 3 €, et ainsi de suite. Au bout de n jours, la tirelire contient donc \(1 + 2 + \dots + n\) euros. Écrire seuil(s) qui renvoie le premier jour n où la tirelire contient strictement plus de s euros. Justifier la terminaison. Tester : seuil(1000) doit renvoyer 45.0
Coup de pouce
Deux variables évoluent ensemble : l’entier n et le total 1 + 2 + ... + n. Quelle condition écrire dans le while pour continuer tant que le seuil n’est pas dépassé ?
Corrigé
Deux variables avancent ensemble (le jour n et le contenu total) ; on continue tant que le seuil n’est pas dépassé, c’est-à-dire tant que total <= s.
def seuil(s):
n = 0
total = 0
while total <= s:
n = n + 1
total = total + n
return n
On teste dans la console :
>>> seuil(1000) # 1+...+44 = 990, 1+...+45 = 1035
45
Le total total est le contenu de la tirelire et n le numéro du jour : Léa dépasse 1000 € le 45e jour. Terminaison. La quantité s - total décroît strictement à chaque tour (on ajoute au moins 1 à total) : elle finit par devenir négative, ce qui arrête la boucle.
★★★ Exercice 29 — PGCD (algorithme d’Euclide) #
≈ 15 min
▶ Pour calculer le PGCD de deux entiers strictement positifs, on remplace répétitivement le plus grand par sa différence avec le plus petit, jusqu’à ce que les deux soient égaux : cette valeur commune est le PGCD.
-
Écrire
pgcd(a, b)avec une bouclewhile a != b. -
Justifier la terminaison : trouver une quantité qui décroît strictement à chaque tour et reste positive.0
Coup de pouce
Faire d’abord à la main
pgcd(12, 18): noter les valeurs deaetbà chaque tour. À chaque tour, lequel des deux modifie-t-on ?0
Coup de pouce 2 (début de solution)
while a != b:
if a > b:
a = a - b
else:…
>>> pgcd(12, 18)
6
Corrigé
def pgcd(a, b):
while a != b:
if a > b:
a = a - b
else:
b = b - a
return a
On teste dans la console :
>>> pgcd(12, 18)
6
>>> pgcd(7, 7)
7
>>> pgcd(1071, 462)
21
Terminaison. La quantité a + b est un variant : à chaque tour on retranche à l’un des deux une valeur strictement positive, donc a + b décroît strictement tout en restant \(\geqslant 2\). La boucle ne peut donc pas tourner indéfiniment.
★★★ Exercice 30 — Racine numérique #
≈ 15 min
▶ La racine numérique d’un entier s’obtient en additionnant ses chiffres, puis en recommençant sur le résultat, jusqu’à n’obtenir qu’un seul chiffre. Par exemple : \(347 \rightarrow 3+4+7 = 14 \rightarrow 1+4 = 5\). Écrire racine_numerique(n) à l’aide d’une boucle while. Justifier la terminaison.0
Coup de pouce
Une fonction déjà écrite dans l’exercice « Décortiquer un entier » fait l’essentiel du travail. Comment reconnaître qu’un entier n’a plus qu’un seul chiffre ?
0
Coup de pouce 2 (début de solution)
def racine_numerique(n):
while n >= 10:
…
>>> racine_numerique(347)
5
>>> racine_numerique(12345)
6
Corrigé
Tant que n a plus d’un chiffre, on le remplace par la somme de ses chiffres.
def somme_chiffres(n): # deja ecrite plus haut
total = 0
while n > 0:
total = total + n % 10
n = n // 10
return total
def racine_numerique(n):
while n >= 10:
n = somme_chiffres(n)
return n
On teste dans la console :
>>> racine_numerique(347)
5
>>> racine_numerique(9)
9
>>> racine_numerique(12345)
6
Terminaison. Dès que n a au moins deux chiffres, la somme de ses chiffres est strictement plus petite que n : la valeur de n décroît strictement à chaque tour tout en restant positive, jusqu’à passer sous 10.
Fonctions : spécifier et tester#
★★ Exercice 31 — Nombres premiers #
• essentiel
▶ Un entier n est premier s’il vaut au moins 2 et n’admet aucun diviseur autre que 1 et lui-même.
-
Écrire
est_premier(n). -
Rédiger un jeu de tests (
assert) couvrant : un premier, un non-premier, et les cas limites0,1,2.0Coup de pouce
Traiter d’abord les entiers inférieurs à
2. Ensuite, chercher un diviseur entre2etn - 1: dès qu’on en trouve un, la réponse est connue.
Corrigé
On écarte d’abord les entiers inférieurs à 2, puis on cherche un diviseur entre 2 et n - 1. Le jeu de tests contient des cas normaux et les cas limites 0, 1, 2 demandés.
def est_premier(n):
if n < 2:
return False
for d in range(2, n):
if n % d == 0:
return False
return True
assert est_premier(2) == True # cas limite
assert est_premier(7) == True
assert est_premier(97) == True
assert est_premier(0) == False # cas limite
assert est_premier(1) == False # cas limite
assert est_premier(9) == False
Dès qu’un diviseur est trouvé, return False sort immédiatement de la fonction : inutile de continuer.
▷ Erreur fréquente. ajouter else: return True dans la boucle : la fonction répond dès le premier diviseur essayé (d = 2), et est_premier(9) renverrait True. On ne peut conclure « premier » qu’après la boucle.
★★ Exercice 32 — Palindrome #
▶ Un palindrome est un mot qui se lit de la même façon dans les deux sens (« radar », « kayak »). Écrire une fonction booléenne est_palindrome(mot) qui le détecte. Prévoir les cas limites (mot vide, une seule lettre). (On rappelle que len(mot) est le nombre de caractères de mot et que mot[i] est le caractère de position i, la première position étant 0.)0
Coup de pouce
Comparer le premier et le dernier caractère, puis se rapprocher du centre avec deux indices i et j. Quand faut-il s’arrêter ?
>>> est_palindrome("radar")
True
>>> est_palindrome("bonjour")
False
Corrigé
On compare les caractères par paires, en partant des deux bords et en se rapprochant du centre avec deux indices i et j. La quantité j - i est un variant : elle diminue de 2 à chaque tour.
def est_palindrome(mot):
i = 0
j = len(mot) - 1
while i < j:
if mot[i] != mot[j]:
return False
i = i + 1
j = j - 1
return True
assert est_palindrome("radar") == True
assert est_palindrome("bonjour") == False
assert est_palindrome("") == True # cas limites
assert est_palindrome("a") == True
★★ Exercice 33 — Jouer avec les bords d’un mot #
▶
-
Écrire
meme_bord(mot)qui renvoieTruesi le mot commence et se termine par la même lettre. (On rappelle quemot[0]est la première lettre etmot[-1]la dernière.) -
Écrire
memes_bords(mot1, mot2)qui renvoieTruesi les deux mots commencent par la même lettre et finissent par la même lettre.0Coup de pouce
Aucune boucle n’est nécessaire : une seule comparaison (ou deux, reliées par
and) suffit. Que se passe-t-il avec le mot vide ?
>>> meme_bord("radar")
True
>>> memes_bords("chat", "chocolat")
True
Corrigé
Aucune boucle : on compare directement la première lettre (mot[0]) et la dernière (mot[-1]), après avoir écarté le mot vide, qui n’a pas de première lettre.
def meme_bord(mot):
if mot == "":
return True # cas limite : mot vide
return mot[0] == mot[-1]
def memes_bords(mot1, mot2):
if mot1 == "" or mot2 == "":
return False # cas limite : pas de premiere lettre a comparer
return mot1[0] == mot2[0] and mot1[-1] == mot2[-1]
assert meme_bord("radar") == True
assert meme_bord("bonjour") == False
assert memes_bords("chat", "chocolat") == True
assert memes_bords("chat", "chien") == False
assert memes_bords("", "chat") == False
★★ Exercice 34 — Le bug qui passe les tests #
On propose la fonction suivante, censée additionner deux nombres :
def addition(a, b):
return a * b
-
Vérifier que le jeu de tests
assert addition(2, 2) == 4etassert addition(0, 0) == 0passe pourtant sans erreur. Expliquer pourquoi. -
Proposer un test
assertqui démasque le bug.0Coup de pouce
Comparer
2 + 2et2 * 2, puis0 + 0et0 * 0. Chercher deux nombres pour lesquels somme et produit diffèrent. -
Que peut-on en conclure sur la phrase du programme : « le succès d’un jeu de tests ne garantit pas la correction d’un programme » ?
Corrigé
1. La fonction renvoie a * b au lieu de a + b. Or les deux tests choisis tombent sur des cas où produit et somme coïncident : \(2 \times 2 = 2 + 2 = 4\) et \(0 \times 0 = 0 + 0 = 0\). Les assert passent donc, alors que la fonction est fausse.
2. Un test qui les sépare démasque le bug, par exemple :
assert addition(1, 3) == 4 # echoue : 1 * 3 = 3, pas 4
3. Réussir un jeu de tests prouve seulement que la fonction est correcte sur les cas testés, jamais qu’elle l’est en général. Un jeu de tests mal choisi peut laisser passer un programme faux : c’est exactement le sens de la phrase du programme. D’où l’importance de tester des cas variés (normaux, limites, particuliers).
Exercices de synthèse#
★★★ Exercice 35 — Les horaires du bus #
≈ 30 min
Sur une ligne de bus de Monaco, le premier bus part à 6 h 30, puis il en part un toutes les 12 minutes ; aucun bus ne part après 21 h 00. Pour calculer facilement, on compte les heures en minutes écoulées depuis minuit : 6 h 30 correspond à \(6 \times 60 + 30 = 390\).
-
▶ Écrire
en_minutes(h, m)qui renvoie le nombre de minutes écoulées depuis minuit à l’heurehhm. -
▶ Écrire
prochain_bus(t)qui renvoie l’horaire (en minutes) du premier bus partant à l’instanttou après, et-1s’il n’y a plus de bus ce jour-là. On utilisera une bouclewhile.0Coup de pouce
Partir de l’horaire du premier bus et passer au bus suivant tant qu’il part trop tôt. Une fois sorti de la boucle, ce bus part-il encore avant 21 h 00 ?
0
Coup de pouce 2 (début de solution)
def prochain_bus(t):
depart = 390
while depart < t:
depart = depart + 12
… -
▶ Écrire
attente(h, m)qui renvoie le nombre de minutes à attendre le prochain bus quand on arrive à l’arrêt àhhm, et-1s’il n’y a plus de bus. -
Quel est l’horaire du dernier bus de la journée ? L’écrire en heures et minutes à l’aide de
//et%.
>>> en_minutes(6, 30)
390
>>> prochain_bus(400)
402
>>> attente(8, 0)
6
>>> attente(22, 15)
-1
Corrigé
On part du premier bus (390) et on passe au suivant (+ 12) tant qu’il part avant l’instant t. En sortie de boucle, il reste à vérifier que ce bus part encore avant 21 h 00, soit \(21 \times 60 = 1260\).
def en_minutes(h, m):
return h * 60 + m
def prochain_bus(t):
depart = 390 # 6 h 30
while depart < t:
depart = depart + 12 # bus suivant
if depart > 1260: # apres 21 h 00 : plus de bus
return -1
return depart
def attente(h, m):
t = en_minutes(h, m)
depart = prochain_bus(t)
if depart == -1:
return -1
return depart - t
assert en_minutes(6, 30) == 390
assert prochain_bus(400) == 402
assert prochain_bus(0) == 390 # avant le premier bus
assert prochain_bus(402) == 402 # un bus part pile a cet instant
assert attente(8, 0) == 6
assert attente(22, 15) == -1
4. Les départs ont lieu à \(390 + 12k\). Comme \(1260 - 390 = 870 = 72 \times 12 + 6\), le dernier départ est \(390 + 72 \times 12 = 1254\) minutes, et 1254 // 60 vaut 20, 1254 % 60 vaut 54 : le dernier bus part à 20 h 54. (On peut le vérifier : prochain_bus(1249) renvoie 1254 et prochain_bus(1255) renvoie -1.)
★★★ Exercice 36 — Un mot de passe robuste #
≈ 30 min
Un site exige des mots de passe robustes : au moins 8 caractères, au moins une majuscule, au moins un chiffre et au moins un caractère spécial parmi !?@#$%&*. (On rappelle que len(mdp) est le nombre de caractères de mdp.)
-
▶ Écrire
compter_chiffres(mdp)qui renvoie le nombre de chiffres que contient la chaînemdp. -
▶ Écrire
est_robuste(mdp)qui renvoieTruesi le mot de passe respecte les quatre règles,Falsesinon.0Coup de pouce
Pour la question 1, reprendre l’exercice « Compter les voyelles » : seule la chaîne de référence change (
"0123456789"au lieu de"aeiouy"). Compter de même les majuscules et les caractères spéciaux.0
Coup de pouce 2 (début de solution)
def est_robuste(mdp):
majuscules = 0
speciaux = 0
for c in mdp:
if c in "ABCDEFGHIJKLMNOPQRSTUVWXYZ":
… -
Rédiger un jeu de tests (
assert) contenant un mot de passe robuste et, pour chacune des quatre règles, un mot de passe qui enfreint cette seule règle et respecte les trois autres.
>>> compter_chiffres("Monaco98")
2
>>> est_robuste("Monaco98!")
True
>>> est_robuste("monaco98!")
False
Corrigé
Même motif que « Compter les voyelles » : un compteur par catégorie de caractères, puis les quatre règles reliées par and.
def compter_chiffres(mdp):
n = 0
for c in mdp:
if c in "0123456789":
n = n + 1
return n
def est_robuste(mdp):
majuscules = 0
speciaux = 0
for c in mdp:
if c in "ABCDEFGHIJKLMNOPQRSTUVWXYZ":
majuscules = majuscules + 1
if c in "!?@#$%&*":
speciaux = speciaux + 1
return (len(mdp) >= 8 and majuscules >= 1
and compter_chiffres(mdp) >= 1 and speciaux >= 1)
assert compter_chiffres("Monaco98") == 2
assert compter_chiffres("") == 0
assert est_robuste("Monaco98!") == True
assert est_robuste("Mon98!") == False # trop court
assert est_robuste("monaco98!") == False # pas de majuscule
assert est_robuste("Monacooo!") == False # pas de chiffre
assert est_robuste("Monaco98") == False # pas de caractere special
Chaque mot de passe refusé n’enfreint qu’une seule règle : si un test échoue, on sait immédiatement quelle condition est mal écrite.
★★★ Exercice 37 — Nombres premiers jumeaux #
≈ 15 min
Deux nombres premiers sont jumeaux si leur différence vaut 2 (par exemple 3 et 5, ou 11 et 13).
▶ En réutilisant est_premier, écrire premiers_jumeaux(n) qui affiche, un couple par ligne, tous les couples p et p+2 de premiers jumeaux tels que les deux nombres soient strictement inférieurs à n.0
Coup de pouce
Parcourir les candidats p un par un et tester deux conditions avec and. Quelle condition garantit que p + 2 reste strictement inférieur à n ?
0
Coup de pouce 2 (début de solution)
for p in range(2, n):
if p + 2 < n and est_premier(p) and …
>>> premiers_jumeaux(20)
3 et 5
5 et 7
11 et 13
17 et 19
Corrigé
On réutilise est_premier. La condition p + 2 < n garantit que les deux nombres du couple sont strictement inférieurs à n.
def premiers_jumeaux(n):
for p in range(2, n):
if p + 2 < n and est_premier(p) and est_premier(p + 2):
print(p, "et", p + 2)
premiers_jumeaux(20) # affiche 3 et 5, 5 et 7, 11 et 13, 17 et 19
★★ Exercice 38 — Une somme selon un assistant d’IA #
Un élève demande à un assistant d’IA : « Écris une fonction Python qui renvoie la somme des entiers impairs de 1 à n. » Voici la réponse obtenue.
def somme_impairs(n):
somme = 0
for i in range(1, n, 2):
somme = somme + i
return somme
« range(1, n, 2) produit 1, 3, 5, … jusqu’à n ; on ajoute chaque valeur à somme, qui est renvoyée à la fin. Par exemple, somme_impairs(5) renvoie 1 + 3 + 5, soit 9. »
-
La réponse est-elle correcte ? Faire la trace de
somme_impairs(5)(valeurs successives deiet desomme), puis ▶ vérifier dans la console. -
Localiser et corriger l’erreur.
-
Quelle vérification simple, faite avant de faire confiance à la réponse, aurait suffi à détecter l’erreur ?0
Coup de pouce
Relire la définition de
range(a, b, pas)dans le cours : la bornebest-elle atteinte ?
Corrigé
1. Non. Trace de somme_impairs(5) : range(1, 5, 2) donne 1 puis 3, et s’arrête avant 5 (la borne est exclue). somme vaut 0, puis 1, puis 4 : la fonction renvoie 4, et non 9 comme l’annonce pourtant l’assistant.
>>> somme_impairs(5)
4
2. L’erreur est la borne du range : range(1, n, 2) exclut n. Il faut range(1, n + 1, 2) :
def somme_impairs(n):
somme = 0
for i in range(1, n + 1, 2):
somme = somme + i
return somme
print(somme_impairs(5)) # 9
print(somme_impairs(6)) # 9 (1 + 3 + 5)
3. Exécuter l’exemple que l’assistant donne lui-même : il annonce 9 pour somme_impairs(5), la console affiche 4. La réponse se contredit ; un seul appel suffisait. Le cas où n est lui-même impair est justement le cas limite à tester.
★★★ Exercice 39 — Euclide en accéléré #
≈ 20 min
L’algorithme de l’exercice « PGCD (algorithme d’Euclide) » est décrit par le mathématicien grec Euclide, vers 300 av. J.-C., au livre VII de ses Éléments (propositions 1 et 2). L’informaticien Donald Knuth y voit le « grand-père de tous les algorithmes », le plus ancien algorithme non trivial encore utilisé aujourd’hui (The Art of Computer Programming, vol. 2, 1981, p. 318). Il a pourtant un défaut, que l’on va corriger.
-
▶ Reprendre la fonction
pgcd(a, b)de l’exercice « PGCD » et y ajouter une variabletours, augmentée de 1 à chaque tour de boucle et affichée juste avant lereturn. Combien de tours faut-il pourpgcd(1000, 3)? -
Dans ce calcul, on retire
3à1000encore et encore, tant que c’est possible. Quelle opération du cours donne directement ce qu’il reste à la fin de toutes ces soustractions ? -
▶ En déduire
pgcd_rapide(a, b): tant quebn’est pas nul, on remplaceaparbetbpar le reste de la division deaparb; à la fin,aest le PGCD. Afficher aussi le nombre de tours : combien pourpgcd_rapide(1000, 3)?0Coup de pouce
Si l’on écrit
a = bpuisb = a % b, l’ancienne valeur deaest déjà perdue. Calculer d’abord le reste dans une variabler, puis faire les deux affectations. -
Justifier la terminaison de
pgcd_rapide: quelle quantité entière, positive, diminue strictement à chaque tour ?
>>> pgcd_rapide(12, 18)
3 tours
6
>>> pgcd_rapide(1071, 462)
3 tours
21
Corrigé
1. On ajoute un compteur à la fonction de l’exercice « PGCD » :
def pgcd(a, b):
"""Renvoie le PGCD de a et b (entiers strictement positifs)."""
tours = 0
while a != b:
if a > b:
a = a - b
else:
b = b - a
tours = tours + 1
print(tours, "tours")
return a
print(pgcd(1000, 3)) # 335 tours, puis 1
Il faut 335 tours : 333 soustractions de 3 pour descendre de 1000 à 1, puis deux tours pour amener b de 3 à 1.
2. Retirer 3 autant de fois que possible à 1000, c’est faire la division euclidienne de 1000 par 3 : ce qui reste à la fin est le reste 1000 % 3, qui vaut 1.
3. On calcule d’abord le reste dans r, sinon l’ancienne valeur de a serait perdue :
def pgcd_rapide(a, b):
"""Renvoie le PGCD de a et b (entiers strictement positifs)."""
tours = 0
while b != 0:
r = a % b
a = b
b = r
tours = tours + 1
print(tours, "tours")
return a
print(pgcd_rapide(1000, 3)) # 2 tours, puis 1
print(pgcd_rapide(12, 18)) # 3 tours, puis 6
print(pgcd_rapide(1071, 462)) # 3 tours, puis 21
Pour pgcd_rapide(1000, 3), 2 tours suffisent au lieu de 335. Si a < b, le premier tour échange simplement les deux nombres (12 % 18 vaut 12).
4. La quantité b est un entier positif ou nul, et elle diminue strictement à chaque tour, car le reste de la division par b est toujours strictement inférieur à b. Une telle quantité ne peut pas diminuer indéfiniment : elle finit par valoir 0, et la boucle s’arrête.
Entraînement#
Exercices courts pour s’entraîner aux gestes de base, chacun avec plusieurs données. Ils font tous partie du parcours essentiel.
★ Exercice 40 — Lire un entier au clavier #
• essentiel
▶ Pour chaque programme, écrire les lignes qui demandent la valeur au clavier avec input, la convertissent avec int, puis affichent le résultat demandé.
-
Demander l’âge de l’utilisateur et afficher l’âge qu’il aura dans 10 ans.
-
Demander le prix d’un article (un nombre entier d’euros) et afficher le prix de trois articles.
-
Demander un nombre de minutes et l’afficher en heures et minutes (
135donne2 h 15 min).
Dans le programme a), que se passe-t-il si l’on oublie int ?0
Coup de pouce
Le paragraphe « Lire une entrée au clavier et convertir » du cours donne le modèle : input renvoie toujours une chaîne. Pour c), penser à // et % avec 60.
Votre age ? 16
Dans 10 ans, vous aurez 26 ans
Corrigé
input renvoie une chaîne : on la convertit avec int avant de calculer.
age = int(input("Votre age ? "))
print("Dans 10 ans, vous aurez", age + 10, "ans")
prix = int(input("Prix d'un article (en euros) ? "))
print("Trois articles coutent", 3 * prix, "euros")
minutes = int(input("Nombre de minutes ? "))
print(minutes // 60, "h", minutes % 60, "min") # 135 donne 2 h 15 min
Sans int, age est la chaîne "16" et age + 10 provoque :
TypeError: can only concatenate str (not "int") to str
▷ Erreur fréquente. oublier int : on ne peut pas additionner un str et un int. Et avec 3 * prix sans conversion, Python ne signale rien mais répète la chaîne : 3 * "4" donne "444".
★ Exercice 41 — Longueur, premier et dernier caractère #
• essentiel
-
Sans machine, donner
len(mot),mot[0]etmot[len(mot) - 1]pour chacune des valeurs suivantes demot: a)"Monaco"; b)"NSI"; c)"z"; d)"Grace Hopper". ▶ Vérifier ensuite dans la console. -
Pourquoi
mot[len(mot)]provoque-t-il toujours une erreur ? -
▶ Écrire
dernier(mot)qui renvoie le dernier caractère d’un mot non vide, à l’aide delen.0Coup de pouce
Les positions commencent à
0: un mot de 3 caractères occupe les positions0,1et2. L’espace compte comme un caractère.
>>> dernier("Monaco")
'o'
Corrigé
1. a) 6, ’M’, ’o’ ; b) 3, ’N’, ’I’ ; c) 1, ’z’, ’z’ (premier et dernier caractère sont le même) ; d) 12 (l’espace compte), ’G’, ’r’.
2. Les positions vont de 0 à len(mot) - 1 : la position len(mot) n’existe pas (IndexError: string index out of range).
3.
def dernier(mot):
return mot[len(mot) - 1]
★ Exercice 42 — Prédire l’affichage, puis exécuter : for #
• essentiel
Pour chaque programme, écrire sans machine ce qu’il affiche, puis ▶ l’exécuter. En cas d’écart, trouver l’erreur de raisonnement avant de passer au programme suivant.0
Coup de pouce
Faire un tableau de trace : une ligne par tour, avec la valeur de la variable de boucle et des autres variables. Bien regarder si le print est dans la boucle (décalé) ou après.
a)
for i in range(4):
print(i * 3)
b)
for i in range(2, 12, 4):
print(i)
c)
total = 0
for i in range(1, 5):
total = total + i
print(total)
d)
mot = ""
for lettre in "NSI":
mot = lettre + mot
print(mot)
Corrigé
(un nombre par ligne à l’écran) a) 0 3 6 9 : i prend les valeurs 0 à 3. b) 2 6 10 : de 2 en 4 ; 14 dépasserait la borne exclue 12. c) 1 3 6 10 : le print est dans la boucle, on voit le total après chaque tour. d) ISN : chaque lettre est placée devant le mot déjà construit, ce qui le renverse ; un seul affichage, après la boucle.
▷ Erreur fréquente. ne pas regarder l’indentation du print : en c), il est dans la boucle (quatre affichages) ; en d), il est après (un seul).
★ Exercice 43 — Prédire l’affichage, puis exécuter : while #
• essentiel
Même consigne avec des boucles while : prédire sans machine, puis ▶ exécuter.0
Coup de pouce
La condition est testée avant chaque tour : noter à chaque tour la valeur des variables, puis si la condition est encore vraie. Elle peut être fausse dès le départ.
a)
n = 1
while n < 20:
n = n * 2
print(n)
b)
n = 10
while n > 0:
print(n)
n = n - 3
c)
i = 5
while i < 5:
print(i)
i = i + 1
print("fin")
d)
x = 100
tours = 0
while x > 1:
x = x // 2
tours = tours + 1
print(x, tours)
Corrigé
a) 32 : n vaut 2, 4, 8, 16, puis 32, qui rend la condition fausse ; un seul affichage, après la boucle. b) 10 7 4 1 : après 1, n vaut -2 et la boucle s’arrête. c) seulement fin : la condition 5 < 5 est fausse dès le départ, la boucle ne fait aucun tour. d) 1 6 : x vaut 50, 25, 12, 6, 3, 1, soit 6 tours.
▷ Erreur fréquente. croire qu’une boucle while fait toujours au moins un tour : la condition est testée avant le premier tour (cas c).
★ Exercice 44 — Spécifier et tester une fonction #
• essentiel
Pour chacune des deux fonctions ci-dessous : (1) écrire sa docstring (rôle, précondition, postcondition) ; (2) ajouter un garde-fou assert sur la précondition ; (3) ▶ écrire un jeu de trois tests assert, dont au moins un cas limite.0
Coup de pouce
Reprendre l’exemple racine(x) du paragraphe « Spécifier une fonction » du cours. Un cas limite est une valeur « au bord » : le seuil d’une condition, ou 0.
a)
def prix_billet(age):
if age < 12:
return 5
return 9
b)
def aire_carre(cote):
return cote * cote
Corrigé
La docstring dit le rôle, ce que l’on attend des arguments (précondition) et ce que l’on garantit (postcondition) ; l’assert traduit la précondition ; les tests visent des cas normaux et un cas limite.
def prix_billet(age):
"""Renvoie le prix du billet (en euros) pour une personne de cet age.
Precondition : age est un entier positif ou nul.
Postcondition : renvoie 5 si age < 12, et 9 sinon."""
assert age >= 0, "l'age doit etre positif"
if age < 12:
return 5
return 9
assert prix_billet(8) == 5
assert prix_billet(30) == 9
assert prix_billet(12) == 9 # cas limite : le seuil de la condition
def aire_carre(cote):
"""Renvoie l'aire d'un carre de cote donne.
Precondition : cote >= 0.
Postcondition : le resultat est positif ou nul."""
assert cote >= 0, "le cote doit etre positif"
return cote * cote
assert aire_carre(3) == 9
assert aire_carre(2.5) == 6.25
assert aire_carre(0) == 0 # cas limite
▷ Erreur fréquente. ne tester que des valeurs « faciles » (8 et 30) : c’est au seuil 12 qu’une erreur < au lieu de <= (ou l’inverse) se cache.
★ Exercice 45 — Corriger d’après le message d’erreur (1) #
• essentiel
On exécute le fichier voyelles.py suivant :
def compter_voyelles(mot):
n = 0
for lettre in mot:
if lettre in "aeiouy":
n = n + 1
return n
print(compter_voyelles("bonjour"))
Python affiche :
File "/home/eleve/voyelles.py", line 5
n = n + 1
^
IndentationError: expected an indented block after 'if' statement on line 4
-
Quel est le type de l’erreur ? Quelles lignes le message cite-t-il ? Traduire la dernière ligne.
-
Corriger le programme. Qu’affiche-t-il alors ?
-
Le programme a-t-il commencé à s’exécuter avant l’erreur ? Justifier.0
Coup de pouce
Lire d’abord la dernière ligne du message : expected signifie « attendu », indented block « bloc décalé ». Quelles lignes doivent être décalées sous un
if?
Corrigé
1. Une IndentationError (erreur d’indentation). Le message cite la ligne 4 (le if) et la ligne 5, où se trouve l’erreur : « un bloc décalé était attendu après l’instruction if de la ligne 4 ». La ligne n = n + 1 n’est pas décalée sous le if.
2. On décale n = n + 1 de 4 espaces de plus (8 en tout) ; le programme affiche alors 3.
3. Non : comme une erreur de syntaxe, une erreur d’indentation est détectée en lisant le fichier, avant toute exécution (il n’y a d’ailleurs pas de ligne Traceback). Aucune ligne n’a été exécutée, même pas le print final.
▷ Erreur fréquente. décaler la ligne d’un nombre d’espaces quelconque : chaque niveau de bloc ajoute exactement 4 espaces, et toutes les lignes d’un même bloc doivent être alignées.
★ Exercice 46 — Corriger d’après le message d’erreur (2) #
• essentiel
On exécute le fichier moyenne.py suivant :
def moyenne(a, b, c):
total = a + b + c
return totale / 3
print(moyenne(12, 15, 9))
Python affiche :
Traceback (most recent call last):
File "/home/eleve/moyenne.py", line 5, in <module>
print(moyenne(12, 15, 9))
^^^^^^^^^^^^^^^^^^
File "/home/eleve/moyenne.py", line 3, in moyenne
return totale / 3
^^^^^^
NameError: name 'totale' is not defined. Did you mean: 'total'?
-
Quel est le type de l’erreur ? Dans quelle fonction et à quelle ligne se produit-elle ?
-
Pourquoi le message cite-t-il aussi la ligne 5 ?
-
Corriger le programme et donner ce qu’il affiche alors.0
Coup de pouce
Le message se lit de bas en haut : la dernière ligne donne le type d’erreur et son explication, le bloc juste au-dessus la ligne fautive. Comparer les noms de variables des lignes 2 et 3.
Corrigé
1. Une NameError : le nom totale n’existe pas. Elle se produit à la ligne 3, dans la fonction moyenne (dernier bloc avant la dernière ligne).
2. La ligne 5 est l’appel de la fonction : le message remonte le chemin suivi, de l’appel (ligne 5) jusqu’à la ligne fautive, à l’intérieur de la fonction (ligne 3).
3. La variable s’appelle total (ligne 2) : on écrit return total / 3. Le programme affiche alors 12.0.
▷ Erreur fréquente. corriger la ligne 5, la première citée : la ligne fautive est celle du dernier bloc, juste au-dessus du type d’erreur.