Réussi sans hésitation : passe au suivant. Réussi avec hésitation : fais deux exercices supplémentaires pour consolider. Échoué : retravaille la leçon correspondante avant de poursuivre.
maximum(L) qui renvoie le plus grand élément d'une liste non vide, sans utiliser max() (la liste peut contenir des nombres négatifs).Réécrire la boucle suivante sous forme de liste en compréhension :
resultat = []
for x in range(1, 11):
if x % 3 == 0:
resultat.append(x ** 2)
Expliquer pourquoi le code suivant affiche [99, 2, 3] :
A = [1, 2, 3]
B = A
B[0] = 99
print(A)
Proposer deux écritures qui, elles, préservent A.
def maximum(L):
maxi = L[0]
for x in L:
if x > maxi:
maxi = x
return maxi
Initialiser maxi avec L[0] et non 0 : sinon, pour une liste de nombres tous négatifs, la fonction renverrait $0$, qui n'est pas dans la liste.
resultat = [x ** 2 for x in range(1, 11) if x % 3 == 0]. Résultat : [9, 36, 81].B = A crée un alias, pas une copie : A et B désignent la même liste en mémoire. Modifier B[0] modifie donc aussi A. Pour une copie indépendante, on écrit B = A[:] ou B = list(A).grille = [[0] * 4 for _ in range(3)]
grille[2][3] = 1
Attention : grille = [[0] * 4] * 3 donne trois fois la même ligne (alias), modifier une cellule modifie les trois lignes.
Si tu échoues : reprends la syntaxe des listes en compréhension, la distinction entre référence et copie, et la construction d'un tableau $2$D. Ces trois points sont systématiquement utilisés en terminale (parcours de matrices, implémentation de structures, algorithmes sur les graphes).
planetes associant "Terre" à $1$, "Mars" à $2$ et "Jupiter" à $5$. Ajouter ensuite la clé "Saturne" avec la valeur $9$.inverser(d) qui échange clés et valeurs d'un dictionnaire (on suppose les valeurs distinctes et immuables).d["cle"] et d.get("cle", 0) ?planetes = {"Terre": 1, "Mars": 2, "Jupiter": 5}
planetes["Saturne"] = 9
def inverser(d):
return {v: k for k, v in d.items()}
d["cle"] lève KeyError si la clé est absente ; d.get("cle", 0) renvoie $0$ (valeur par défaut) sans erreur.Si tu échoues : reprends la syntaxe {cle: valeur}, l'ajout par d[cle] = valeur, la méthode items() qui donne les paires parcourables, et la différence tuple (immuable) / liste (mutable). Les dictionnaires indexés par tuples sont omniprésents en terminale (matrices creuses, mémoïsation, graphes).
recherche(L, x) qui renvoie l'indice de la première occurrence de x dans L, ou $-1$ si x est absent.Donner la trace de la recherche dichotomique de $7$ dans L = [1, 3, 5, 7, 9, 11] :
| Étape | gauche | droite | milieu | Comparaison |
|---|---|---|---|---|
| 1 | ||||
| 2 | ||||
| 3 |
def recherche(L, x):
for i in range(len(L)):
if L[i] == x:
return i
return -1
Trace pour la dichotomie de $7$ dans [1, 3, 5, 7, 9, 11] :
| Étape | gauche | droite | milieu | Comparaison |
|---|---|---|---|---|
| 1 | 0 | 5 | 2 | $L[2] = 5 < 7$, on cherche à droite |
| 2 | 3 | 5 | 4 | $L[4] = 9 > 7$, on cherche à gauche |
| 3 | 3 | 3 | 3 | $L[3] = 7$, trouvé à l'indice $3$ |
Si tu échoues : reprends la recherche séquentielle (avec sortie anticipée dès que l'élément est trouvé) et la dichotomie sur tableau trié (maintien des bornes gauche et droite, calcul du milieu, mise à jour par moitié). Retiens la précondition de tri : la dichotomie sur un tableau non trié renvoie n'importe quoi.
tri_selection(L) qui trie la liste L en place (l'algorithme cherche, à chaque étape, le minimum de la partie non encore triée et le place à sa position).Compléter le tableau d'état du tri par sélection appliqué à L = [5, 2, 8, 1, 4] :
Itération i | Minimum de L[i:] | Échange | État de L après échange |
|---|---|---|---|
| 0 | |||
| 1 | |||
| 2 | |||
| 3 |
def tri_selection(L):
n = len(L)
for i in range(n - 1):
imin = i
for j in range(i + 1, n):
if L[j] < L[imin]:
imin = j
L[i], L[imin] = L[imin], L[i]
État du tri par sélection sur [5, 2, 8, 1, 4] :
Itération i | Minimum de L[i:] | Échange | État de L après échange |
|---|---|---|---|
| 0 | $1$ (en position $3$) | L[0] $\leftrightarrow$ L[3] | [1, 2, 8, 5, 4] |
| 1 | $2$ (en position $1$) | pas d'échange | [1, 2, 8, 5, 4] |
| 2 | $4$ (en position $4$) | L[2] $\leftrightarrow$ L[4] | [1, 2, 4, 5, 8] |
| 3 | $5$ (en position $3$) | pas d'échange | [1, 2, 4, 5, 8] |
Si tu échoues : reprends le schéma commun aux deux tris (invariant : la partie gauche de la liste est triée à chaque itération) et la différence de stratégie (sélection : on choisit le minimum ; insertion : on insère le courant). Écrire au moins une fois un tri à la main est indispensable avant de voir le tri fusion en terminale.
rendu(somme, pieces) qui rend somme en utilisant le minimum de pièces selon la stratégie gloutonne, en supposant pieces triée par ordre décroissant. La fonction renvoie la liste des pièces utilisées.rendu(47, [200, 100, 50, 20, 10, 5, 2, 1]). Combien de pièces la fonction renvoie-t-elle ?[4, 3, 1]. L'algorithme glouton donne-t-il toujours le nombre minimal de pièces pour obtenir $6$ ? Justifier avec un contre-exemple.def rendu(somme, pieces):
resultat = []
for p in pieces:
while somme >= p:
resultat.append(p)
somme -= p
return resultat
rendu(47, [200, 100, 50, 20, 10, 5, 2, 1]) renvoie [20, 20, 5, 2], soit $4$ pièces.[4, 3, 1] pour obtenir $6$ : le glouton prend d'abord $4$, puis il faut faire $2$ avec les pièces restantes, ce qui donne $4 + 1 + 1 = 3$ pièces. Or $3 + 3 = 6$ ne nécessite que $2$ pièces. Le système n'est donc pas canonique : sur celui-ci, le glouton n'est pas optimal.Si tu échoues : reprends la définition (choix local, pas de retour en arrière) et le principe du rendu de monnaie. Retiens surtout que le système de pièces de l'euro est canonique (le glouton est optimal), mais que changer le système peut rendre le glouton sous-optimal. En terminale, cette nuance revient pour justifier que programmer dynamique est parfois nécessaire.
Si tu échoues : reprends la distance euclidienne en dimension $2$ puis $d$, le principe du vote majoritaire, et l'effet du choix de $k$. La normalisation des variables est un réflexe à acquérir : elle réapparaîtra en terminale dès qu'on mélange des grandeurs de nature différente.
Donner la complexité de chacune des fonctions suivantes en fonction de la taille $n$ de l'entrée :
def f(L):
s = 0
for x in L:
s += x
return s
def g(L):
for x in L:
for y in L:
pass
def h(n):
while n > 1:
n = n // 2
return n
f : une boucle parcourt la liste, donc $O(n)$. g : deux boucles imbriquées parcourent la liste, donc $O(n^2)$. h : $n$ est divisé par $2$ à chaque itération, le nombre d'itérations est de l'ordre de $\log_2 n$, donc $O(\log n)$.Si tu échoues : reprends le comptage asymptotique (on ne garde que le terme dominant, les constantes multiplicatives disparaissent) et les trois patrons essentiels — une boucle simple donne $O(n)$, deux boucles imbriquées donnent $O(n^2)$, une division par deux à chaque tour donne $O(\log n)$. La différence de comportement pratique entre $O(n^2)$ et $O(n \log n)$ est le fil conducteur du cours d'algorithmique de terminale.