Python, Java, PHP, JavaScript : syntaxe, complexité asymptotique, POO, patrons de conception et programmation dynamique.
Avoir validé la Leçon 1 (référentiel et parcours). Connaître les opérateurs de base d'un langage impératif (Python ou JavaScript). Avoir installé sur sa machine : Python 3.11+, OpenJDK 17+, PHP 8.2+, Node.js 20+, un IDE (VS Code, IntelliJ IDEA Community ou PyCharm Educational). Lecture conseillée : docs.python.org/3/tutorial.
En 1968, lors de la célèbre conférence de l'OTAN à Garmisch, le terme « crise du logiciel » est forgé : les projets explosent en coût, en délai et en bugs. C'est l'acte de naissance du génie logiciel et de l'algorithmique scientifique. Depuis, Donald Knuth (TAOCP, 1968-2025), Edsger Dijkstra (« Go To Statement Considered Harmful », 1968), Niklaus Wirth (Pascal) et plus récemment Guido van Rossum (Python, 1991) ont forgé les fondations.
Au BUT Informatique, la R1.01 Initiation au développement au S1 forme à la rigueur algorithmique (séquence, sélection, itération, fonctions, complexité). La R2.01 Développement orienté objets au S2 introduit Java et les 4 piliers de la POO. Les SAÉ S1.01 (« Implémentation d'un besoin client »), S2.01 (« Développement d'une application »), S3.01 (« Développement d'une application complexe ») cadrent l'évaluation.
L'enjeu : un développeur senior produit 10 à 20 lignes de code par jour en moyenne — ce qui semble peu jusqu'à comprendre que 80 % du temps est consacré à lire du code, débuguer, tester et documenter. La lisibilité prime donc sur le « code malin ». Le programme du BUT cible donc autant la maîtrise syntaxique que les bonnes pratiques : nommage, modularité, tests unitaires, revues de code.
Cette leçon couvre les langages obligatoires, la théorie de la complexité, les structures de données, la POO, les patrons et la programmation dynamique. Tous les exemples sont fonctionnels et testés.
Le Programme National BUT Informatique 2021 impose un tronc multilingue. Aucun parcours ne se limite à un seul langage.
| Langage | Paradigme principal | Usage métier | Semestre d'apparition | Créateur / date |
|---|---|---|---|---|
Python | Multi-paradigme (impératif + objet) | Scripting, IA, data science, prototypage | S1 | Guido van Rossum, 1991 |
Java | Orienté objet pur | Backend entreprise, Android, JEE | S2 | James Gosling (Sun), 1995 |
JavaScript / TypeScript | Multi-paradigme (fonctionnel + objet) | Frontend (React, Vue), Node.js | S2 | Brendan Eich, 1995 / Microsoft, 2012 |
SQL | Déclaratif (ensembliste) | Requêtage bases relationnelles | S1 | Boyce & Chamberlin (IBM), 1974 |
PHP | Multi-paradigme | Web côté serveur (Symfony, Laravel) | S3 | Rasmus Lerdorf, 1995 |
C / C++ | Impératif / Objet | Systèmes, embarqué, performance | S3 (selon parcours) | Kernighan & Ritchie 1972 / Stroustrup 1985 |
Bash / PowerShell | Shell scripting | Administration, automatisation | S2 (ASRV) | Bourne 1979 / Microsoft 2006 |
Selon Bjarne Stroustrup, créateur du C++ : « Il n'existe que deux types de langages : ceux dont les gens se plaignent et ceux que personne n'utilise. » — Choisir un langage, c'est accepter ses compromis.
Source : stroustrup.com.
Tout algorithme repose sur 3 structures de base, démontrées formellement par Corrado Böhm & Giuseppe Jacopini en 1966 (« Flow Diagrams, Turing Machines and Languages with Only Two Formation Rules », Communications of the ACM) :
if / else if / else, ou switch / match.while, for, do-while.Toute fonction calculable peut être écrite avec ces 3 structures — c'est l'équivalence de Turing. Le goto est donc strictement évitable (cf. Dijkstra, 1968).
La notation O (Big-O) mesure le comportement d'un algorithme quand n → +∞, indépendamment du matériel.
| Notation | Type | n=10 | n=1 000 | n=10⁶ | Exemple | Verdict |
|---|---|---|---|---|---|---|
O(1) | Constant | 1 | 1 | 1 | Accès tableau par index, lookup HashMap | Idéal |
O(log n) | Logarithmique | 3 | 10 | 20 | Recherche dichotomique, arbre équilibré | Excellent |
O(n) | Linéaire | 10 | 1 000 | 10⁶ | Parcours liste, max d'un tableau | Très bon |
O(n log n) | Quasi-linéaire | 33 | 10 000 | 2×10⁷ | Tri fusion, tri rapide moyen, FFT | Bon (limite tri optimal) |
O(n²) | Quadratique | 100 | 10⁶ | 10¹² | Tri bulle, tri insertion, comparaison naïve | Acceptable jusqu'à 10 000 |
O(n³) | Cubique | 1 000 | 10⁹ | 10¹⁸ | Multiplication matricielle naïve, Floyd-Warshall | Lent au-delà de 500 |
O(2ⁿ) | Exponentiel | 1 024 | 10³⁰⁰ | ∞ | Fibonacci naïf, sous-ensembles, sac à dos exact | Inutilisable au-delà de 40 |
O(n!) | Factoriel | 3,6 M | ∞ | ∞ | Voyageur de commerce force brute, permutations | Inutilisable au-delà de 12 |
Selon Donald Knuth (« Structured Programming with go to Statements », Computing Surveys, 1974) : « Premature optimization is the root of all evil. » — La lisibilité prime sur l'optimisation tant que la complexité asymptotique reste correcte.
Source : doi.org/10.1145/356635.356640.
| Structure | Accès | Insertion | Suppression | Recherche | Usage typique |
|---|---|---|---|---|---|
Tableau dynamique (list, ArrayList) | O(1) | O(1) fin / O(n) milieu | O(n) | O(n) | Collection ordonnée générique |
| Liste chaînée simple | O(n) | O(1) si pointeur | O(1) si pointeur | O(n) | File d'attente, historique navigateur |
| Liste doublement chaînée | O(n) | O(1) | O(1) | O(n) | LRU cache, undo/redo |
| Pile (Stack, LIFO) | O(1) sommet | O(1) push | O(1) pop | O(n) | Parcours profondeur, undo, parser |
| File (Queue, FIFO) | O(1) | O(1) enqueue | O(1) dequeue | O(n) | Parcours largeur, tâches asynchrones |
Table de hachage (dict, HashMap) | O(1) moyen | O(1) moyen | O(1) moyen | O(1) moyen | Index, cache, dictionnaire |
PILE (LIFO) FILE (FIFO)
+---+---+---+---+
[Pop] <- sommet -> [D] | A | B | C | D | -> [Dequeue C? Non, A]
+---+---+---+---+
[Push E] -> [E] debut fin
[D] [C] [B] [A] base Enqueue --> (cote fin)
Sur un tableau trié de 1 million d'éléments, la recherche dichotomique trouve l'élément en max 20 comparaisons (log₂ 10⁶ ≈ 20), contre 500 000 en moyenne pour une recherche linéaire.
# Recherche dichotomique en Python - O(log n) en temps, O(1) en espace
def dicho(tab, x):
g, d = 0, len(tab) - 1
while g <= d:
m = (g + d) // 2
if tab[m] == x:
return m
elif tab[m] < x:
g = m + 1
else:
d = m - 1
return -1
# Test
print(dicho([1, 3, 5, 7, 9, 11, 13], 7)) # 3
print(dicho([1, 3, 5, 7, 9, 11, 13], 4)) # -1
Complexité : O(log n). Préconditions impératives : tableau trié en ordre croissant et accès indexé O(1) (liste Python OK).
// Recherche dichotomique en Java 17 - O(log n)
public class Dicho {
public static int rechercher(int[] tab, int x) {
int g = 0, d = tab.length - 1;
while (g <= d) {
int m = g + (d - g) / 2; // evite overflow int
if (tab[m] == x) return m;
if (tab[m] < x) g = m + 1;
else d = m - 1;
}
return -1;
}
public static void main(String[] args) {
int[] t = {1, 3, 5, 7, 9, 11, 13};
System.out.println(rechercher(t, 7)); // 3
}
}
Détail crucial : m = g + (d - g) / 2 évite l'overflow d'entier qui frappait java.util.Arrays.binarySearch avant Java 6 (cf. Joshua Bloch, Google Research, 2006).
# Fibonacci naif - O(2^n) - inutilisable au-dela de n=35
def fib_naif(n):
if n < 2:
return n
return fib_naif(n - 1) + fib_naif(n - 2)
# Fibonacci memoise - O(n) en temps, O(n) en espace
from functools import lru_cache
@lru_cache(maxsize=None)
def fib(n):
if n < 2:
return n
return fib(n - 1) + fib(n - 2)
# Iteratif - O(n) en temps, O(1) en espace (optimal)
def fib_iter(n):
a, b = 0, 1
for _ in range(n):
a, b = b, a + b
return a
print(fib_iter(100)) # 354224848179261915075
Comparaison de temps réel pour n=35 : fib_naif ≈ 4 s, fib mémoïsé < 1 ms, fib_iter < 1 µs. Voir functools.lru_cache dans la documentation Python officielle.
<?php
// Pattern Observer (GoF, 1994) - PHP 8 avec types stricts
declare(strict_types=1);
interface Observer {
public function notifier(string $event): void;
}
class Sujet {
/** @var Observer[] */
private array $observers = [];
public function attacher(Observer $o): void {
$this->observers[] = $o;
}
public function diffuser(string $event): void {
foreach ($this->observers as $o) {
$o->notifier($event);
}
}
}
class Logger implements Observer {
public function notifier(string $event): void {
echo "[LOG] " . $event . PHP_EOL;
}
}
$sujet = new Sujet();
$sujet->attacher(new Logger());
$sujet->diffuser("Utilisateur connecte");
// [LOG] Utilisateur connecte
Le pattern Observer est l'un des 23 patrons GoF (Gamma, Helm, Johnson, Vlissides, 1994). PHP 8 introduit le typage strict (declare(strict_types=1);) qui rejette les conversions implicites string ↔ int.
Les 4 piliers de la POO :
private / protected / public. Cacher les détails internes.extends en Java, : en Python, extends en PHP). Réutiliser le comportement parent.abstract, interfaces. Exposer un contrat sans dévoiler l'implémentation.Les 23 design patterns GoF (Gamma, Helm, Johnson, Vlissides, « Design Patterns », Addison-Wesley, 1994) sont étudiés en S4. Les 5 patrons incontournables BUT :
| Patron | Famille GoF | Problème résolu | Exemple BUT Info |
|---|---|---|---|
| Singleton | Création | Une seule instance globale | Connexion DB unique |
| Factory Method | Création | Déléguer la création d'objets | Construire User/Admin/Guest |
| Observer | Comportement | Notifier 1-N abonnés | Event bus, MVC |
| Strategy | Comportement | Choisir un algo à l'exécution | Tri rapide vs fusion selon n |
| MVC | Architectural (Reenskaug 1979) | Séparer données / vue / logique | Symfony, Spring MVC, Laravel |
En Python, la limite de récursion par défaut est 1 000 niveaux (cf. sys.getrecursionlimit()). Un Fibonacci récursif naïf au-dessus de fib(35) dépasse 4 s ; au-dessus de fib(990), RecursionError. Solutions :
functools.lru_cache ou functools.cache (Python 3.9+) pour mémoïser.sys.setrecursionlimit(10000) — risque de stack overflow OS.Le GIL sérialise l'exécution des threads CPython, rendant le threading inefficace pour du CPU-bound. Solutions : multiprocessing, concurrent.futures.ProcessPoolExecutor, ou Python 3.13+ avec mode no-GIL expérimental (PEP 703).
Lister 1 000 utilisateurs puis pour chacun faire un SELECT de leurs commandes = 1 001 requêtes au lieu d'une seule jointure ou d'un IN (...). Frappe massivement les ORM (Doctrine, Hibernate, SQLAlchemy). Détection : activer le query log Doctrine ou utiliser EXPLAIN ANALYZE.
# MAUVAIS : la liste par defaut est partagee entre TOUS les appels
def ajouter(elem, lst=[]):
lst.append(elem)
return lst
print(ajouter(1)) # [1]
print(ajouter(2)) # [1, 2] <-- inattendu !
# BON
def ajouter(elem, lst=None):
if lst is None:
lst = []
lst.append(elem)
return lst
En SAÉ, indiquez TOUJOURS la complexité au-dessus de votre fonction (commentaire). C'est un critère explicite de notation. Exemple :
# dicho(tab, x) -> O(log n) en temps, O(1) en espace.
# Precondition : tab trie en ordre croissant.
def dicho(tab, x): ...
Les revues de code en entreprise (GitHub PR, GitLab MR) suivent la checklist Google Engineering Practices : lisibilité, complexité, tests, sécurité, perf.
Correctifs : 1=b (log₂ n comparaisons max) ; 2=c (polymorphisme = override) ; 3=d (Singleton, Gang of Four 1994).
Inscrivez-vous pour accéder aux 5 autres leçons + le quiz final.
Créer mon compteChoisis quels cookies tu acceptes — modifiable à tout moment.