{ "cells": [ { "cell_type": "markdown", "metadata": { "toc": "true" }, "source": [ "# Table of Contents\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Texte d'oral de modélisation - Agrégation Option Informatique\n", "## Préparation à l'agrégation - ENS de Rennes, 2016-17\n", "- *Date* : 7 avril 2017\n", "- *Auteur* : [Lilian Besson](https://GitHub.com/Naereen/notebooks/)\n", "- *Texte*: Annale 2009, \"Contraintes temporelles\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## À propos de ce document\n", "- Ceci est une *proposition* de correction, partielle et probablement non-optimale, pour la partie implémentation d'un [texte d'annale de l'agrégation de mathématiques, option informatique](http://Agreg.org/Textes/).\n", "- Ce document est un [notebook Jupyter](https://www.Jupyter.org/), et [est open-source sous Licence MIT sur GitHub](https://github.com/Naereen/notebooks/tree/master/agreg/), comme les autres solutions de textes de modélisation que [j](https://GitHub.com/Naereen)'ai écrite cette année.\n", "- L'implémentation sera faite en OCaml, version 4+ :" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "scrolled": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The OCaml toplevel, version 4.02.3\n" ] }, { "data": { "text/html": [ " " ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Sys.command \"ocaml -version\";;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> Notez que certaines fonctions des modules usuels [`List`](http://caml.inria.fr/pub/docs/manual-ocaml/libref/List.html) et [`Array`](http://caml.inria.fr/pub/docs/manual-ocaml/libref/List.html) ne sont pas disponibles en OCaml 3.12.\n", "> J'essaie autant que possible de ne pas les utiliser, ou alors de les redéfinir si je m'en sers." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----\n", "## Question de programmation\n", "La question de programmation pour ce texte était donnée en question 1 en page 9 :\n", "\n", "> \"Programmer l'algorithme PC. Faites le tourner sur l'exemple de réseau non distributif.\"\n", "\n", "La consigne était très courte, mais avec aucune indication.\n", "Notez qu'il est rare que le texte exige un exemple particulier." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----\n", "## Réponse à l'exercice requis\n", "Ça va être assez rapide :\n", "\n", "1. D'abord, il faut définir les types de données avec lesquels on va travailler,\n", "2. Ensuite, implémenter les deux opérations $\\oplus$ et $\\otimes$ sur les ensembles d'intervalles,\n", "3. Puis implémenter l'algorithme PC (similaire à Floyd-Warshall),\n", "4. Et enfin, l'exécuter sur l'exemple de réseau non distributif donné en page 8.\n", "\n", "Si possible, on va essayer de faire des *tests* pour chaque fonction intermédiaire, et un exemple de plus à la fin." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----\n", "### Structures de données\n", "\n", "On se restreint aux intervalles à coordonnées *entières*, et on considère des listes d'intervalles.\n", "Tous les intervalles sont fermés à gauche et à droite." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type intervalle = (int * int);; (* (a, b) représente l'intervalle [a, b] *)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type intervalles = intervalle list;;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "On définit tout de suite deux exemples, $T_a$ et $S_a$ tirés de la Figure 2.a) et $T_b,S_b$ de la Figure 2.b).\n", "Cela permettra de vérifier les opérations $\\oplus$ et $\\otimes$." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- $T_a = \\{ [1, 4], [6, 8] \\}$ et $S_a = \\{ [0, 1], [3, 5], [6, 7] \\}$." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ " " ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "let t_a : intervalles = [\n", " (1, 4);\n", " (6, 8)\n", "];;\n", "\n", "let s_a : intervalles = [\n", " (0, 1);\n", " (3, 5);\n", " (6, 7)\n", "];;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- $T_b = \\{ [-1, 0], [2, 4] \\}$ et $S_b = \\{ [0, 1], [4, 4] \\}$." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ " " ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "let t_b : intervalles = [\n", " (-1, 0);\n", " (2, 4)\n", "];;\n", "\n", "let s_b : intervalles = [\n", " (0, 1);\n", " (4, 4) (* Intervalle de longueur nulle *)\n", "];;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----\n", "### Opérations de base, $\\oplus$ et $\\otimes$\n", "On peut écrire des opérations d'intersection et de composition sur deux intervalles, ensuite il suffira de les généraliser à un ensemble d'intervalle.\n", "\n", "On suit les définitions de l'énoncé.\n", "\n", "#### Intersection\n", "$$\n", "\\forall T = (I_1,\\dots,I_l),\n", "\\forall S = (J_1,\\dots,J_m),\\\\\n", "T \\oplus S := \\{ K_1, \\dots, K_n\\}\n", "\\;\\;\\text{Où}\\;\\; K_k = I_i \\cap J_j.\n", "$$\n", "Notez que $n \\leq l + m$ ici." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pour l'intersection de deux intervalles, l'intervalle vide $\\emptyset$ peut être obtenu, donc la fonction suivante renvoie un type `intervalle option` : soit `None` si $I \\cap J = \\emptyset$, soit `Some (x, y)` si $I \\cap J = [x, y]$." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "let intersection (i : intervalle) (j : intervalle) : intervalle option =\n", " let a = fst i and b = snd i in\n", " let c = fst j and d = snd j in\n", " if b < c || d < a then\n", " None\n", " else\n", " Some (max a c, min b d)\n", ";;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ensuite, il suffit d'explorer tous les couples $(I, J)$ possible, et de ne garder que ceux qui donnent un intervalle.\n", "On supprimera les doublons en vérifiant au fur et à mesure (ça a la même complexité que si on le fait à la fin).\n", "\n", "En manipulant une liste d'intervalle `option`, on doit ruser un peu pour n'ajouter que ceux qui ne sont pas dans `acc` et qui sont des vrais intervalles." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "let ajoute_nouveaux_option (acc : intervalles) (liste_option : intervalle option list) =\n", " List.map\n", " (fun i -> match i with Some i2 -> i2 | None -> (0, 0))\n", " (List.filter (fun i ->\n", " match i with\n", " | None -> false\n", " | Some i2 -> not (List.mem i2 acc)\n", " ) liste_option)\n", ";;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Avec tout ça, on a une belle fonction récursive, avec un accumulateur `acc` qui contient la liste des intervalles dans $T \\oplus S$, construite en considérant les intervalles de $S$ les un après les autres.\n", "\n", "On s'assure de n'avoir ni intervalles vide, ni doublon, grâce à `ajoute_nouveaux_option`." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "let intersections (t : intervalles) (s : intervalles) : intervalles =\n", " let rec inter_aux acc tx sx =\n", " match sx with\n", " | [] -> acc (* Plus rien à ajouter *)\n", " | j :: s2 -> (* On traite j, puis récursivement la suite de s *)\n", " let t_inter_j = List.map (intersection j) tx in\n", " inter_aux ((ajoute_nouveaux_option acc t_inter_j) @ acc) tx s2\n", " in\n", " List.sort compare (inter_aux [] t s)\n", " (* On trie pour les avoir en ordre croissant, c'est tout *)\n", ";;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pour frimer un peu et simplifier l'écriture de l'algorithme PC, on peut définir une opération infixe en raccourci :\n", "$$ T \\oplus S = \\texttt{t ++ s}.$$" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "let ( ++ ) = intersections;;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Composition\n", "Ce sera plus facile.\n", "$$\n", "\\forall T = (I_1,\\dots,I_l),\n", "\\forall S = (J_1,\\dots,J_m),\\\\\n", "T \\otimes S := \\{ K_1, \\dots, K_n\\}\n", "\\;\\;\\text{Où}\\;\\; K_k = [a + c, b + d], \\;\\text{si}\\; I_i = [a, b], J_j = [c, d].\n", "$$\n", "Notez que $n \\leq l \\times m$ ici." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pour la composition de deux intervalles, il n'y pas de difficulté particulière :" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "let composition (i : intervalle) (j : intervalle) : intervalle =\n", " let a = fst i and b = snd i in\n", " let c = fst j and d = snd j in\n", " (a + c, b + d)\n", ";;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Et on les combine facilement, en gardant la même architecture que pour `intersections`." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "let ajoute_nouveaux (acc : intervalles) (liste : intervalles) : intervalles =\n", " List.filter (fun i -> not (List.mem i acc)) liste\n", ";;" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "let compositions (t : intervalles) (s : intervalles) : intervalles =\n", " let rec compo_aux acc tx sx =\n", " match sx with\n", " | [] -> acc (* Plus rien à ajouter *)\n", " | j :: s2 -> (* On traite j, puis récursivement la suite de s *)\n", " let t_compo_j = List.map (composition j) tx in\n", " compo_aux ((ajoute_nouveaux acc t_compo_j) @ acc) tx s2\n", " in\n", " List.sort compare (compo_aux [] t s)\n", " (* On trie pour les avoir en ordre croissant, c'est tout *)\n", ";;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pour frimer un peu et simplifier l'écriture de l'algorithme PC, on peut définir une opération infixe en raccourci :\n", "$$ T \\otimes S = \\texttt{t ** s}.$$" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "let ( ** ) = compositions;;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Union\n", "On peut aussi rapidement définier $T \\cup S$, pour l'union. C'est très facile." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "let union (t : intervalles) (s : intervalles) : intervalles =\n", " List.append t s\n", ";;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Exemples\n", "On aimerait reproduire les exemples de la Figure 2 du texte.\n", "![images/intersection_composition_contraintes.png](images/intersection_composition_contraintes.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- Pour les deux intervalles de la Figure 2.a) : $T_a = \\{ [1, 4], [6, 8] \\}$ et $S_a = \\{ [0, 1], [3, 5], [6, 7] \\}$." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "t_a ++ s_a;;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "On retrouve bien le résultat de la Figure 2.a)." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ " " ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "t_a ** s_a;;\n", "union t_a s_a;;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- Pour les deux intervalles de la Figure 2.b) : $T_b = \\{ [-1, 0], [2, 4] \\}$ et $S_b = \\{ [0, 1], [4, 4] \\}$." ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "t_b ** s_b;;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "On retrouve bien le résultat de la Figure 2.b).\n", "\n", "L'intervalle $[3, 4]$ est inclus dans $[2, 5]$, donc on devrait ajouter une étape de nettoyage pour donner une forme canonique aux intervalles produit par `composition`. On le fait plus bas." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ " " ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "t_b ++ s_b;;\n", "union t_b s_b;;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "On remarque que les intervalles sont bien donnés dans l'ordre croissant, puisqu'on a pensé à trier la sortie des deux fonctions, mais ça ne change rien." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Une étape de plus : ne pas inclure des intervalles inclus dans ceux déjà présents\n", "\n", "On va raffiner les fonctions définis ci-dessus en ajoutant un test, sur leur résultat final.\n", "\n", "- `est_inclus i j` teste si $I \\subseteq J$." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "let est_inclus (i : intervalle) (j : intervalle) : bool =\n", " let a = fst i and b = snd i in\n", " let c = fst j and d = snd j in\n", " c <= a && b <= d\n", ";;" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ " " ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ " " ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "est_inclus (3, 4) (2, 5);; (* true *)\n", "est_inclus (2, 5) (3, 4);; (* false *)\n", "est_inclus (1, 1) (1, 1);; (* true *)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- `est_inclus_dans_un i acc` teste si $I \\subseteq J$ pour **un** $J \\neq I \\in \\mathrm{Acc}$." ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "let est_inclus_dans_un (i : intervalle) (acc : intervalles) : bool =\n", " List.exists (fun j -> (i != j) && (est_inclus i j)) acc\n", ";;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- On peut écrire une fonction `filtre` qui retire les intervalles inclus dans d'autres, puis retire les doublons." ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ " " ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ " " ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "let retire_les_inclus (liste : intervalles) : intervalles =\n", " List.filter (fun i -> not (est_inclus_dans_un i liste)) liste\n", ";;\n", "\n", "let retire_les_doublons (liste : intervalles) : intervalles =\n", " let reponse = ref [] in\n", " List.iter (fun i ->\n", " if not (List.mem i !reponse) then\n", " reponse := i :: !reponse\n", " ) liste;\n", " !reponse\n", ";;\n", "\n", "let filtre liste =\n", " retire_les_doublons (retire_les_inclus liste)\n", ";;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- L'intersection ne devrait pas être impactée." ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "let intersections2 (t : intervalles) (s : intervalles) : intervalles =\n", " List.sort compare (filtre (intersections t s))\n", " (* On trie pour les avoir en ordre croissant, c'est tout *)\n", ";;" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ " " ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "let ( ++ ) = intersections2;;\n", "\n", "t_a ++ s_a;;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- Mais la composition va donner la bonne réponse désormais pour l'exemple de la figure 2.b)." ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "let compositions2 (t : intervalles) (s : intervalles) : intervalles =\n", " List.sort compare (filtre (compositions t s))\n", " (* On trie pour les avoir en ordre croissant, c'est tout *)\n", ";;" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ " " ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "let ( ** ) = compositions2;;\n", "\n", "t_b ** s_b;;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> C'était un peu long, mais c'est propre au moins." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notez que pour obtenir une vraie forme canonique, il faudrait aussi rassembler les intervalles consécutifs ($\\{ [0, 1], [1, 2] \\} \\rightarrow \\{ [0, 1] \\}$) et se recoupant ($\\{ [0, 3], [2, 4] \\} \\rightarrow \\{ [0, 4] \\}$).\n", "\n", "> Ça prendrait trop de temps. Et ce n'était pas exigé." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----\n", "### L'algorithme PC" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Structure de données pour les réseaux STP\n", "On a besoin désormais de considérer des réseaux STP, qui sont des graphes dont les sommets sont des entiers, et dont les arêtes sont étiquetées par des *listes* (non vides) d'intervalles.\n", "\n", "L'algorithme PC demande de pouvoir accéder rapidement et facilement à l'arête entre deux sommets $x,y$, $T_{x,y}$.\n", "Ainsi, la structure de matrice d'adjacence semble appropriée.\n", "Les arêtes inexistantes dans le réseau auront simplement $T_{x,y} = \\emptyset$, c'est-à-dire `[]` (liste vide).\n", "\n", "> On supposera que toutes les matrices données aux différentes fonctions définies plus bas sont carrées, on ne le vérifie pas (mais ce serait facile)." ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ " " ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ " " ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type sommet = int;;\n", "type arete = intervalles;; (* c'est l'idée *)\n", "\n", "type reseauSTP = intervalles array array;;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "On essaie tout de suite notre structure de données avec l'exemple du réseau STP de la figure 4 :" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ " " ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ " " ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ " " ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ " " ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "let t_01 : intervalles = [(0, 1); (10, 20)];;\n", "let t_12 : intervalles = [(0, 10)];;\n", "let t_13 : intervalles = [(25, 50)];;\n", "let t_23 : intervalles = [(0, 20); (40, 40)];;\n", "\n", "let stp_4 : reseauSTP = [|\n", " [| []; t_01; []; [] |];\n", " [| []; t_12; t_13; [] |];\n", " [| []; []; t_23; [] |];\n", " [| []; []; []; [] |];\n", "|];;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "On peut vérifier qu'il n'est pas distributif, en prenant l'exemple du texte (fin page 8) :" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ " " ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ " " ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ " " ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ " " ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ " " ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "let s_13 = t_12 ** t_23;;\n", "\n", "t_13 ++ s_13;;\n", "t_01 ** (t_13 ++ s_13);; (* 1er cas *)\n", "\n", "t_01 ** t_13;;\n", "t_01 ** s_13;;\n", "(t_01 ** t_13) ++ (t_01 ** s_13);; (* 2nd cas *)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "En simplifiant, on obtient :\n", "\n", "- $T_{01} \\otimes (T_{13} \\oplus S_{13} = \\{ [25, 31], [35, 70] \\}$,\n", "- $(T_{01} \\otimes T_{13}) \\oplus (T_{01} \\otimes S_{13}) = \\{ [25, 70] \\}$,\n", "\n", "qui sont bien différents." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Enfin, on peut rapidement vérifier si la matrice d'un graphe est bien carrée :" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "let est_carree matrice =\n", " let n = Array.length matrice in\n", " Array.fold_left (fun b x -> b && (n = (Array.length x))) true matrice\n", ";;" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "est_carree stp_4;;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### L'algorithme PC\n", "Ça semble être un bon choix.\n", "Maintenant, passons à l'algorithme PC.\n", "\n", "![images/algorithme_PC.png](images/algorithme_PC.png)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Il n'y a pas de boucle `until` en Caml, mais avec une boucle `while` on arrivera presque à la même chose." ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "exception Fini;; (* Pour faire le [exit]. *)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "On peut l'écrire avant pour la rendre plus claire, mais l'étape clé de l'algorithme PC (et Floyd-Warshall) est une opération dite de *relaxation* :\n", "$$ T_{i,j} \\oplus (T_{i,k} \\otimes T_{k,j}).$$" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "let relaxe (reseau : reseauSTP) i j k =\n", " let t_ij = reseau.(i).(j)\n", " and t_ik = reseau.(i).(k)\n", " and t_kj = reseau.(k).(j)\n", " in t_ij ++ (t_ik ** t_kj)\n", ";;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "On a tout ce qu'il faut pour écrire l'algorithme." ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "let algorithmePC (reseau : reseauSTP) : (reseauSTP * intervalles list) =\n", " let resT = Array.copy reseau (* on ne modifie pas l'entrée *)\n", " and resS = ref [||] in\n", " let n = Array.length resT\n", " and allseen = ref [] (* Pour débogguer, je veux la liste des Tij vus *)\n", " in\n", " begin\n", " try begin\n", " while !resS != resT do\n", " resS := Array.copy resT; (* S := T *)\n", " for k = 0 to n - 1 do\n", " for i = 0 to n - 1 do\n", " for j = 0 to n - 1 do\n", " resT.(i).(j) <-\n", " relaxe resT i j k;\n", " allseen := (resT.(i).(j)) :: !allseen; (* on l'ajoute *)\n", " if resT.(i).(j) = [] then\n", " raise Fini\n", " done\n", " done\n", " done\n", " done;\n", " end\n", " with Fini -> () (* On ignore l'exception, on a juste terminé. *)\n", " end;\n", " resT, !allseen\n", ";;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----\n", "### Exemple de réseau non distributif\n", "On va traiter l'exemple de la Figure 4 du texte, comme défini plus haut :\n", "\n", "![images/reseau_distributif.png](images/reseau_distributif.png)" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "stp_4;;" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "algorithmePC stp_4;;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> Je ne suis pas sûr de comment interprêter ce résultat...\n", "> \n", "> - soit j'ai fait une erreur dans l'implémentation,\n", "> - soit l'algorithme PC devait ne rien modifier à $T$ sur cet exemple..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----\n", "### D'autres exemples\n", "\n", "On peut étudier le STP de la Figure 1., en enlevant la contrainte $[60, \\infty)$, qui ne rentre pas dans notre implémentation.\n", "\n", "![images/stp_figure1.png](images/stp_figure1.png)." ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ " " ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ " " ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ " " ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ " " ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ " " ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "let t_01 : intervalles = [(10, 20)];;\n", "let t_12 : intervalles = [(30, 40)];;\n", "let t_32 : intervalles = [(10, 20)];;\n", "let t_34 : intervalles = [(20, 30); (40, 50)];;\n", "let t_40 : intervalles = [(60, 70)];;\n", "\n", "let stp_1 : reseauSTP = [|\n", " [| []; t_01; []; []; [] |];\n", " [| []; []; t_12; []; [] |];\n", " [| []; []; []; []; [] |];\n", " [| []; []; t_32; []; t_34 |];\n", " [| t_40; []; []; []; [] |];\n", "|];;" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "data": { "text/html": [ " " ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "algorithmePC stp_1;;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> Je ne suis pas sûr de comment interprêter ce résultat...\n", "> \n", "> - soit j'ai fait une erreur dans l'implémentation,\n", "> - soit l'algorithme PC devait ne rien modifier à $T$ sur cet exemple..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----\n", "## Conclusion\n", "\n", "Voilà pour la question obligatoire de programmation :\n", "\n", "- on a décomposé le problème en sous-fonctions,\n", "- on a essayé d'être fainéant, en réutilisant les sous-fonctions,\n", "- on a fait des exemples et *on les garde* dans ce qu'on présente au jury,\n", "- on a testé la fonction exigée sur de petits exemples et sur un exemple de taille réelle (venant du texte)\n", "\n", "Et on a essayé de faire *un peu plus*, en implémentant la vérification d'une contrainte de plus.\n", "\n", "> Bien-sûr, ce petit notebook ne se prétend pas être une solution optimale, ni exhaustive." ] } ], "metadata": { "kernelspec": { "display_name": "OCaml", "language": "ocaml", "name": "iocaml-kernel" }, "language_info": { "name": "ocaml", "version": "4.1.0" }, "toc": { "colors": { "hover_highlight": "#DAA520", "running_highlight": "#FF0000", "selected_highlight": "#FFD700" }, "moveMenuLeft": true, "nav_menu": { "height": "289px", "width": "252px" }, "navigate_menu": true, "number_sections": true, "sideBar": true, "threshold": 4, "toc_cell": true, "toc_position": { "height": "610px", "left": "0px", "right": "1223px", "top": "124px", "width": "249px" }, "toc_section_display": "block", "toc_window_display": true } }, "nbformat": 4, "nbformat_minor": 2 }