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..bd56c5e --- /dev/null +++ b/_doc/practice/exams/td_note_2025.ipynb @@ -0,0 +1,309 @@ +{ + "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" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.int64(45)" + ] + }, + "execution_count": 3, + "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" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "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": 6, + "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" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 6, 8],\n", + " [18, 18],\n", + " [ 8, 7]])" + ] + }, + "execution_count": 23, + "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" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "((1, 0, 2), (1, 0))" + ] + }, + "execution_count": 24, + "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\n", + "\n", + "Si $i$ et $j$ sont les dimensions de deux tables, c'est $O((i!)(j!))$." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Q7" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "((0, 1), (1, 0, 2))" + ] + }, + "execution_count": 25, + "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": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "\n", + "coef = np.random.rand(5).reshape((-1, 1))\n", + "coef[:, 0] = 1\n", + "print(coef)\n", + "t1 = np.ones((5, 1)) @ np.array([[5, 1, 2, 3, 4]], dtype=np.float32)\n", + "t2 = np.ones((5, 1)) @ np.array([[2, 3, 1, 4, 5]], dtype=np.float32)\n", + "M = np.array(\n", + " [\n", + " [0, 0, 1, 0, 0],\n", + " [0, 0, 0, 1, 0],\n", + " [0, 1, 0, 0, 0],\n", + " [0, 0, 0, 0, 1],\n", + " [1, 0, 0, 0, 0],\n", + " ]\n", + ").T\n", + "\n", + "# Il faut diminuer le nombre de solutions pour n'en garder qu'une.\n", + "for i in range(5):\n", + " t1[i, t1[i, :] == i] = i + 0.01\n", + " t2[i, t2[i, :] == i] = i + 0.01\n", + "\n", + "assert (t1 @ M - t2).max() < 1e-6\n", + "\n", + "m = np.linalg.lstsq(t1, t2)\n", + "print(t1)\n", + "print(t2)\n", + "print(M)\n", + "print((m[0] * 100).astype(int) / 100)" + ] + } + ], + "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}