diff --git a/.gitignore b/.gitignore
index 581c686..02118f6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,7 @@
*.dbf
*.xlsx
*.pickle
+*.dvi
*.shp
*.shx
*.png
diff --git a/_doc/articles/2024/anim_evolution.png b/_doc/articles/2024/anim_evolution.png
new file mode 100644
index 0000000..e70b2d0
Binary files /dev/null and b/_doc/articles/2024/anim_evolution.png differ
diff --git a/_doc/articles/2024/pyramide.png b/_doc/articles/2024/pyramide.png
new file mode 100644
index 0000000..44953cb
Binary files /dev/null and b/_doc/articles/2024/pyramide.png differ
diff --git a/_doc/articles/2024/tsp_simple.png b/_doc/articles/2024/tsp_simple.png
new file mode 100644
index 0000000..7cb518f
Binary files /dev/null and b/_doc/articles/2024/tsp_simple.png differ
diff --git a/_doc/articles/2024/vie_1.png b/_doc/articles/2024/vie_1.png
new file mode 100644
index 0000000..1fa4a52
Binary files /dev/null and b/_doc/articles/2024/vie_1.png differ
diff --git a/_doc/articles/2025/2025-03-01-route2025.rst b/_doc/articles/2025/2025-03-01-route2025.rst
new file mode 100644
index 0000000..f9bb2b7
--- /dev/null
+++ b/_doc/articles/2025/2025-03-01-route2025.rst
@@ -0,0 +1,25 @@
+.. _l-feuille-route-2025:
+
+==================================
+2025-03-01 : feuille de route 2025
+==================================
+
+Séance 1 (31/1)
+===============
+
+* site web : `sdpython.github.io `_
+* :ref:`l-ml-rappel`
+* programmation (python :epkg:`numpy`, :epkg:`pandas`, :epkg:`matplotlib`, :epkg:`jupyter`)
+* :ref:`Tests unitaires `, package python
+* `SQL `_
+* `CPU `_,
+ `CUDA `_
+* machine learning, :epkg:`scikit-learn`, :epkg:`pytorch`
+* `comparaison torch/scikit-learn `_
+* :ref:`l-regclass`
+* évaluation, :ref:`ROC `, :math:`R^2`
+* ranking, clustering
+* `ChatGPT `_,
+ `LLM `_,
+ (Large Language Model), SLLM (Small LLM)
+* Coder avec un LLM en local.
diff --git a/_doc/articles/index.rst b/_doc/articles/index.rst
index 9960bcf..fcfa86a 100644
--- a/_doc/articles/index.rst
+++ b/_doc/articles/index.rst
@@ -5,6 +5,12 @@ Collections d'articles périssables
Ou *blog*.
+.. toctree::
+ :caption: 2025
+ :maxdepth: 1
+
+ 2025/2025-03-01-route2025
+
.. toctree::
:caption: 2024
:maxdepth: 1
diff --git a/_doc/practice/exams.rst b/_doc/practice/exams.rst
index 776d5f4..1a1895a 100644
--- a/_doc/practice/exams.rst
+++ b/_doc/practice/exams.rst
@@ -3,7 +3,8 @@ Exercices minutés
=================
Ces exercices ont été proposés pour examens d'évaluation.
-Il est recommandé de savoir les faire en moins de deux heures.
+Il est recommandé de savoir les faire en moins de deux heures pour les premiers
+et une heure et demie pour les derniers.
.. _l-examens-notes:
@@ -37,6 +38,7 @@ Enoncés
* :download:`td_note_2023 `
* :download:`td_note_2023-2024 `
* :download:`td_note_2023-2024_rattrapage `
+* :download:`td_note_2024 `
Corrections
+++++++++++
@@ -64,6 +66,7 @@ Corrections
exams/td_note_2023
exams/td_note_2023-2024
exams/td_note_2023-2024_rattrapage
+ exams/td_note_2024
Exercices courts
================
diff --git a/_doc/practice/exams/td_note_2024.ipynb b/_doc/practice/exams/td_note_2024.ipynb
new file mode 100644
index 0000000..8848c5a
--- /dev/null
+++ b/_doc/practice/exams/td_note_2024.ipynb
@@ -0,0 +1,406 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 1A - Enoncé 6 novembre 2024\n",
+ "\n",
+ "Correction de l'examen du 6 novembre 2024.\n",
+ "\n",
+ "Toutes les questions valent 2 points."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Aparté dessin\n",
+ "\n",
+ "Cette partie ne fait pas partie de l'énoncé."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 8,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "image/png": "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",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "import matplotlib.pyplot as plt\n",
+ "\n",
+ "\n",
+ "def dessin(n=4):\n",
+ " fig, ax = plt.subplots(1, 1, figsize=(3, 3))\n",
+ " ax.plot([-1, n + 1], [-1, n + 1], \"w\", linewidth=1)\n",
+ " for i in range(0, n + 1):\n",
+ " ax.plot([i, i], [0, n], \"b-\", linewidth=4)\n",
+ " ax.plot([0, n], [i, i], \"b-\", linewidth=4)\n",
+ " ax.plot([-1, 0], [n + 1, n], \"ro-\", linewidth=4, ms=8)\n",
+ " ax.text(0, n + 0.1, \"A\", fontsize=20)\n",
+ " ax.get_xaxis().set_visible(False)\n",
+ " ax.get_yaxis().set_visible(False)\n",
+ " fig.patch.set_visible(False)\n",
+ " return ax\n",
+ "\n",
+ "\n",
+ "dessin()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Exercice 1 : optimization\n",
+ "\n",
+ "25 maisons sont positionnées aux 25 intersections du quadrillage ci-dessus. Le courant (rouge) arrive à un angle du carré (point A). Il faut relier chaque maison au courant pour un coût minimal. Pour cela il faut tirer un câble entre chaque intersection au point A. Les câbles ne peuvent passer que par les routes (les lignes du quadrillage), pas de diagonales donc.\n",
+ "\n",
+ "\n",
+ "### Q1 : implémenter une fonction qui calcule la distance L1\n",
+ "\n",
+ "$d(x_1,y_1,x_2,y_2) = |x_1 - x_2| + |y_1 - y_2|$"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def distance(x1, y1, x2, y2):\n",
+ " return abs(x1 - x2) + abs(y1 - y2)\n",
+ "\n",
+ "\n",
+ "assert distance(0, 0, 3, 4) == 7"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Q2: calculer la longueur de câble pour relier les 25 maisons.\n",
+ "\n",
+ "Il y a deux façons de faire cela, une façon gloutonne ou une autre plus rapide mais qui nécessite d'utiliser une série des sommes de Gauss : la somme est $n$ premiers entiers est $\\frac{n(n+1)}{2}$. On considère la façon gloutonne d'abord."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def longueur_cable(n=5):\n",
+ " total = 0\n",
+ " for i in range(0, n):\n",
+ " for j in range(0, n):\n",
+ " total += distance(0, 0, i, j)\n",
+ " return total\n",
+ "\n",
+ "\n",
+ "assert longueur_cable(5) == 100"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "La façon non gloutonne mais pas appropriée pour la suite."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def longueur_cable_gauss(n=5):\n",
+ " somme_longueur = (n - 1) * n / 2 # on part de 0 à 4\n",
+ " total = n * somme_longueur + n * somme_longueur\n",
+ " return total\n",
+ "\n",
+ "\n",
+ "assert longueur_cable_gauss(5) == 100"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Q3: même calcul pour 36, 49, 72 maisons (carrés de côté 6, 7, un rectangle 8x9) ?"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def longueur_cable(longueur, largeur):\n",
+ " total = 0\n",
+ " for i in range(0, longueur):\n",
+ " for j in range(0, largeur):\n",
+ " total += distance(0, 0, i, j)\n",
+ " return total\n",
+ "\n",
+ "\n",
+ "assert longueur_cable(6, 6) == 180\n",
+ "assert longueur_cable(7, 7) == 294\n",
+ "assert longueur_cable(8, 9) == 540"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Q4: avec deux câbles\n",
+ "\n",
+ "On dispose de deux câbles : \n",
+ "\n",
+ "* un câble ne pouvant relier qu'une maison avec un coût $c_1$ par mètre\n",
+ "* un câble ne pouvant relier qu'une ou deux maisons avec un coût $c_2$ par mètre\n",
+ "\n",
+ "Par conséquent, on peut relier une maison *M1* avec un câble $c_2$ puis relier *M1* à une autre maison *M2* avec un câble $c_1$. On veut savoir quand utiliser tel ou tel câble pour minimiser les coûts.\n",
+ "\n",
+ "Ecrire une fonction qui retourne le coût du câblage décrit ci-dessus.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def cout_cablage(x1, y1, x2, y2, c1, c2):\n",
+ " return distance(0, 0, x1, y1) * c2 + distance(x1, y1, x2, y2) * c1\n",
+ "\n",
+ "\n",
+ "assert cout_cablage(1, 2, 2, 4, 1, 1.5) == 7.5"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Q5: que fait le code suivant et que montre-t-il ?"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n",
+ "[5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 10.0, 12.5, 15.0, 17.5]\n"
+ ]
+ }
+ ],
+ "source": [
+ "def position_m1(n, c1, c2):\n",
+ " x = []\n",
+ " y = []\n",
+ " for i in range(2 * n):\n",
+ " x.append(i)\n",
+ " c = cout_cablage(0, i, 0, n, c1, c2)\n",
+ " y.append(c)\n",
+ " return x, y\n",
+ "\n",
+ "\n",
+ "x, y = position_m1(5, 1, 1.5)\n",
+ "print(x)\n",
+ "print(y)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Le code calcule le coût du câblage pour une maison mobile M1 et une maison M2 fixe. Il montre que plus la maison M1 est loin de A, plus le coût est grand. Minimiser les coûts revient à minimiser la distance A jusque M1."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Q6,7: idée d'algorithme\n",
+ "\n",
+ "* on prend une maison non câblée M1 la plus proche de A\n",
+ "* on prend ensuite une maison non câblée M2 la plus proche du coin opposé\n",
+ "* on continue jusque à la fin\n",
+ "\n",
+ "On crée une matrice *M*, $n \\times n$, initilialisé à -1. La maison 0,0 est relié avec le câble 0: $M[i,j] =$ le numéro du câble qui la relie.\n",
+ "\n",
+ "Ecrire une fonction qui retourne la maison la plus proche d'une position $(i,j)$ et non câblée."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "\n",
+ "\n",
+ "def init(n=5):\n",
+ " M = np.zeros((n, n)) - 1\n",
+ " M[0, 0] = 0\n",
+ " return M\n",
+ "\n",
+ "\n",
+ "def maison_proche(M, x, y):\n",
+ " best = None\n",
+ " meilleur = None, None\n",
+ " for i in range(0, M.shape[0]):\n",
+ " for j in range(0, M.shape[1]):\n",
+ " if M[i, j] >= 0:\n",
+ " continue\n",
+ " d = distance(x, y, i, j)\n",
+ " if best is None or d < best:\n",
+ " best = d\n",
+ " meilleur = i, j\n",
+ " return meilleur\n",
+ "\n",
+ "\n",
+ "M = init()\n",
+ "assert maison_proche(M, 0, 0) == (0, 1)\n",
+ "M[0, 1] = 1\n",
+ "M[1, 0] = 2\n",
+ "M[1, 1] = 3\n",
+ "assert maison_proche(M, 0, 1) == (0, 2)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Q8: on veut tirer un nouveau câble\n",
+ "\n",
+ "Ecrire une fonction qui tire un nouveau câble :\n",
+ "\n",
+ "* on prend une maison proche de A\n",
+ "* on met à jour la matrice M\n",
+ "* on prend une maison proche du coin opposé\n",
+ "* on met à jour la matrice M\n",
+ "* une retourne le coût"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 33,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def nouveau_cable(M, c1, c2):\n",
+ " numero = int(M.max() + 1)\n",
+ " m1 = maison_proche(M, 0, 0)\n",
+ " M[m1] = numero\n",
+ " m2 = maison_proche(M, *M.shape)\n",
+ " M[m2] = numero\n",
+ " c = cout_cablage(*m1, *m2, c1, c2)\n",
+ " return m1, m2, c\n",
+ "\n",
+ "\n",
+ "M = init()\n",
+ "g = nouveau_cable(M, 1, 1.5)\n",
+ "assert g == ((0, 1), (4, 4), 8.5)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Q9: on complète l'algorithme\n",
+ "\n",
+ "Il suffit d'une boucle pour câbler toutes les maisons. La trouverez-vous ?"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 36,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[((0, 1), (4, 4), 8.5), ((1, 0), (3, 4), 7.5), ((0, 2), (4, 3), 8.0), ((1, 1), (2, 4), 7.0), ((2, 0), (3, 3), 7.0), ((0, 3), (4, 2), 9.5), ((1, 2), (1, 4), 6.5), ((2, 1), (2, 3), 6.5), ((3, 0), (3, 2), 6.5), ((0, 4), (4, 1), 13.0), ((1, 3), (2, 2), 8.0), ((3, 1), (4, 0), 8.0)]\n"
+ ]
+ }
+ ],
+ "source": [
+ "def algorithme_cablage(n, c1, c2):\n",
+ " M = init(n)\n",
+ " n_cables = n * n // 2\n",
+ " cables = []\n",
+ " for i in range(0, n_cables):\n",
+ " cable = nouveau_cable(M, c1, c2)\n",
+ " cables.append(cable)\n",
+ " return cables\n",
+ "\n",
+ "\n",
+ "print(algorithme_cablage(5, 1, 1.5))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Q10: L'algorithme a un défaut, trouverez-vous lequel ?\n",
+ "\n",
+ "La fonction ``nouveau_cable`` choisit la première maison de façon optimale et ensuite choisit l'extrémité en fonction d'un point qui ne dépend de la première maison. Il faudrait choisir une maison $M2$ à la fois éloignée de A et proche de $M1$ mais cela ne suffira sans doute pas. Une fois l'algorithme achevé, il sera sans doute possible d'améliorer le câblage en permutant les maisons M2 entre elles :\n",
+ "\n",
+ "Soit deux câbles ``A -> M1 -> M2`` et ``A -> N1 -> N2``, si ``cout(A -> M1 -> N2) < cout(A -> N1 -> M2)`` alors on peut permuter M2 et N2."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "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.10.12"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
\ No newline at end of file
diff --git a/_doc/practice/exams/td_note_2024.pdf b/_doc/practice/exams/td_note_2024.pdf
new file mode 100644
index 0000000..0f28cff
Binary files /dev/null and b/_doc/practice/exams/td_note_2024.pdf differ
diff --git a/_doc/practice/exams/td_note_2024.png b/_doc/practice/exams/td_note_2024.png
new file mode 100644
index 0000000..4ace638
Binary files /dev/null and b/_doc/practice/exams/td_note_2024.png differ
diff --git a/_latex/ensae/package.tex b/_latex/ensae/package.tex
index ace1099..907c98b 100644
--- a/_latex/ensae/package.tex
+++ b/_latex/ensae/package.tex
@@ -51,6 +51,7 @@
\usepackage{tabularx}
\usepackage{placeins}
\usepackage{url}
+
\urlstyle{sf}
diff --git a/_latex/ensae/td_note_2024.png b/_latex/ensae/td_note_2024.png
new file mode 100644
index 0000000..4ace638
Binary files /dev/null and b/_latex/ensae/td_note_2024.png differ
diff --git a/_latex/ensae/td_note_2024.tex b/_latex/ensae/td_note_2024.tex
new file mode 100644
index 0000000..e0a1bb9
--- /dev/null
+++ b/_latex/ensae/td_note_2024.tex
@@ -0,0 +1,216 @@
+\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 6 novembre 2024}
+\date{}
+
+\begin{document}
+
+\vspace{-3cm}
+\maketitle
+\vspace{-1.5cm}
+
+\textit{L'accès à internet est autorisé mais l'utilisation d'intelligences artificielle génératives de type ChatGPT est interdite.}
+
+\medskip
+\emph{Toutes les questions valent 2 points.}
+\bigskip
+
+\begin{center}
+\includegraphics[width=0.25\textwidth]{td_note_2024.png}
+\end{center}
+
+25 maisons sont positionnées aux 25 intersections du quadrillage ci-dessus.
+Le courant (rouge) arrive à un angle du carré (point A).
+Il faut relier chaque maison au courant pour un coût minimal.
+Pour cela il faut tirer un câble entre le point A et chaque intersection.
+Les cibles ne peuvent passer que par les routes (les lignes du quadrillage),
+pas de diagonales donc.
+
+\emph{Les assertions du sujets (exprimées avec le mot clé \texttt{assert}) doivent être satisfaites par vos solutions.}
+
+%%%%%
+\exequest{Implémenter une fonction qui calcule la distance L1.}
+
+La distance L1 est définie par $d(x_1,y_1,x_2,y_2) = |x_1 - x_2| + |y_1 - y_2|$.
+
+\begin{lstlisting}[style=mypython]
+def distance(x1, y1, x2, y2):
+ # ...
+ return ...
+
+assert distance(0, 0, 3, 4) == 7
+\end{lstlisting}
+
+%%%%%
+\exequest{Calculer la longueur de câble pour relier les 25 maisons.}
+
+\begin{lstlisting}[style=mypython]
+def longueur_cable(n=5):
+ # ...
+
+assert longueur_cable(5) == 100
+\end{lstlisting}
+
+On suppose dans la suite de l'énoncé que le quadrillage reste carré.
+
+%%%%%
+\exequest{Adapter la fonction pour un rectangle 8x9.}
+
+\begin{lstlisting}[style=mypython]
+assert longueur_cable(8, 9) == 540
+\end{lstlisting}
+
+%%%%%
+\exequest{Avec deux câbles...}
+
+On dispose de deux câbles :
+
+\begin{enumerate}
+\item un câble ne pouvant relier qu'une maison avec un coût $c_1$ par mètre (un quadrillage $n\times m$ est de dimension $n$ mètres par $m$ mètres)
+\item un câble ne pouvant relier qu'une ou deux maisons avec un coût $c_2$ par mètre
+\end{enumerate}
+
+Par conséquent, on peut relier une maison \textit{M1} avec un câble $c_2$ puis relier
+\textit{M1} à une autre maison \textit{M2} avec un câble $c_1$.
+On veut savoir quand utiliser tel ou tel câble pour minimiser les coûts.
+
+Ecrire une fonction qui retourne le coût du câblage décrit ci-dessus.
+
+\begin{lstlisting}[style=mypython]
+def cout_cablage(x1,y1, x2,y2, c1, c2):
+ # ...
+
+assert cout_cablage(1,2, 2,4, 1, 1.5) == 7.5
+\end{lstlisting}
+
+%%%%%
+\exequest{Que fait le code suivant et que montre-t-il ?}
+
+\begin{lstlisting}[style=mypython]
+def position_m1(n, c1, c2):
+ x = []
+ y = []
+ for i in range(2*n):
+ x.append(i)
+ y.append(cout_cablage(0,i, 0,n, c1, c2))
+ return x, y
+
+x, y = position_m1(5, 1, 1.5)
+print(x) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+print(y) # [5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 10.0, 12.5, 15.0, 17.5]
+\end{lstlisting}
+
+%%%%%
+\exequest{Idée d'algorithme}
+
+\begin{enumerate}
+\item On prend une maison non câblée M1 la plus proche de A.
+\item On prend ensuite une maison non câblée M2 la plus proche du coin opposé
+\item On continue jusqu'à la fin.
+\end{enumerate}
+
+On crée une matrice $M$, $n \times n$ telle que $M[i][j]$ représente le numéro du câble ($1$ pour $c_1$ et $2$ pour $c_2$) qui relie la maison à l'intersection $i, j$ (sans information sur l'origine du cable). La maison 0, 0 est initialement reliée avec le câble 0, les autres valeurs sont initialisées à -1.
+
+Écrire une fonction qui initialise la matrice.
+
+\begin{lstlisting}[style=mypython]
+def init(n=5):
+ # ...
+\end{lstlisting}
+
+%%%%%
+\exequest{Ecrire une fonction qui retourne la maison la plus proche d'une position $(i,j)$ et non câblée.}
+
+\begin{lstlisting}[style=mypython]
+def maison_proche(M, i, j):
+ # ...
+
+M = init()
+assert maison_proche(M, 0,0) == (0, 1)
+M[0][1] = 1
+M[1][0] = 2
+M[1][1] = 3
+assert maison_proche(M,0,1) == (0, 2)
+\end{lstlisting}
+
+
+%%%%%
+\exequest{On veut tirer un nouveau câble comme suit :}
+
+\begin{enumerate}
+\item on prend la maison $m_1$ la plus proche de A non cablée, on la cable avec le câble $c_2$
+\item on met à jour la matrice M
+\item on prend une maison proche du coin opposé (non câblée), qu'on cable avec $c_1$ depuis $m_1$
+\item on met à jour la matrice M
+\item on renvoie le coût de ces deux câbles ainsi que les deux maisons choisies
+\end{enumerate}
+
+Écrire une fonction qui tire un nouveau câble de cette manière :
+
+\begin{lstlisting}[style=mypython]
+def nouveau_cable(M, c1, c2):
+ # ...
+
+M = init()
+g = nouveau_cable(M, 1, 1.5)
+assert g == ((0, 1), (4, 4), 8.5)
+\end{lstlisting}
+
+
+%%%%%
+\exequest{Terminer l'algorithme.}
+
+Il suffit d'une boucle pour câbler toutes les maisons. La trouverez-vous ?
+
+\begin{lstlisting}[style=mypython]
+def algorithme_cablage(n, c1, c2):
+ M = init(n)
+ n_cables = n * n // 2
+ cables = []
+ # for ...
+ # ...
+ return cables
+
+print(algorithme_cablage(5, 1, 1.5))
+\end{lstlisting}
+
+
+%%%%%
+\exequest{L'algorithme a un défaut, trouverez-vous lequel ?}
+
+
+
+\end{document}
diff --git a/pyproject.toml b/pyproject.toml
index 09c0d00..ce9709e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -160,7 +160,12 @@ select = [
]
[tool.ruff.lint.per-file-ignores]
-"**" = ["C401", "C408", "C413", "RUF012", "RUF100", "RUF010", "SIM108", "SIM102", "SIM114", "SIM103", "UP015", "UP027", "UP031", "UP034", "UP032"]
+"**" = [
+ "C401", "C408", "C413",
+ "RUF012", "RUF100", "RUF010",
+ "SIM108", "SIM102", "SIM114", "SIM103", "SIM910",
+ "UP015", "UP027", "UP031", "UP034", "UP032"
+]
"**/plot*.py" = ["B018", "B010"]
"_doc/conf.py" = ["F821", "E501"]
"teachpyx/__init__.py" = ["E501"]