diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c46d592..4b2604c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,4 +44,10 @@ jobs: - name: run tests run: | pip install pytest - PYTHONPATH=. UNITTEST_GOING=1 pytest --durations=10 _unittests --ignore-glob=**pygame*.py + PYTHONPATH=. UNITTEST_GOING=1 pytest --durations=10 _unittests --ignore-glob=**pygame*.py --ignore _unittests/ut_xrun_doc/test_documentation_examples.py --ignore _unittests/ut_xrun_doc/test_documentation_notebook.py + + - name: run tests examples + run: PYTHONPATH=. UNITTEST_GOING=1 pytest --durations=10 _unittests/ut_xrun_doc/test_documentation_examples.py -v + + - name: run tests notebooks + run: PYTHONPATH=. UNITTEST_GOING=1 pytest --durations=10 _unittests/ut_xrun_doc/test_documentation_notebook.py -v diff --git a/_doc/practice/exams.rst b/_doc/practice/exams.rst index 1a1895a..8bc8360 100644 --- a/_doc/practice/exams.rst +++ b/_doc/practice/exams.rst @@ -14,31 +14,32 @@ Séances notées Enoncés +++++++ -* :download:`td_note_2006 ` -* :download:`td_note_2007 ` -* :download:`td_note_2008 ` -* :download:`td_note_2009 ` -* :download:`td_note_2010 ` -* :download:`td_note_2010_rattrape ` -* :download:`td_note_2011 ` -* :download:`td_note_2012 ` -* :download:`td_note_2013 ` +* :download:`td_note_2006 ` (recheche dans une liste triée) +* :download:`td_note_2007 ` (écart entre jours fériés) +* :download:`td_note_2008 ` (pandas) +* :download:`td_note_2009 ` (pièces de 4 euros) +* :download:`td_note_2010 ` (centres mobiles) +* :download:`td_note_2010_rattrape ` (plus court chemin passant par toutes les villes) +* :download:`td_note_2011 ` (noms de quartiers) +* :download:`td_note_2012 ` (prétraitement de données) +* :download:`td_note_2013 ` (coloriage) * :download:`td_note_2013_rattrape ` -* :download:`td_note_2014 ` -* :download:`td_note_2015 ` -* :download:`td_note_2016 ` +* :download:`td_note_2014 ` (distance d'édition) +* :download:`td_note_2015 ` (investissement locatif) +* :download:`td_note_2016 ` (régression linéaire) * :download:`td_note_2017 ` (broken) -* :download:`td_note_2018 ` -* :download:`td_note_2019 ` -* :download:`td_note_2020 ` -* :download:`td_note_2021 ` -* :download:`td_note_2022 ` -* :download:`td_note_2022_rattrapage ` -* :download:`td_note_2022_rattrapage2 ` -* :download:`td_note_2023 ` -* :download:`td_note_2023-2024 ` -* :download:`td_note_2023-2024_rattrapage ` -* :download:`td_note_2024 ` +* :download:`td_note_2018 ` (centres mobiles) +* :download:`td_note_2019 ` (machine learning et catégories) +* :download:`td_note_2020 ` (appariement et mariages) +* :download:`td_note_2021 ` (épidémie) +* :download:`td_note_2022 ` (multiplication de plusieurs matrices) +* :download:`td_note_2022_rattrapage ` (brique de lait) +* :download:`td_note_2022_rattrapage2 ` (dessin d'arbre) +* :download:`td_note_2023 ` (pairwise distances) +* :download:`td_note_2023-2024 ` (compression) +* :download:`td_note_2023-2024_rattrapage ` (recherche de motifs) +* :download:`td_note_2024 ` (câblage électrique) +* :download:`td_note_2025 ` (comptabilité schtroumph) Corrections +++++++++++ @@ -67,6 +68,7 @@ Corrections exams/td_note_2023-2024 exams/td_note_2023-2024_rattrapage exams/td_note_2024 + exams/td_note_2025 Exercices courts ================ diff --git a/_doc/practice/exams/td_note_2025.ipynb b/_doc/practice/exams/td_note_2025.ipynb new file mode 100644 index 0000000..b8e5319 --- /dev/null +++ b/_doc/practice/exams/td_note_2025.ipynb @@ -0,0 +1,428 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 1A - Enoncé 5 novembre 2025 - comptabilité schtroumph\n", + "\n", + "Correction de l'examen du 5 novembre 2025.\n", + "\n", + "Toutes les questions valent 2 points." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Q1 - Implémenter une fonction qui calcule la distance entre deux matrices" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.int64(45)" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import numpy as np\n", + "\n", + "\n", + "def distance(table1, table2):\n", + " return np.abs(table1 - table2).sum()\n", + "\n", + "\n", + "table1 = np.array([[8, 7], [18, 18], [6, 8]])\n", + "table2 = np.array([[18, 18], [7, 9], [8, 6]])\n", + "distance(table1, table2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Q2 - Implémenter une fonction qui retourne les p ermutations des n premiers entiers" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[(0, 1, 2, 3),\n", + " (0, 1, 3, 2),\n", + " (0, 2, 1, 3),\n", + " (0, 2, 3, 1),\n", + " (0, 3, 1, 2),\n", + " (0, 3, 2, 1),\n", + " (1, 0, 2, 3),\n", + " (1, 0, 3, 2),\n", + " (1, 2, 0, 3),\n", + " (1, 2, 3, 0),\n", + " (1, 3, 0, 2),\n", + " (1, 3, 2, 0),\n", + " (2, 0, 1, 3),\n", + " (2, 0, 3, 1),\n", + " (2, 1, 0, 3),\n", + " (2, 1, 3, 0),\n", + " (2, 3, 0, 1),\n", + " (2, 3, 1, 0),\n", + " (3, 0, 1, 2),\n", + " (3, 0, 2, 1),\n", + " (3, 1, 0, 2),\n", + " (3, 1, 2, 0),\n", + " (3, 2, 0, 1),\n", + " (3, 2, 1, 0)]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import itertools\n", + "\n", + "\n", + "def permutations(n):\n", + " return list(itertools.permutations(list(range(n))))\n", + "\n", + "\n", + "permutations(4)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Q3, Q4 - Implémenter une fonction qui p ermute les colonnes d'une matrice." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 6, 8],\n", + " [18, 18],\n", + " [ 8, 7]])" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def permute_ligne_ou_colonne(table, permutation, axis):\n", + " if axis == 0:\n", + " if len(table.shape) == 2:\n", + " return table[permutation, :]\n", + " return table[list(permutation)]\n", + " return table[:, permutation]\n", + "\n", + "\n", + "permute_ligne_ou_colonne(table1, [2, 1, 0], axis=0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Q5 - Ecrire une fonctionne qui retourne les deux p ermutations ligne/colonne qui minimise la distance entre les deux matrices, en déduire la case qui a changé." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "((1, 0, 2), (1, 0))" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def optimise(table1, table2):\n", + " best = None\n", + " for p1 in permutations(table1.shape[0]):\n", + " for p2 in permutations(table1.shape[1]):\n", + " t = permute_ligne_ou_colonne(permute_ligne_ou_colonne(table1, p1, 0), p2, 1)\n", + " d = distance(t, table2)\n", + " if best is None or d < best:\n", + " best = d\n", + " perms = p1, p2\n", + " return perms\n", + "\n", + "\n", + "optimise(table1, table2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Q6 - Quel est le coût de cette fonction ?\n", + "\n", + "Si $i$ et $j$ sont les dimensions de deux tables, c'est $O((i!)(j!))$." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Q7 - C'est b eaucoup trop long.\n", + "\n", + "On prop ose que calculer chaque p ermutation séparément. On cherche donc la meilleure p ermutation qui minimise la distribution de la somme par ligne et par colonne entre les deux matrices. Ecrire une fonctionne qui implémente ce raisonnement.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "((0, 1), (1, 0, 2))" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def optimise_vecteur(vec1, vec2):\n", + " best = None\n", + " for p1 in permutations(vec1.shape[0]):\n", + " t = permute_ligne_ou_colonne(vec1, p1, 0)\n", + " d = distance(t, vec2)\n", + " if best is None or d < best:\n", + " best = d\n", + " perm = p1\n", + " return perm\n", + "\n", + "\n", + "def optimise_fast(table1, table2):\n", + " return (\n", + " optimise_vecteur(table1.sum(axis=0), table2.sum(axis=0)),\n", + " optimise_vecteur(table1.sum(axis=1), table2.sum(axis=1)),\n", + " )\n", + "\n", + "\n", + "optimise_fast(table1, table2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Le coût est en $O(i!) + O(j!)$. Pas nécessairement optimal mais beaucoup plus rapide." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Q8 - Mais c'est encore trop coûteux.\n", + "\n", + "On cherche la matrice M qui minimise AM =B\n", + "où A et B sont les sommes sur les colonnes où lignes des matrices de statistiques observées sur deux années." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Précisons d'abord ce qu'est une matrice de permutations $M$ : une matrice carrée dont les coefficients sont 0 ou 1. De plus, sur chaque ligne et chaque colonne, on ne trouve qu'un et un seul 1." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "A\n", + "[[0 1]\n", + " [2 3]\n", + " [4 5]]\n", + "M\n", + "[[0 1 0]\n", + " [0 0 1]\n", + " [1 0 0]]\n", + "M @ A\n", + "[[2 3]\n", + " [4 5]\n", + " [0 1]]\n" + ] + } + ], + "source": [ + "M = np.array([[0, 1, 0], [0, 0, 1], [1, 0, 0]])\n", + "A = np.arange(6).reshape((3, -1))\n", + "print(\"A\")\n", + "print(A)\n", + "print(\"M\")\n", + "print(M)\n", + "print(\"M @ A\")\n", + "print(M @ A)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "$MA$ permute les lignes, $AM$ permute les colonnes. Donc trouver la matrice $M$ qui minimise $\\lVert AM - B \\rVert^2$ où $B$ obtenue avec une permutation des colonnes de la matrice $A$ permettrait de déterminer cette permutation. Ce n'est pas un système d'équations en bonne et due forme mais c'en est un." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[ 1.76711027 2.68346008]\n", + " [-1.00760456 -1.85931559]]\n", + "rang 2\n" + ] + } + ], + "source": [ + "M, _, rang, _ = np.linalg.lstsq(table1, table2)\n", + "print(M)\n", + "print(\"rang\", rang)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Le problème est qu'il y a plus d'inconnues que d'équations, d'où le rang faible (2). Avant de revenir à cette option. On part dans une autre direction. La plus grande des catégories de populations a beaucoup de chance d'être la plus grande l'année suivante. Donc la recherche du maximum dans les matrices A et B dévoile une partie de la matrice M. On applique cette idée aux sommes des lignes et des colonnes." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "((0, 1), (2, 0, 1))" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def optimise_vecteur_tri(vec1, vec2):\n", + " pos_vec1 = [(v, i) for i, v in enumerate(vec1)]\n", + " pos_vec1.sort()\n", + " return tuple(p[1] for p in pos_vec1)\n", + "\n", + "\n", + "def optimise_fast_tri(table1, table2):\n", + " return (\n", + " optimise_vecteur_tri(table1.sum(axis=0), table2.sum(axis=0)),\n", + " optimise_vecteur_tri(table1.sum(axis=1), table2.sum(axis=1)),\n", + " )\n", + "\n", + "\n", + "optimise_fast_tri(table1, table2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "On revient au problème d'optimisation : $\\lVert AM - B \\rVert^2$. Il faudrait pouvoir forcer les coefficients de la matrice à être 0 ou 1 en ajoutant une contrainte. On utilise pour cela fonction $f(x)=x(1-X)$ qui vaut 0 quand $x \\epsilon \\{0,1\\}$. On cherche donc $M$ qui minimise $\\lVert AM - B \\rVert^2 + \\lambda \\lVert M^2*(1-M)^2\\rVert$ où $*$ est une multiplication terme à terme. Mais résoudre ce problème n'est pas simple. On en restera là pour le moment." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Q9 - Comment utiliser cette fonction p our implémenter une version plus rapide de la fonction à la question 5." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Le code de la question précédente répond à la question." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Q10 - La troisième année, une colonne est coupée en deux : une catégorie est divisée en deux sous-catégorie. Que proposez-vous p our y remédier ?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "L'idée est assez simple, on choisit au hasard deux lignes de la seconde matrice et on les aggrège. On la fonction précédente pour en déduire les deux permutations les moins coûteuses puis on conserve le coût de cette permutation. On fait pour toutes les paires et on ne garde que la meilleure paire." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "this312", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/_doc/practice/exams/td_note_2025.pdf b/_doc/practice/exams/td_note_2025.pdf new file mode 100644 index 0000000..c9dece4 Binary files /dev/null and b/_doc/practice/exams/td_note_2025.pdf differ diff --git a/_doc/practice/years/2025/index.rst b/_doc/practice/years/2025/index.rst index c0165bb..6381ae4 100644 --- a/_doc/practice/years/2025/index.rst +++ b/_doc/practice/years/2025/index.rst @@ -11,3 +11,4 @@ seance5_algo2 seance6_regex seance7_postier_chinois + seance8_ski diff --git a/_doc/practice/years/2025/seance8_ski.ipynb b/_doc/practice/years/2025/seance8_ski.ipynb new file mode 100644 index 0000000..422d81a --- /dev/null +++ b/_doc/practice/years/2025/seance8_ski.ipynb @@ -0,0 +1,144 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Appariement des paires de skis\n", + "\n", + "On cherche l'appariement optimal entre des skieurs et des paires de skis sachant que les skis les plus appropriés pour un skieur font sa taille. On note $(t_i)_i$ représente l'ensemble des tailles des skieurs et $(s_i)_i$ celui des paires de skis. S'il y a autant de paires que de skieurs, le problème revient à trouver la meilleure permutation $\\sigma$ qui minimise:\n", + "\n", + "$$\\sum_{i=1}^n \\left|t_i - s_{\\sigma(i)}\\right|$$\n", + "\n", + "Dans ce cas, on montre que les paires de skis doivent être triées par ordre de croissant. Pour cela, on raisonne par l'absurde en supposant que cela n'est pas vrai. Donc il existe $i$ et $j$ tels que $t_i < t_j$ et $s_i > s_j$. Si c'est possible, alors :\n", + "\n", + "$$|t_i - s_i| +|t_j - s_j| \\leqslant |t_i - s_j| +|t_j - s_i|$$\n", + "\n", + "Si les paires de skis sont tous les deux plus petites que les tailles de skieurs ou l'inverse, les deux quantités sont égales. En revanche, si $t_i < s_i < t_j$, alors :\n", + "\n", + "$$s_i - t_i +t_j - s_j \\leqslant |t_i - s_j| + t_j - s_i \\Rightarrow s_i - s_j + t_j - t_i \\leqslant |t_i - s_j| + t_j - s_i $$\n", + "\n", + "C'est faux si $t_i < s_j$. C'est aussi faux si $t_i > s_j$ car $s_j < t_i < s_i < t_j$.\n", + "\n", + "On en déduit que l'ordre importe peu lorsque toutes les paires sont plus ou plus petites que tous les skieurs mais que les paires ordonnées associées au skieurs ordonnés est la solution optimale lorsqu'il y a chevauchement.\n", + "\n", + "Dans un premier temps, on implémente une solution par récurrence." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "8GTzJjK3BoQ-", + "outputId": "21016f1f-a3ce-496d-cbae-9f7aa1907d57" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[3, 4, 4, 5, 6, 8]\n" + ] + } + ], + "source": [ + "def appariement(tailles, skis):\n", + " # tailles.sort()\n", + " if len(tailles) == len(skis):\n", + " skis.sort()\n", + " return skis\n", + "\n", + " if len(tailles) == len(skis) - 1:\n", + " # il suffit d'enlever une paire, on les essaye toute jusqu'à trouver\n", + " # celle qui minimise l'appariement\n", + " meilleur = None\n", + " for p in range(len(skis)):\n", + " app = appariement(tailles, skis[:p] + skis[p + 1 :])\n", + " cout = sum([abs(t - s) for t, s in zip(tailles, app)])\n", + " if meilleur is None or cout < meilleur:\n", + " meilleur = cout\n", + " meilleur_appariement = app\n", + " return meilleur_appariement\n", + "\n", + " if len(tailles) <= len(skis) - 2:\n", + " # il suffit d'enlever une paire, on les essaye toute jusqu'à trouver\n", + " # celle qui minimise l'appariement\n", + " meilleur = None\n", + " for p in range(len(skis)):\n", + " app = appariement(tailles, skis[:p] + skis[p + 1 :])\n", + " cout = sum([abs(t - s) for t, s in zip(tailles, app)])\n", + " if meilleur is None or cout < meilleur:\n", + " meilleur = cout\n", + " meilleur_appariement = app\n", + " return meilleur_appariement\n", + " return None\n", + "\n", + "\n", + "tailles = [5, 6, 3, 4, 4, 8]\n", + "skis = [5, 6, 3, 4, 4, 8, 7, 9]\n", + "print(appariement(tailles, skis))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "e-hpvealOWyQ" + }, + "source": [ + "Il existe une solution au coût polynômial. On s'inspire de la distance d'édition. On définit $T_{1..i}$ les i premières tailles de skieurs triées par ordre croissant, $S_{1..j}$ les j premières paires triées par ordre croissant puis $c\\left(T_{1..i}, S_{1..j}\\right)$ le coût d'appariement.\n", + "\n", + "Si on ajoute un skieur, alors dans la meilleure solution, soit il est associé à la dernière paire, soit il ne l'est pas.\n", + "\n", + "$$c\\left(T_{1..i}, S_{1..j}\\right) = \\min\\left\\{\\begin{array}{l}c\\left(T_{1..i-1}, S_{1..j-1}\\right) + |T_i - S_j| \\\\ c\\left(T_{1..i}, S_{1..j-1}\\right) \\end{array}\\right.$$\n", + "\n", + "Il ne reste plus qu'à l'implémenter. L'appariement consiste à constuire le chemin qui permet d'atteindre le coût minimum." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "this312", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/_latex/ensae/td_note_2025.tex b/_latex/ensae/td_note_2025.tex new file mode 100644 index 0000000..150b264 --- /dev/null +++ b/_latex/ensae/td_note_2025.tex @@ -0,0 +1,140 @@ +\documentclass[a4paper,11pt]{article} +%\usepackage[utf8]{inputenc} +\usepackage[french]{babel} +\usepackage[T1]{fontenc} +\usepackage{amsmath} +\usepackage{graphicx} +\usepackage{enumitem} +\usepackage[lmargin=2.5cm,rmargin=2.5cm,tmargin=2cm,bmargin=2.5cm]{geometry} +\usepackage{listings} +\usepackage[dvipsnames]{xcolor} + +\lstdefinestyle{mypython}{ + language=Python, + backgroundcolor=\color{white}, + basicstyle=\ttfamily\footnotesize, + frame=single, + keywordstyle=\color{blue}, + commentstyle=\color{ForestGreen}, + stringstyle=\color{red}, + numbers=left, + numberstyle=\tiny\color{gray}, + stepnumber=1, + tabsize=4, + showstringspaces=false +} + +%\newenvironment{verbatim}{\begin{lstlisting}[style=mypython]}{\end{lstlisting}} +%\newenvironment{verbatim}{\begin{lstlisting}[style=mypython]}{\end{lstlisting}} + +\setlength{\parindent}{0pt} + +\newcounter{question} +\newcommand{\exequest}[1]{\bigskip \stepcounter{question} \textbf{Question \arabic{question}} : #1} + +% Informations sur l'examen +\title{ENSAE TD noté, mercredi 5 novembre 2024} +\date{} + +\begin{document} + +\vspace{-3cm} +\maketitle +\vspace{-1.5cm} + +\medskip +\emph{Toutes les questions valent 2 points.} +\bigskip + +Au pays des schtroumphs, le schtroumph statisticien a décidé d'organiser le recensement +des schtroumphs. La première tout s'est déroulé à la perfection et a abouti +au résultat suivant : + +\\ + +\begin{tabular}{lrrl} +\toprule + & schtroumph & schtroumph & index \\ +\midrule +0 & 8 & 7 & schtroumph \\ +1 & 18 & 18 & schtroumph \\ +2 & 6 & 8 & schtroumph \\ +\bottomrule +\end{tabular} + +\\ + +L'année suivante, le statisticien a délégué le recensement à son apprenti. + +\\ + +\begin{tabular}{lrrl} +\toprule + & schtroumph & schtroumph & index \\ +\midrule +0 & 18 & 18 & schtroumph \\ +1 & 7 & 9 & schtroumph \\ +2 & 8 & 6 & schtroumph \\ +\bottomrule +\end{tabular} + +\\ + +Au début, il s'est demandé comment la population avait tant changé. +Puis il s'est rappelé que la langue schtroumph est une langue parlée. +Tous les mots s'écrivent de la même façon. +Le schroumph statisticien va devoir réconcilier les données. + + +%%%%% +\exequest{Implémenter une fonction qui calcule la distance entre deux matrices} + +\begin{lstlisting}[style=mypython] +def distance(table1, table2): + # ... + return ... + +assert distance(np.array([[0, 1], [0, 1]]), np.array([[0, 1], [0, 1]])) == 0 +\end{lstlisting} + +\exequest{Implémenter une fonction qui retourne les permutations des n premiers entiers} + +\exequest{Implémenter une fonction qui permute les colonnes d'une matrice.} + +\begin{lstlisting}[style=mypython] +def permute_colonne(table, permutation): + # ... + return ... +\end{lstlisting} + +\exequest{Faire de même pour les lignes} + +\begin{lstlisting}[style=mypython] +def permute_ligne(table, permutation): + # ... + return ... +\end{lstlisting} + +Faire de même pour les lignes. + +\exequest{Ecrire une fonctionne qui retourne les deux permutations ligne/colonne +qui minimise la distance entre les deux matrices, en déduire la case qui a changé.} + +\exequest{Quel est le coût de cette fonction ?} + +\exequest{C'est beaucoup trop long. On propose que calculer chaque permutation séparément.} +On cherche donc la meilleure permutation qui minimise la distribution de la somme par ligne +et par colonne entre les deux matrices. +Ecrire une fonctionne qui implémente ce raisonnement. + +\exequest{Mais c'est encore trop coûteux} +On cherche la matrice M qui minimise $AM=B$ où A et B sont les sommes sur les colonnes où lignes +des matrices de statistiques observées sur deux années. + +\exequest{Comment utiliser cette fonction pour implémenter une version plus rapide de la fonction à la question 5.} + +\exequest{La troisième année, une colonne est coupée en deux : une catégorie est divisée en deux sous-catégories.} + +Que proposez-vous pour y remédier ? + +\end{document} diff --git a/_unittests/ut_xrun_doc/test_documentation_notebook.py b/_unittests/ut_xrun_doc/test_documentation_notebook.py index 3ecf8b3..ae714f5 100644 --- a/_unittests/ut_xrun_doc/test_documentation_notebook.py +++ b/_unittests/ut_xrun_doc/test_documentation_notebook.py @@ -111,40 +111,40 @@ def add_test_methods_path(cls, fold, copy_folder=None): for name in found: if name.endswith(".ipynb"): fullname = os.path.join(fold, name) - if "interro_rapide_" in name or ( - sys.platform == "win32" - and ( - "protobuf" in name - or "td_note_2021" in name - or "nb_pandas" in name + reason = None + if not reason and ( + "interro_rapide_" in name + or ( + sys.platform == "win32" + and ( + "protobuf" in name + or "td_note_2021" in name + or "nb_pandas" in name + ) ) ): - - @unittest.skip("notebook with questions or issues with windows") - def _test_(self, fullname=fullname): - res = self.run_test(fullname, verbose=VERBOSE) - self.assertIn(res, (-1, 1)) - - elif "module_file_regex" in name and sys.platform != "win32": - - @unittest.skip("issues with linux") - def _test_(self, fullname=fullname): - res = self.run_test(fullname, verbose=VERBOSE) - self.assertIn(res, (-1, 1)) - - elif ( + reason = "protobuf on windows not working" + if not reason and "seance5_algo2" in name: + reason = "profiling not working on CI" + if ( + not reason + and "module_file_regex" in name + and sys.platform != "win32" + ): + reason = "regex not working on windows" + if not reason and ( "ml_a_tree_overfitting" in name and os.environ.get("CIRCLECI", "undefined") != "undefined" ): + reason = "tree_overfitting too long" + if not reason and "pretraitement_son" in name: + reason = "audio too long" + if not reason and "ml_ml_features_model" in name: + reason = "http request denied" - @unittest.skip("issues with circleci") - def _test_(self, fullname=fullname): - res = self.run_test(fullname, verbose=VERBOSE) - self.assertIn(res, (-1, 1)) - - elif "pretraitement_son" in name: + if reason: - @unittest.skip("audio not working") + @unittest.skip(reason) def _test_(self, fullname=fullname): res = self.run_test(fullname, verbose=VERBOSE) self.assertIn(res, (-1, 1))