Adloun

Algorithmes, programmes et complexité

Cours · informatique (MP2I/MPI), chapitre 3 · MP2I et MPI · le début du chapitre, la suite sur Adloun

La complexité d'un algorithme : les temps de calcul en log n, n et n² en fonction de la taille des données.

3.1 Deux mots qu'on confond, et qu'il faut séparer

Un algorithme est une méthode : une suite finie d'opérations qui, à partir de données d'entrée, produit un résultat. Un programme en est une mise en œuvre dans un langage donné. La dichotomie est un algorithme ; les quinze lignes de C du chapitre chap:diviser en sont un programme, et les huit lignes d'OCaml en sont un autre — le même algorithme, deux programmes.

La distinction n'est pas scolaire. Elle décide de ce qu'on peut affirmer. « Cet algorithme fait comparaisons » est une propriété de la méthode, vraie dans tous les langages. « Ce programme s'exécute en 3 ms » est une propriété d'un programme sur une machine un jour donné. Les deux se mesurent, mais pas de la même façon, et l'un ne remplace jamais l'autre.

3.2 Paradigmes

Définition 3.1Trois manières de dire ce qu'on veut
  • Impératif structuré : on décrit une suite d'actions qui modifient un état. Les boucles et l'affectation en sont les outils. C en est l'exemple du programme.
  • Déclaratif fonctionnel : on décrit le résultat comme une expression, composée de fonctions, sans état modifié. OCaml en est l'exemple.
  • Logique : on décrit les relations que le résultat doit satisfaire, et un moteur cherche ce qui les satisfait. Le programme ne le rencontre qu'une fois : à l'occasion des bases de données (chapitre chap:sql), où l'on écrit quelles lignes on veut, jamais comment les trouver.
iRemarqueCe que le programme écarte

« On ne présente pas de théorie générale sur les paradigmes de programmation, on se contente d'observer les paradigmes employés sur des exemples. » Et le saut inconditionnel — l'instruction GOTO — est explicitement hors programme : c'est une instruction qui permet d'aller n'importe où dans le code, et dont l'abandon est précisément ce qui a rendu la programmation structurée raisonnable.

Exemple 3.2La même somme, trois paradigmes

/* Impératif : un état (s) que l'on modifie tour après tour. */
int s = 0;
for (int i = 0; i < n; i = i + 1) { s = s + t[i]; }

(* Fonctionnel : une expression, aucun état. *)
let rec somme = function [] -> 0 | x :: r -> x + somme r

-- Déclaratif : on dit CE QU'ON VEUT, pas comment l'obtenir.
SELECT SUM(valeur) FROM mesures;

3.3 Compilé, interprété

Définition 3.3Deux façons d'exécuter un texte

Un langage compilé est traduit en entier, avant exécution, en un fichier que la machine sait exécuter. Un langage interprété est lu et exécuté au fil de la lecture par un autre programme, l'interpréteur.

Compilé (C, OCaml)Interprété (Python)
Erreurs de typedétectées avant l'exécutiondécouvertes pendant
Vitessele code machine est directune couche s'interpose
Cycle de travailcompiler, puis lancerlancer

Le programme demande de connaître le passage « d'un fichier texte source en un fichier objet puis en un fichier exécutable », et la « différence entre fichiers d'interface et fichiers d'implémentation » — vue au chapitre chap:langage-c.

iRemarqueOCaml sait faire les deux

ocamlopt produit du code machine, ocamlc un bytecode interprété, et la boucle interactive ocaml se comporte comme un interpréteur. « Compilé » et « interprété » qualifient donc une mise en œuvre, pas un langage.

3.4 Les flottants, ou pourquoi

Définition 3.4Ce qu'est un flottant

Un double occupe 64 bits, répartis en un signe, un exposant et une mantisse. Il représente donc un nombre de la forme , où ne compte qu'un nombre fini de bits. Les réels représentables sont en nombre fini ; tout le reste est arrondi au plus proche.

AttentionLa divergence entre le calcul théorique et le calcul effectué

0.1 +. 0.2 = 0.3        (* false *)
0.1 +. 0.2              (* 0.30000000000000004 *)

Ni ni ne s'écrivent exactement en base 2 — pas plus que ne s'écrit exactement en base 10. Chacun est arrondi, et les deux erreurs se cumulent.

Le programme demande d'« illustrer l'impact de la représentation par des exemples de divergence entre le calcul théorique d'un algorithme et les valeurs calculées par un programme ». En voici un second, plus inquiétant :


double x = 0.0;
for (int i = 0; i < 10; i = i + 1) { x = x + 0.1; }
/* x n'est PAS égal à 1.0 : c'est 0.9999999999999999 */

Un algorithme dont la condition d'arrêt serait x == 1.0 ne s'arrêterait jamais. La boucle théorique termine ; le programme, non.

Exercices du chapitre

La suite du cours · Tous les exercices du chapitre