Chapitre 24
24 — Tendances
> **Objectif** : Panorama des algorithmes modernes — IA/ML, algorithmes quantiques, algorithmes distribués, streaming (Bloom filter, HyperLogLog), algorithmes GPU et sublinear algorithms. ---
24 — Tendances : les algorithmes du futur
Ce chapitre ouvre la formation vers les domaines en pleine expansion : l'intelligence artificielle, le calcul quantique, les systèmes distribués, le traitement de flux de données, le calcul sur GPU et les algorithmes sublinéaires. Pour chaque domaine : le problème, les algorithmes clés, les complexités, et du code d'illustration.
Sommaire
- Pourquoi ce chapitre ?
- Algorithmes en IA/ML
- Algorithmes quantiques
- Algorithmes distribués
- Streaming algorithms
- Algorithmes GPU
- Sublinear algorithms
- Synthèse et perspectives
1. Pourquoi ce chapitre ?
Les 23 premiers chapitres couvrent le cœur classique de l'algorithmique — les fondations que tout ingénieur doit maîtriser. Ce chapitre répond à la question « et maintenant ? » : l'algorithmique vit une révolution tranquille portée par quatre forces :
- L'explosion des données — impossible de tout lire ni tout stocker → streaming et sublinear.
- Le parallélisme — les cœurs ne vont plus plus vite, ils se multiplient → GPU et distribué.
- L'apprentissage automatique — les modèles sont entraînés par des algorithmes d'optimisation.
- L'informatique quantique — un nouveau modèle de calcul qui change les complexités.
Un bon ingénieur algorithmique ne connaît pas seulement O, Θ, la DP et les graphes — il sait choisir le bon modèle de calcul pour le problème et les données du moment.
2. Algorithmes en IA/ML
Le problème
Apprendre une fonction f : X → Y à partir d'exemples étiquetés (supervisé) ou de structures dans les données (non supervisé). L'algorithmique y intervient à tous les étages : optimisation, matrice, graphe, tri.
Les algorithmes fondateurs
Descente de gradient (gradient descent)
On minimise une fonction de coût L(θ) en se déplaçant dans le sens opposé au gradient, pas à pas. C'est l'algorithme le plus important de l'IA moderne.
def gradient_descent(loss_grad, theta, lr=0.01, iters=1000):
for _ in range(iters):
theta -= lr * loss_grad(theta)
return theta
Complexité : O(I × coût du gradient) avec I itérations. Les variantes — SGD (stochastic gradient descent), mini-batch, Adam — améliorent la vitesse et la stabilité.
Backpropagation
Pour un réseau de neurones, la rétropropagation calcule le gradient de la perte par rapport à chaque poids via la règle de la chaîne, en parcourant le réseau en sens inverse. C'est une application directe de la DP/des graphes (calcul des gradients en O(nb poids), une passe avant + une passe arrière).
Algorithmes d'apprentissage classiques
| Algorithme | Principe | Complexité d'entraînement |
|---|---|---|
| KNN | Prédire par majorité des k plus proches voisins (kd-tree ou ball tree pour accélérer) | O(n·d) par requête |
| Naive Bayes | Probabilités conditionnelles indépendantes | O(n·d) |
| Arbre de décision (ID3/CART) | Partitionnement récursif par gain d'information | O(n·d·hauteur) |
| Perceptron | Séparation linéaire par corrections itératives | O(n·d) |
| SVM | Marge maximale, résolu par optimisation quadratique | O(n²·d) à O(n³) |
| K-means | Clustering par centroïdes, itératif (LLoyd) | O(n·k·I) |
| DBSCAN | Clustering par densité, voisinage ε | O(n²) pire cas |
| Random Forest | Ensemble d'arbres + bagging | O(t·n·d·h) |
| Boosting (AdaBoost/XGBoost) | Combinaison pondérée de faibles apprenants | O(t·n·d) |
Le fond algorithmique caché
- Multiplication matricielle : les réseaux de neurones sont des GEMM géants → Strassen (O(n^2.807)) et cuBLAS sur GPU.
- Recherche de voisins : kd-trees, ball trees, hashing local sensible (LSH) pour les grandes bases.
- Graphes : PageRank (itération de Markov), propagation de labels, traversals sur grands graphes.
- Tri et statistiques : sélection des top-k, partitionnement des données d'entraînement.
Exemple : K-means en Python
import random
def kmeans(points, k, iters=100):
centroids = random.sample(points, k)
for _ in range(iters):
clusters = [[] for _ in range(k)]
for p in points:
idx = min(range(k), key=lambda i: dist(p, centroids[i]))
clusters[idx].append(p)
new_centroids = [mean(c) for c in clusters if c]
if new_centroids == centroids:
break
centroids = new_centroids
return centroids, clusters
3. Algorithmes quantiques
Les idées physiques
L'informatique quantique manipule des qubits qui, contrairement aux bits, peuvent être en superposition (α|0⟩ + β|1⟩). Les algorithmes tirent parti de deux phénomènes :
- Intrication : des qubits corrélés agissent ensemble.
- Interférence : les amplitudes peuvent s'additionner (bonnes réponses) ou s'annuler (mauvaises réponses).
Un registre de n qubits encode 2^n amplitudes simultanément — d'où un parallélisme exponentiel en théorie. Le défi : lire le résultat sans détruire la superposition (la mesure est probabiliste).
Les algorithmes majeurs
Grover (recherche non structurée)
Trouve un élément dans une base non triée de taille N en O(√N) requêtes, contre O(N) classique. Application : recherche, inversion de fonctions, SAT par recherche.
Shor (factorisation)
Factorise un entier n en temps polynomial — O((log n)³). Or la sécurité du RSA repose sur l'hypothèse que la factorisation est difficile en classique. Shor ne l'a pas « brisé » pratiquement (besoin de millions de qubits logiques), mais il a reconfiguré la recherche en cryptographie post-quantique.
Autres familles
| Famille | Exemples | Usage |
|---|---|---|
| Recherche | Grover, amplitude amplification | Recherche, optimisation |
| Transformée | QFT (transformation de Fourier quantique) | Périodes, Shor |
| Simulation | Trotter, QAOA, VQE | Chimie, matériaux, optimisation |
| Optimization | Quantum annealing (D-Wave) | QA, Ising, portefeuille |
Quelle complexité ?
- BQP (bounded-error quantum polynomial time) : problèmes résolubles en temps polynomial avec probabilité d'erreur bornée. C'est la classe quantique « pratique ».
- La recherche quantique donne un gain quadratique (√N), pas exponentiel — la menace la plus immédiate est sur le hachage (√2^n au lieu de 2^n).
État des lieux (2026)
Les machines actuelles comptent 1000+ qubits physiques mais restent bruyantes (NISQ). Les algorithmes pratiques pour le futur proche : QAOA/VQE (optimisation et chimie), et des schémas de réduction d'erreur. L'algèbre modulaire, les lattices (crypto post-quantique) et le problème du logarithme discret sont les cibles de recherche majeures.
4. Algorithmes distribués
Le problème
Exécuter un algorithme sur plusieurs machines communiquant par le réseau. Deux enjeux : la performance (décomposer le travail) et la cohérence (garder des vues cohérentes malgré pannes).
Théorème CAP
Parmi Consistance, Availability et Partition tolerance, un système distribué ne peut garantir que deux sur trois. En pratique, les partitions sont inévitables → on choisit entre cohérence forte (CP) et disponibilité (AP).
Consensus
Plusieurs nœuds doivent se mettre d'accord malgré les pannes.
- Paxos (Lamport) : première famille d'algorithmes de consensus, difficile à implémenter correctement.
- Raft (Diego Ongaro) : consensus rédigé pour l'enseignement et l'implémentation — élection de leader + réplication de journal. Utilisé par etcd, Consul, CockroachDB.
- PBFT (Practical Byzantine Fault Tolerance) : tolère les pannes byzantines (comportements arbitraires) : n ≥ 3f+1 nœuds pour tolérer f pannes.
Modèles de calcul
| Modèle | Idée | Exemples |
|---|---|---|
| MapReduce | Map (parallèle) puis Reduce (agrégation), orchestré par un master | Hadoop, Spark RDD |
| BSP (bulk synchronous parallel) | Super-pas : calcul local + communication + barrière | Pregel (graphes), Giraph |
| Actor model | Acteurs asynchrones communicant par messages | Akka, Erlang |
| Streaming distribué | Opérateurs en pipeline, fenêtres | Kafka Streams, Flink, Spark Streaming |
MapReduce en miniature
from collections import defaultdict
def map_reduce(mapper, reducer, data):
intermediate = defaultdict(list)
for item in data:
for k, v in mapper(item):
intermediate[k].append(v)
result = {}
for k, values in intermediate.items():
result[k] = reducer(k, values)
return result
# Word count
mapper = lambda line: ((w, 1) for w in line.split())
reducer = lambda k, vs: sum(vs)
Défis algorithmiques distribués
- Tri distribué : external merge sort, TeraSort, sortie en partitions ordonnées.
- Comptage / top-k distribué : approximations par échantillonnage et sketches locaux.
- Graphes massifs : Pregel (BSP), éventail de parcours BFS/PageRank avec agrégation des messages.
- File d'attente et back-pressure : équilibrage de charge, watermarks.
5. Streaming algorithms
Le problème
Un flux infini (clics, logs, capteurs) arrive en un seul passage ; la mémoire disponible est très inférieure à la taille du flux. On ne peut pas stocker, et on ne passe qu'une fois (ou peu) sur chaque élément. On cherche des approximations avec des garanties probabilistes.
Bloom filter
Teste l'appartenance d'un élément à un ensemble avec 0 faux négatif et un petit taux de faux positifs. Principe : k fonctions de hachage mappent vers un bitmap de m bits ; à l'insertion on met les k bits à 1 ; au test, on renvoie vrai si tous les k bits sont à 1.
class BloomFilter:
def __init__(self, m, k):
self.m, self.k = m, k
self.bits = bytearray(m)
def _hashes(self, x):
# simule k hashages indépendants
return [(hash((x, i)) % self.m) for i in range(self.k)]
def add(self, x):
for h in self._hashes(x):
self.bits[h] = 1
def __contains__(self, x):
return all(self.bits[h] for h in self._hashes(x))
Taux de faux positifs optimal : k = (m/n)·ln 2 → probabilité ≈ (1 − e^(−kn/m))^k.
HyperLogLog (HLL)
Estime le nombre d'éléments distincts (cardinalité) d'un flux en O(1) mémoire (typiquement quelques Ko, précision ~1-2 %). Principe : observer le nombre maximal de zéros de tête dans les hashs des éléments — plus la cardinalité est grande, plus on a de chances d'avoir une longue suite de zéros. Combiné avec le stochastic averaging (m registres) et une correction de biais.
Count-Min Sketch (CMS)
Estime la fréquence d'un élément dans un flux (réponse ≥ vraie fréquence, avec erreur additive εN). Principe : une grille de compteurs, d hachages ; à chaque occurrence, incrémenter les d compteurs ; la réponse est le minimum des d compteurs.
Stream Summary & Sampling
- Reservoir sampling : échantillon aléatoire uniforme de taille k sans connaître n (algorithme de Vitter).
- Misra-Gries / Space-Saving : top-k fréquents (heavy hitters) avec peu de compteurs.
- Exact distinct / approximate distinct : HLL vs algorithmes exacts (impossibles en mémoire bornée).
Le compromis central
| Problème | Exact (mémoire) | Approximé (mémoire) | Erreur |
|---|---|---|---|
| Appartenance | O(n) | O(m) bits (Bloom) | faux positifs ~1 % |
| Cardinallité | O(n) | O(log log n) (HLL) | ~1-2 % |
| Fréquence | O(n) | O(1/ε) (CMS) | ε·N additive |
| Top-k | O(n) | O(k) | sur liste de candidats |
Code : reservoir sampling
import random
def reservoir_sample(stream, k):
reservoir = []
for i, item in enumerate(stream):
if i < k:
reservoir.append(item)
else:
j = random.randint(0, i)
if j < k:
reservoir[j] = item
return reservoir
6. Algorithmes GPU
Le modèle de calcul
Le GPU exécute des milliers de threads en parallèle sur le même programme (SIMT). L'efficacité repose sur :
- Taux d'occupation : suffisamment de threads pour masquer la latence mémoire.
- Cohérence de divergence : les threads d'un même warp doivent suivre le même chemin de contrôle.
- Localité mémoire : accès coalescés (contigus) pour exploiter les lignes de cache.
Les algorithmes GPU fondamentaux
Réduction (sum/min/max)
Arbre binaire de réduction par niveaux : à chaque étape, chaque thread combine deux valeurs.
def gpu_reduce_style(arr):
# version "séquentielle" du pattern de réduction par paires
n = len(arr)
while n > 1:
half = (n + 1) // 2
for i in range(half):
if i + half < n:
arr[i] += arr[i + half]
n = half
return arr[0]
Complexité : O(log n) « pas » de réduction avec n/2 threads par niveau (en pratique O(n) travail total, O(n/p) temps).
Scan (prefix sum) — Blelloch
Calculer les sommes de préfixes en deux passes : up-sweep (réduire) puis down-sweep (distribuer). Le scan parallèle est la brique des algorithmes de stream compaction, de tri par comptage parallèle et de many-to-many mapping.
Multiplication de matrices (GEMM)
Découpage en tuiles (tiling) : chaque bloc charge une tuile en mémoire partagée, puis accumule les produits. C'est l'opération centrale de l'apprentissage profond (cublas, cuDNN).
Tri parallèle
- Bitonic sort : O(n log² n) comparateurs, réseau fixe — adapté au GPU.
- Radix sort : parallélisable naturellement (counting sort par digits) — utilisé en pratique.
- Sample sort : pivot approximatif par échantillonnage, partitionnement en seaux.
Grille de référence
| Algorithme | Complexité temps (p cœurs) | Travail total |
|---|---|---|
| Réduction | O(n/p + log p) | O(n) |
| Scan (Blelloch) | O(n/p + log p) | O(n) |
| GEMM (tuilé) | O(n³/p) | O(n³) |
| Bitonic sort | O((n log² n)/p) | O(n log² n) |
| Radix sort | O((n·d)/p) | O(n·d) |
7. Sublinear algorithms
Le problème
Quand l'entrée est immense (exabytes), même O(n) est trop coûteux. Les algorithmes sublinéaires donnent une réponse approximative en ne lisant qu'une petite partie de l'entrée — souvent O(poly(log n)) ou O(√n) temps/mémoire. On distingue :
- Temps sublinéaire : algorithmes à échantillonnage et accès aléatoire (propriétés, métriques).
- Espace sublinéaire : streaming (section 5).
- Requêtes (query complexity) : nombre de lectures minimales nécessaire.
Property testing
Déterminer si un objet satisfait une propriété, ou en est ε-loin, en lisant O(1) échantillons.
- Test de tri : un tableau trié ? Lire O(1/ε) positions aléatoires suffit pour distinguer « trié » de « ε-loin d'être trié ».
- Test de graphe bipartite / connexité : échantillonnage de sommets.
- Bounded occurrence testing : propriétés dans des modèles à degré borné.
Échantillonnage et estimation
| Problème | Méthode | Échantillons |
|---|---|---|
| Moyenne (bornée) | Moyenne empirique (Hoeffding) | O(1/ε² · log(1/δ)) |
| Médiane | Échantillonnage + sélection | O(√(1/ε)) approx |
| Fréquence d'un motif | Sampling par positions | O(log n) |
| Diamètre d'un point set | Projections aléatoires (Johnson-Lindenstrauss) | O(log n) |
| Appartenance massive | Bloom filter (section 5) | O(1) par requête |
La borne fondamentale
Un algorithme sublinéaire en temps ne peut pas être exact sur toute entrée (il lit trop peu). D'où la théorie des bornes inférieures de complexité de requêtes : pour bien des problèmes, tout algorithme approché à facteur 1+ε nécessite Ω(f(n)) requêtes — savoir cette borne évite de chercher un algorithme exact sublinéaire inexistant.
8. Synthèse et perspectives
Le tableau d'ensemble
| Domaine | Problème central | Réponse clé | Gain |
|---|---|---|---|
| IA/ML | Apprendre une fonction | Optimisation par gradient | Échelle massive |
| Quantique | Recherche / factorisation | Grover O(√N), Shor poly | Classe BQP |
| Distribué | Cohérence + pannes | Consensus (Raft, PBFT) | Scalabilité |
| Streaming | Une passe, mémoire bornée | Bloom, HLL, CMS | Approximations garanties |
| GPU | Parallélisme massif | Réduction, scan, GEMM | n/p |
| Sublinear | Ne pas tout lire | Property testing, sampling | poly(log n) |
Les tendances à surveiller (2026+)
- LLM et algorithmique : les tokens (attention, KV-cache) posent des problèmes algorithmiques nouveaux (indexation, récupération, déduplication, échantillonnage).
- Quantique tolérant aux fautes : les machine à correction d'erreur (surface codes) détermineront la date de Shor pratique.
- Data-centric AI : l'algorithmique des données (déduplication, curation, embeddings) devient un sujet de première classe.
- Rust, WASM, Edge : l'exécution d'algorithmes près des données (edge computing) pose des contraintes de mémoire et de réseau.
- Confidentialité : chiffrement homomorphe, MPC (secure multi-party computation), differential privacy — des algorithmes dont la complexité doit tenir compte de la confidentialité des données.
- Approximation certifiée : les bornes théoriques (ε, δ) deviennent un argument marketing et produit.
Comment continuer après la formation
- Refaire les 100 exercices du chapitre 17 avec les templates du chapitre 23.
- S'entraîner sur les plateformes du chapitre 20.
- Lire les livres du chapitre 21 — le niveau universitaire y est couvert.
- Suivre les tendances de ce chapitre via les références.
Mot de la fin : les algorithmes sont un métier — le nôtre. Continuez à pratiquer, à lire, à coder. La meilleure façon de prédire l'avenir de l'algorithmique est encore de contribuer à l'écrire.