NSI 2026

Vue d'ensemble NSI
Analyse des sujets 2022–2025 · Fréquences · Ce qui peut tomber en 2026
4
Sujets analysés
~38
Questions/sujet
5h
Durée
12
Exercices
Fréquence des thèmes (2022–2025)
Structures de données
toutes années
Algorithmique + complexité
toutes années
SQL / bases de données
toutes années
Récursivité / backtracking
2022·2023·2025
Programmation dynamique
2024·2025
Graphes / BFS / DFS
2023·2024
Classes Python (POO)
2023·2024·2025
2022

Éditeur de texte (tableau, gap buffer, piece table). SQL sur terrain altitudes + simulation de gouttes d'eau. Algorithmique 2D : tracer/remplir une grille avec analyse de complexité fine.

2023

Polices CSS + graphe de compatibilité + clique maximale (backtracking). Moteur CSS (sélecteurs, arbre DOM, cascade). Hachage cryptographique et protocoles d'échange, arbres de Merkle.

2024

Œufs : programmation dynamique L(e,r), stratégie optimale. Voxels : faces, BFS surface externe de géode. Demi-arêtes : classes Python, composantes connexes, sérialisation SQL.

2025

Solveur SAT (FNC, simplification, backtracking récursif et itératif, listes de surveillance, clauses unitaires). Wagons (pile, gare de triage, LDS). SQL sur statistiques de formules SAT.

Structure invariante : Chaque sujet = 2–3 problèmes largement indépendants. Commencer par les Q.1–3 de chaque partie (points faciles garantis). Ne jamais rester bloqué sur une question difficile — passer à la suivante.
Listes & tuples
Structure séquentielle fondamentale · Mutable (liste) vs immuable (tuple) · Pièges courants

Qu'est-ce qu'une liste ?

Une liste Python est une séquence ordonnée et modifiable d'éléments pouvant être de types différents. En mémoire, Python stocke des références (pointeurs) vers les objets. C'est pourquoi b = a ne copie pas — les deux variables pointent vers le même objet.

Création — toutes les façons
# Littéral direct
a = [1, 2, 3]

# Répétition : crée n fois le même élément
b = [0] * 5          # → [0, 0, 0, 0, 0]

# range → list
c = list(range(1, 6))  # → [1, 2, 3, 4, 5]

# Compréhension : [expression for var in itérable if condition]
carres = [x**2 for x in range(6)]      # [0,1,4,9,16,25]
pairs  = [x for x in range(10) if x%2==0] # [0,2,4,6,8]

# Matrice 3×3 — TOUJOURS utiliser compréhension !
mat = [[0]*3 for _ in range(3)]  # ← CORRECT
# mat = [[0]*3] * 3            ← FAUX : 3 références au même tableau !
Piège matrice 2D : [[0]*3]*3 crée 3 références vers le même sous-tableau. Modifier mat[0][0] modifie aussi mat[1][0] et mat[2][0].

Accès et slicing

L'indexation accède à un élément en O(1). Les indices négatifs comptent depuis la fin. Le slicing t[début:fin:pas] extrait une sous-liste (copie superficielle).

t = [10, 20, 30, 40, 50]
#     0    1    2    3    4   ← positifs
#    -5   -4   -3   -2   -1  ← négatifs
t[0]      # → 10
t[-1]     # → 50  (dernier)
t[1:3]   # → [20, 30]  (1 inclus, 3 exclus)
t[::-1]  # → [50, 40, 30, 20, 10]  (inversé)
t[1:4:2] # → [20, 40]  (pas de 2)

Méthodes et complexités

append et pop() sont O(1) amorti. insert(0,x) et pop(0) sont O(n) car ils décalent tous les éléments — ne pas les utiliser pour faire une file !

a = [3, 1, 4, 1, 5]
a.append(9)      # O(1) — ajoute en fin
a.insert(2, 7)  # O(n) ! — insère à pos 2
a.pop()         # O(1) — retire le dernier
a.pop(0)        # O(n) ! — retire le premier
a.remove(1)    # O(n) — retire 1ère occurrence de 1
len(a)          # O(1)
1 in a          # O(n) — parcours linéaire
a.sort()            # O(n log n) — en place
b = sorted(a)       # O(n log n) — nouvelle liste
a.extend([10,20])  # O(k) — ajoute k éléments

Copie — le piège crucial au CGL

# ❌ MAUVAIS — b et a = même objet
a = [1, 2, 3]
b = a
b.append(4)
print(a)  # → [1, 2, 3, 4]  ← a a changé !

# ✅ Copie superficielle (liste 1D)
b = a[:]         # ou list(a) ou a.copy()

# ✅ Copie profonde (liste 2D)
import copy
m2 = copy.deepcopy(mat)

# ❌ NE PAS MODIFIER EN ITÉRANT — résultat imprévisible !
a = [1, 2, 3, 4]
for x in a:
    if x%2==0: a.remove(x)  # FAUX

# ✅ CORRECT — reconstruire une nouvelle liste
a = [x for x in a if x%2!=0]  # [1, 3]

Tuples

Un tuple est une liste immuable. Utilisable comme clé de dictionnaire. Le déballage (unpacking) permet de récupérer plusieurs valeurs renvoyées par une fonction.

t = (1, 2, 3)
t[0]   # → 1  (lecture OK)
# t[0] = 99  → TypeError !

# Déballage
x, y, z = (10, 20, 30)
a, *reste = (1, 2, 3, 4)  # a=1, reste=[2,3,4]

# Retourner plusieurs valeurs
def min_max(lst): return min(lst), max(lst)
mini, maxi = min_max([3,1,4,1,5])

# Clé de dictionnaire (impossible avec liste)
d = {(0,0): "origine"}  # ✓
Dictionnaires
Table de hachage clé→valeur · O(1) moyen · Incontournable au CGL

Principe

Un dictionnaire est une table de hachage : Python calcule un hash de la clé pour savoir où stocker la valeur. Cela donne accès, insertion et suppression en O(1) en moyenne. Les clés doivent être immuables (int, str, tuple). Depuis Python 3.7, l'ordre d'insertion est préservé.

Opérations de base
d = {"nom": "Alice", "age": 17}

# Accès sécurisé
d["nom"]                  # → "Alice"  | O(1)
d["inconnu"]              # → KeyError !
d.get("inconnu", "?")   # → "?"  (jamais d'erreur)

# Modification
d["score"] = 100   # ajoute si absent, modifie sinon
del d["age"]        # supprime
v = d.pop("nom")    # supprime et renvoie

# Appartenance — teste les CLÉS
"score" in d         # O(1)
100 in d.values()    # O(n) !

# Itération
for cle in d: ...
for cle, val in d.items(): ...
for val in d.values(): ...

Patterns courants au CGL

Compteur d'occurrences — pattern très fréquent
# Pattern classique
freq = {}
for c in "concours":
    freq[c] = freq.get(c, 0) + 1

# Ou avec collections.Counter
from collections import Counter
freq = Counter("concours")
freq.most_common(3)  # 3 plus fréquents

# Grouper des éléments
mots = ["chat", "chien", "dauphin"]
groupes = {}
for m in mots:
    groupes.setdefault(m[0], []).append(m)
# {'c': ['chat','chien'], 'd': ['dauphin']}
Mémoïsation (top-down DP)
cache = {}
def fib(n):
    if n in cache: return cache[n]  # déjà calculé → O(1)
    if n <= 1: return n
    cache[n] = fib(n-1) + fib(n-2)
    return cache[n]
# O(n) appels au lieu de O(2^n) sans cache

# Avec décorateur @lru_cache (plus simple)
from functools import lru_cache
@lru_cache(maxsize=None)
def fib2(n):
    if n <= 1: return n
    return fib2(n-1) + fib2(n-2)
POO & Classes
Attributs, méthodes, héritage · Crucial en 2023, 2024 et 2025

Anatomie d'une classe

La POO regroupe des données (attributs) et des comportements (méthodes) dans un objet. self est une référence vers l'instance courante — obligatoire en premier paramètre de toute méthode.

class Pile:
    """Pile LIFO — Last In, First Out."""
    def __init__(self):       # constructeur
        self._data = []        # attribut "privé" (convention _)
        self._n = 0

    def push(self, x):
        self._data.append(x); self._n += 1

    def pop(self):
        if self.est_vide():
            raise IndexError("Pile vide")
        self._n -= 1
        return self._data.pop()

    def peek(self):
        return self._data[-1] if not self.est_vide() else None

    def est_vide(self): return self._n == 0
    def __len__(self): return self._n       # permet len(p)
    def __repr__(self): return f"Pile({self._data})"

p = Pile()
p.push(10); p.push(20)
print(p.pop())  # → 20

Héritage

class Animal:
    def __init__(self, nom): self.nom = nom
    def parler(self): return "..."
    def __repr__(self): return f"{self.nom}: {self.parler()}"

class Chien(Animal):          # hérite de Animal
    def parler(self): return "Woof"  # surcharge

class Chat(Animal):
    def __init__(self, nom, couleur):
        super().__init__(nom)    # appel parent
        self.couleur = couleur
    def parler(self): return "Miaou"

print(Chien("Rex"))   # Rex: Woof
print(Chat("Mimi","roux"))  # Mimi: Miaou

Pattern CGL 2024 — dépendances cycliques (Demi-arêtes)

Sommet, Face et DArete se référencent mutuellement. On initialise à None et on complète ensuite. La structure de suivant forme une liste chaînée circulaire autour de chaque face.

class Sommet:
    def __init__(self, x, y, z):
        self.x, self.y, self.z = x, y, z
        self.darete = None   # rempli plus tard

class Face:
    def __init__(self): self.darete = None

class DArete:
    def __init__(self, source, but, face):
        self.source = source; self.but = but; self.face = face
        self.suivant = None   # prochaine DA autour de face
        self.miroir  = None   # DA opposée (sens inverse)
        if face.darete is None: face.darete = self
        if source.darete is None: source.darete = self

# Tour d'une face = liste chaînée circulaire
def tour_face(face):
    res = []; da = face.darete
    while True:
        res.append(da.source); da = da.suivant
        if da is face.darete: break
    return res
Récursivité
Cas de base, variant, terminaison · Preuves formelles exigées au CGL

Le principe

Une fonction récursive décompose le problème en sous-problèmes plus petits du même type jusqu'à atteindre un cas trivial. Trois conditions nécessaires :

  • Cas de base : au moins un cas résolu sans appel récursif.
  • Réduction : chaque appel récursif traite un problème strictement plus petit.
  • Terminaison : le problème atteint forcément le cas de base (variant).
Exemples avec traces et variants
# Factorielle — variant : n (décroît vers 0)
def fact(n):
    if n == 0: return 1        # cas de base
    return n * fact(n - 1)    # n décroît strictement
# fact(4) = 4×fact(3) = 4×3×2×1×1 = 24

# Dichotomie récursive — variant : haut-bas
def dicho(t, x, bas=0, haut=None):
    if haut is None: haut = len(t) - 1
    if bas > haut: return -1      # absent
    mid = (bas + haut) // 2
    if t[mid] == x: return mid
    if t[mid] < x: return dicho(t, x, mid+1, haut)
    return dicho(t, x, bas, mid-1)
    # Variant: haut-bas décroît strictement à chaque appel

# Hanoi — 2^n - 1 déplacements
def hanoi(n, A, B, C):
    if n == 0: return
    hanoi(n-1, A, C, B)
    print(f"Disque {n}: {A} → {C}")
    hanoi(n-1, B, A, C)
Prouver la terminaison (exigé CGL) : Nommer le variant, montrer qu'il est ≥ 0 et décroît strictement. Ex: "Le variant haut - bas est un entier ≥ 0. Si cassé : haut = mid < haut_avant → décroît. Si intact : bas = mid+1 > bas_avant → décroît. Donc la boucle se termine."
Python avancé
Fonctions méconnues · Générateurs · Logique 3 valeurs · Décorateurs
enumerate, zip, any, all — indispensables
# enumerate : indice + valeur en même temps
for i, v in enumerate(["a","b","c"], start=1):
    print(i, v)  # 1 a, 2 b, 3 c

# zip : plusieurs listes en parallèle
for n, s in zip(["Alice","Bob"], [18,14]):
    print(f"{n}: {s}/20")

# any / all : conditions globales
t = [2, 4, 6, 8]
all(x%2==0 for x in t)  # → True
any(x > 5 for x in t)   # → True

# sorted avec clé personnalisée
mots = ["banane", "kiwi", "ananas"]
sorted(mots, key=len)           # par longueur
sorted(mots, key=lambda m: m[-1]) # par dernière lettre

# next() avec valeur par défaut — très utile CGL 2025
u = next((cl[0] for cl in F if len(cl)==1), None)
# Renvoie le 1er élément vérifiant la condition, ou None
Générateurs — lazy evaluation
def fib_gen():
    a, b = 0, 1
    while True:
        yield a        # suspend et retourne
        a, b = b, a+b

gen = fib_gen()
print(next(gen), next(gen), next(gen))  # 0 1 1

# Expression génératrice (lazy, économise mémoire)
total = sum(x**2 for x in range(1000000))
Logique 3 valeurs — CGL 2025 (solveur SAT)
# None = indéterminé. Nécessite une logique spéciale.
def val_clause(clause, V):
    res = False
    for lit in clause:
        x = abs(lit)
        v = V[x]
        if v is True  and lit > 0: return True
        if v is False and lit < 0: return True
        if v is None: res = None   # indéterminé
    return res  # False ou None

# Table: None∨True=True, None∨False=None
#        None∧False=False, None∧True=None
Structures de données
Pile, file, gap buffer, demi-arêtes, voxels

La pile suit le principe LIFO (Last In, First Out). En Python : list.append() + list.pop(), tous deux O(1).

# Vérifier parenthèses équilibrées
def equilibre(expr):
    pile = []; paires = {")":"(","}":"{","]":"["}
    for c in expr:
        if c in "({[": pile.append(c)
        elif c in ")]}":
            if not pile or pile[-1]!=paires[c]: return False
            pile.pop()
    return not pile

print(equilibre("({[]})"))  # True
print(equilibre("({])"))    # False

La file suit le principe FIFO (First In, First Out). Utiliser collections.deque — jamais list.pop(0) qui est O(n) !

from collections import deque
file = deque()
file.append("A")    # O(1)
x = file.popleft()  # O(1)

# BFS — parcours en largeur
def bfs(g, src):
    dist = {src: 0}; file = deque([src])
    while file:
        s = file.popleft()
        for v in g.get(s,[]):
            if v not in dist:
                dist[v] = dist[s]+1; file.append(v)
    return dist
Tableau simple
Insertion en O(n) — décale tout. Cas de base CGL 2022.
Gap buffer
Trou au curseur. Insertion O(1), déplacement O(n).
Piece table
Données immuables + liste de morceaux. Insertion O(1) sans copie.
class GapBuffer:
    def __init__(self, cap=200):
        self.buf=[None]*cap; self.g=0; self.d=cap; self.cap=cap
    def inserer(self, c):
        self.buf[self.g]=c; self.g+=1  # O(1) !
    def avancer(self):
        if self.d1; self.d+=1

Structure CGL 2024. Chaque arête → 2 demi-arêtes orientées. Attributs : source, but, face, suivant, miroir. La chaîne suivant forme une liste chaînée circulaire autour de chaque face.

# Depuis a : a.suivant.miroir → c
def faces_voisines(face):
    res=[]; da=face.darete
    while True:
        voisine=da.miroir.face
        if voisine is not face: res.append(voisine)
        da=da.suivant
        if da is face.darete: break
    return res

Un voxel (x,y,z) est un cube de côté 1 repéré par son coin minimal. Il a 6 faces et 26 voisins (toutes les combinaisons de décalages -1,0,+1 sauf (0,0,0)).

def voisins_voxel(x, y, z):
    return [(x+dx,y+dy,z+dz)
            for dx in [-1,0,1]
            for dy in [-1,0,1]
            for dz in [-1,0,1]
            if (dx,dy,dz)!=(0,0,0)]  # 26 voisins
Complexité algorithmique
Notation O · Analyse · Preuves de terminaison

La complexité mesure comment le temps d'exécution croît en fonction de la taille de l'entrée. On note O(f(n)) en ignorant les constantes.

NotationNomExemplesn=10⁶
O(1)ConstantAccès liste, dict lookup, push/pop~1 ns
O(log n)LogarithmiqueDichotomie, BST équilibré~20 ops
O(n)LinéaireParcours, BFS/DFS, recherche linéaire~1 ms
O(n log n)Quasi-linéaireTri fusion, tri rapide~20 ms
O(n²)QuadratiqueTri bulle, double boucle~17 min
O(2ⁿ)ExponentielBacktracking naïf, tous les sous-ensembles
Important CGL : le "coût en temps" est défini dans l'énoncé comme "nombre de lectures/écritures dans un tableau". Adapter la réponse à cette définition précise.
Tris & recherche
Tri insertion O(n²) · Tri fusion O(n log n) · Dichotomie O(log n)
Tri par insertion — O(n²) pire cas, O(n) si presque trié
def tri_insertion(t):
    for i in range(1, len(t)):
        cle = t[i]; j = i-1
        while j >= 0 and t[j] > cle:
            t[j+1] = t[j]; j -= 1
        t[j+1] = cle
    return t
Tri fusion — O(n log n) garanti
def fusionner(g, d):
    res=[]; i=j=0
    while i<len(g) and j<len(d):
        if g[i]<=d[j]: res.append(g[i]); i+=1
        else: res.append(d[j]); j+=1
    return res+g[i:]+d[j:]

def tri_fusion(t):
    if len(t)<=1: return t
    m=len(t)//2
    return fusionner(tri_fusion(t[:m]),tri_fusion(t[m:]))
Dichotomie itérative — O(log n)
def dicho(t, x):
    bas, haut = 0, len(t)-1
    while bas <= haut:
        mid = (bas+haut)//2
        if   t[mid]==x: return mid
        elif t[mid]1
        else:           haut = mid-1
    return -1
# Variant: haut-bas ≥ 0, décroît strictement → terminaison
Programmation dynamique
Mémoïsation · Tableaux DP · Récurrences · CGL 2024 et 2025

La prog. dynamique décompose un problème en sous-problèmes chevauchants et mémorise leurs solutions. 4 étapes : identifier les sous-problèmes, écrire la récurrence, les cas de base, puis calculer dans le bon ordre.

# Bottom-up (tableau)
def fib_dp(n):
    dp = [0]*(n+1); dp[1] = 1
    for i in range(2,n+1): dp[i]=dp[i-1]+dp[i-2]
    return dp[n]

# Top-down (mémoïsation)
def fib_m(n, m={}):
    if n in m: return m[n]
    if n<=1: return n
    m[n]=fib_m(n-1)+fib_m(n-2); return m[n]
L(e, r) = min₁≤ᵢ≤ₑ { max(L(i-1, r-1), L(e-i, r)) } + 1

L(e,r) = nb minimal de lâchers pour e étages, r œufs. L(0,r)=0, L(e,1)=e. Lâcher depuis étage i : si cassé → L(i-1,r-1) ; si intact → L(e-i,r). Pire cas = max des deux.

Inf=float('inf')
def calcul_L(n,k):
    dp=[[0]*(k+1) for _ in range(n+1)]
    prec=[[1]*(k+1) for _ in range(n+1)]
    for e in range(1,n+1): dp[e][1]=e
    for e in range(1,n+1):
        for r in range(2,k+1):
            best,bi=Inf,1
            for i in range(1,e+1):
                c=max(dp[i-1][r-1],dp[e-i][r])+1
                if creturn dp,prec
LDS[i] = 1 + max{ LDS[j] | j < i et T[j] > T[i] }

Théorème de Dilworth (2025) : nb minimal de voies de triage = longueur de la LDS du train.

def lds(T):
    n=len(T); D=[1]*n; par=[-1]*n
    for i in range(1,n):
        for j in range(i):
            if T[j]>T[i] and D[j]+1>D[i]:
                D[i]=D[j]+1; par[i]=j
    idx=D.index(max(D)); ss=[]
    while idx!=-1: ss.append(T[idx]); idx=par[idx]
    ss.reverse()
    return max(D), ss
dp[i][w] = max(dp[i-1][w], dp[i-1][w-p[i]] + v[i]) si p[i]≤w
def sac(poids, valeurs, W):
    n=len(poids)
    dp=[[0]*(W+1) for _ in range(n+1)]
    for i in range(1,n+1):
        for w in range(W+1):
            dp[i][w]=dp[i-1][w]
            if poids[i-1]<=w:
                dp[i][w]=max(dp[i][w],dp[i-1][w-poids[i-1]]+valeurs[i-1])
    return dp[n][W]
dp[i][j] = min(dp[i-1][j]+1, dp[i][j-1]+1, dp[i-1][j-1] + (0 si a[i]=b[j] sinon 1))
def levenshtein(a, b):
    n,m=len(a),len(b)
    dp=[[0]*(m+1) for _ in range(n+1)]
    for i in range(n+1): dp[i][0]=i
    for j in range(m+1): dp[0][j]=j
    for i in range(1,n+1):
        for j in range(1,m+1):
            sub=0 if a[i-1]==b[j-1] else 1
            dp[i][j]=min(dp[i-1][j]+1,dp[i][j-1]+1,dp[i-1][j-1]+sub)
    return dp[n][m]
print(levenshtein("kitten","sitting"))  # → 3
Graphes
BFS, DFS, composantes connexes, Dijkstra, clique maximale

Un graphe = ensemble de sommets reliés par des arêtes. Représentation standard en Python : liste d'adjacence g[i] = voisins du sommet i.

BFS et DFS complets
from collections import deque
import heapq

def bfs(g, src):
    dist={src:0}; q=deque([src])
    while q:
        s=q.popleft()
        for v in g.get(s,[]):
            if v not in dist: dist[v]=dist[s]+1; q.append(v)
    return dist

def dfs(g, src, vus=None):
    if vus is None: vus=set()
    vus.add(src)
    for v in g.get(src,[]): 
        if v not in vus: dfs(g,v,vus)
    return vus

# Dijkstra — plus court chemin pondéré
def dijkstra(g, src):
    dist={src:0}; tas=[(0,src)]
    while tas:
        d,s=heapq.heappop(tas)
        if d>dist.get(s,float('inf')): continue
        for v,w in g.get(s,[]):
            nd=d+w
            if ndget(v,float('inf')):
                dist[v]=nd; heapq.heappush(tas,(nd,v))
    return dist
Backtracking
DFS de l'arbre des solutions · Élagage · SAT 2025 · N-Reines

Le backtracking explore l'espace des solutions par DFS. À chaque nœud : choisir → explorer → défaire. L'élagage (pruning) détecte les branches sans solution le plus tôt possible.

N-Reines + Solveur SAT
def n_reines(n):
    solutions=[]
    def placer(ligne, cols):
        if ligne==n: solutions.append(cols[:]); return
        for col in range(n):
            ok=all(c!=col and abs(c-col)!=abs(r-ligne)
                     for r,c in enumerate(cols))
            if ok:
                cols.append(col)       # choisir
                placer(ligne+1, cols)  # explorer
                cols.pop()             # défaire
    placer(0, []); return len(solutions)
print(n_reines(8))  # → 92

# Solveur SAT avec clauses unitaires (CGL 2025)
def simplifie(F,lit):
    return [[l for l in cl if l!=-lit] for cl in F if lit not in cl]
def sat(F,sol=[]):
    if F==[]: return sol
    if [] in F: return None
    lit=F[0][0]
    r=sat(simplifie(F,lit),sol+[lit])
    return r if r else sat(simplifie(F,-lit),sol+[-lit])
SQL complet
SELECT, JOIN, agrégats · Sans GROUP BY · Patterns CGL · Exemples des 4 sujets
Interdit au CGL : GROUP BY, HAVING, ORDER BY, LIMIT, EXISTS. Remplacer par self-joins + LEFT JOIN IS NULL + COUNT(DISTINCT) + tables temporaires.

Le SQL interroge des bases de données relationnelles. Une table = des lignes (enregistrements) avec des colonnes (attributs).

SELECT x, altitude FROM T WHERE altitude > 0;
SELECT COUNT(*), SUM(altitude), MAX(altitude) FROM T;
SELECT COUNT(DISTINCT ABS(num_litteral)) FROM litteral;
INSERT INTO tmp SELECT x, altitude FROM T WHERE altitude > 0;
UPDATE T SET altitude = 0 WHERE altitude < 0;

JOIN croise les lignes selon la condition ON. LEFT JOIN conserve toutes les lignes de gauche (colonnes droites = NULL si pas de correspondance).

-- Self-join sur positions consécutives
SELECT A.x, A.altitude, B.altitude
FROM T AS A JOIN T AS B ON B.x = A.x + 1;

-- LEFT JOIN + IS NULL = trouver le maximum
-- (aucun B n'a altitude > A ↔ A est le max)
SELECT A.x FROM T AS A
LEFT JOIN T AS B ON B.altitude > A.altitude
WHERE B.x IS NULL;
Sommet local (max local)
SELECT COUNT(*) FROM T AS C
JOIN T AS G ON G.x = C.x - 1
JOIN T AS D ON D.x = C.x + 1
WHERE C.altitude > G.altitude AND C.altitude > D.altitude;
-- 2022 Q17 : Somme altitudes positives
SELECT SUM(altitude) FROM T WHERE altitude > 0;

-- 2022 Q18 : Max dénivelé entre consécutifs > 0
SELECT MAX(ABS(A.altitude-B.altitude))
FROM T A JOIN T B ON B.x=A.x+1
WHERE A.altitude>0 AND B.altitude>0;

-- 2025 Q17 : Nb clauses par formule
SELECT id_formule, COUNT(DISTINCT num_clause) AS nb
FROM litteral;

-- 2025 Q18 : Nb variables par formule
SELECT id_formule, COUNT(DISTINCT ABS(num_litteral)) AS nb
FROM litteral;
Solveur SAT — CGL 2025
FNC · Simplification · Clauses unitaires · Backtracking itératif
Littéral
Variable +k ou sa négation -k. Entier non nul en Python.
Clause
Disjonction (∨) de littéraux. [1,-2,3] = (x₁ ∨ ¬x₂ ∨ x₃). Vraie si ≥1 littéral vrai.
FNC
Conjonction (∧) de clauses. Vraie si toutes les clauses sont vraies.
Clause unitaire
Une seule clause → ce littéral est forcément vrai !
def simplifie(F, lit):
    return [[l for l in cl if l!=-lit] for cl in F if lit not in cl]

def clauses_unit(F):
    forcés=[]; vus=set()
    while True:
        u=next((cl[0] for cl in F if len(cl)==1 and cl[0] not in vus),None)
        if u is None: break
        forcés.append(u); vus.add(u); F=simplifie(F,u)
    return F, forcés

def sat(F, sol=[]):
    F,f=clauses_unit(F); sol=sol+f
    if F==[]: return sol
    if [] in F: return None
    lit=F[0][0]
    r=sat(simplifie(F,lit),sol+[lit])
    return r if r is not None else sat(simplifie(F,-lit),sol+[-lit])

print(sat([[1,2],[-1,3],[-2,-3]]))  # [1, 3, -2]
print(sat([[1],[-1]]))                # None (insatisfaisable)
Problème des œufs — CGL 2024
Programmation dynamique · Dichotomie · Stratégie optimale
L(e, r) = min₁≤ᵢ≤ₑ { max(L(i-1, r-1), L(e-i, r)) } + 1 — avec L(0,r)=0 et L(e,1)=e
Décalage d'indice (piège CGL) : le tableau DP raisonne sur des "étages restants" (1..e), pas sur la numérotation réelle. Si niv_bas ≠ 1, ajouter niv_bas − 1 à l'étage optimal trouvé.
def dichotomie(n):
    """Œufs illimités — O(log n) lâchers. Invariant: critique ∈ [bas,haut]"""
    bas, haut = 1, n
    while bas < haut:
        mid=(bas+haut)//2
        if lacher(mid): haut=mid   # critique ≤ mid
        else: bas=mid+1            # critique > mid
    return bas
Wagons — CGL 2025
Pile, gare de triage, LDS, théorème de Dilworth
Voie de stockage
Pile LIFO. Triable ⟺ pas de pattern 120.
Gare de triage
m files FIFO parallèles. Triable ⟺ LDS ≤ m (Dilworth).
from collections import deque

def gare_triage(m, entree):
    voies=[deque() for _ in range(m)]
    sortie=deque(); prochain=0
    for wagon in entree:
        placé=False
        for v in voies:
            if not v or v[-1]>wagon:
                v.append(wagon); placé=True; break
        if not placé: return False,[]
    n=sum(len(v) for v in voies)
    for _ in range(n):
        for v in voies:
            if v and v[0]==prochain:
                sortie.append(v.popleft()); prochain+=1; break
    return True, list(sortie)

ok, res=gare_triage(3,[3,1,4,0,2])
print(ok, res)  # True [0,1,2,3,4]
Hachage cryptographique — CGL 2023
Fonctions de hachage · Protocoles d'échange · Arbres de Merkle
Propriétés
Entrée quelconque → 64 octets fixes. Déterministe. Résistant aux collisions. Effet avalanche (1 bit → ~50% changent).
Q.22 Égalité
A hache son fichier (512 bits) → B compare. Total = 512 bits.
Q.25 1er différent
Dichotomie sur le fichier : O(log n) échanges × 512 bits.
Q.31 Merkle
Arbre binaire de hachés. Prouver 1 bloc : O(log n) hachés.
Arbre de Merkle : h(nœud) = hash(h(gauche) || h(droite))
Éditeur Python
Python dans le navigateur via Pyodide · Ctrl+Entrée pour exécuter
Chargement de Python… (~5s la première fois, puis instantané)
Python · Ctrl+Entrée pour exécuter
Sortie
Clique sur ▶ Exécuter pour voir le résultat.
Modules disponibles : math, collections, functools, itertools, heapq, copy, random, etc. Modules système (os, sys.argv) non disponibles.
Exercices
12 exercices générés dynamiquement · Cliquer pour ouvrir · Régénérer pour changer les valeurs
Quiz
10 questions tirées aléatoirement parmi 18 · Score affiché à la fin
Générateur de projets
Sélectionne les thèmes, la durée, et génère des idées de projets à coder

Thèmes à travailler

1–2 heures
3
Méthode & conseils
Stratégie pour les 5h · Erreurs fréquentes · Ce qui est valorisé
Stratégie des 5 heures
0–15 min
Lecture diagonale — Parcourir tout. Identifier les parties indépendantes, noter les Q.1–3 faciles.
15–60 min
Points faciles — Traiter toutes les premières questions de chaque partie. Socle garanti.
60–240 min
Approfondissement — Traiter par ordre de maîtrise décroissante. Ne pas rester bloqué.
240–300 min
Finition — Cas limites, descriptions en français, preuves de terminaison.
Erreurs fréquentes à éviter
Modifier une liste pendant l'itération — Toujours reconstruire avec une compréhension.
GROUP BY en SQL — Interdit ! LEFT JOIN IS NULL pour les maxima.
Décalage d'indice en prog. dyn. — DP raisonne sur "étages restants", ajouter niv_bas-1.
Oublier la preuve de terminaison — Nommer le variant, montrer qu'il décroît strictement.
b = a ne copie pas ! — Utiliser b = a[:] ou copy.deepcopy(a).
Ce qui est valorisé
SQL en une seule requête — Bonification explicite. 2–3 requêtes correctes donnent la majorité des points.
Complexité optimale — Mentionnée dans l'énoncé. Solution moins efficace mais correcte = points partiels.
Description en français précise — Peut valoir tous les points quand l'implémentation n'est pas demandée.