Cadre réglementaire : Le langage utilisé en CPGE est Python 3. La rigueur formelle est exigée tant dans la conception des algorithmes que dans leur implémentation. Cette fiche dresse un panorama exhaustif des notions indispensables au programme de MPSI.
1. Modèle d’évaluation, Types et Structures de Données
Définition (Compréhension de liste) :
La syntaxe par compréhension permet de définir des listes selon un formalisme mathématique proche de la notation {F(x)∣x∈E∧P(x)}.
En Python, le passage des arguments s’effectue par assignation (souvent qualifié abusivement de pass-by-object-reference).
Si l’objet passé est immutable, toute modification locale à l’intérieur de la fonction n’affecte pas l’appelant.
Si l’objet est mutable, une modification in situ (ex: L.append(), L[0] = x) se répercute sur l’objet initial. En revanche, une réaffectation du paramètre (ex: L = []) n’a aucun impact hors de la fonction.
Méthode : Pour prouver la correction d’une boucle while, on doit identifier un invariant de boucleI vérifiant trois propriétés :
Initialisation :I est vrai avant la première itération.
Conservation : Si I est vrai et que la condition de boucle est vérifiée avant une itération, alors I est vrai après cette itération.
Terminaison : Si la boucle se termine, l’invariant couplé à la négation de la condition garantit la post-condition (exactitude partielle), et un variant de boucle assure la terminaison.
Un cas récursif où le problème est ramené à des instances plus petites, convergeant strictement vers le cas de base (mesuré par un variant de récursion).
Piège (Récursion excessive) : Python limite la profondeur de répile d’appels (par défaut 1000, consultable via sys.getrecursionlimit()). Une récursion non terminale trop profonde lève une exception RecursionError.
Recherche dichotomique dans un tableau trié : complexité O(ln(n)).
Tri par sélection / insertion / fusion / rapide : Maîtriser l’écriture itérative et récursive, ainsi que leurs complexités (Quadratique O(n2) au pire pour les trois premiers, O(nlnn) en moyenne pour le tri fusion et rapide).
Diviser pour régner : Décomposer un problème en sous-problèmes indépendants, résoudre récursivement, puis combiner (ex: Tri fusion).
Programmation dynamique : Résolution d’un problème par sous-structure optimale avec mémorisation (mémoïsation ascendante ou descendante) pour éviter les recalculs redondants (ex: calcul des nombres de Fibonacci, distance d’édition).
Effet de bord sur les valeurs par défaut mutables :
deff(x, L=[]): # MAUVAIS : la liste L est évaluée une seule fois à la définition
L.append(x)
return L
# Utiliser plutôt :
deff(x, L=None):
if L isNone:
L =[]
L.append(x)
return L
Copie superficielle (Shallow copy) vs Copie profonde (Deep copy) :
L2 = L1 : Copie de référence (alias).
L2 = list(L1) ou L2 = L1[:] ou L2 = copy.copy(L1) : Copie superficielle (les sous-objets imbriqués restent partagés).
copy.deepcopy(L1) : Copie récursive intégrale.
Modification d’un conteneur en cours d’itération :
Il ne faut jamais itérer directement sur un itérable mutable tout en le modifiant (ajout/suppression d’éléments). Préférer itérer sur une copie ou utiliser une construction par compréhension.