From f7813e6a1b21713ee119f18d1de791eea85736c6 Mon Sep 17 00:00:00 2001 From: Quentin Fortier Date: Sun, 5 Nov 2023 20:10:46 +0000 Subject: [PATCH] Add tp_prog_dyn.ipynb --- .../dl/algo/prog_dyn/tp/tp1/tp_prog_dyn.ipynb | 289 ------------------ 1 file changed, 289 deletions(-) diff --git a/files/dl/algo/prog_dyn/tp/tp1/tp_prog_dyn.ipynb b/files/dl/algo/prog_dyn/tp/tp1/tp_prog_dyn.ipynb index 6ffca0fe..cbf7d41e 100644 --- a/files/dl/algo/prog_dyn/tp/tp1/tp_prog_dyn.ipynb +++ b/files/dl/algo/prog_dyn/tp/tp1/tp_prog_dyn.ipynb @@ -25,18 +25,6 @@ "**Question** : Que peut-on prendre comme cas de base ?" ] }, - { - "cell_type": "markdown", - "metadata": { - "tags": [ - "cor" - ] - }, - "source": [ - "$$\\binom{0}{k} = 0$$\n", - "$$\\binom{n}{0} = 1, \\text{si } n \\neq 0$$" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -44,24 +32,6 @@ "**Question** : Écrire une fonction récursive `binom_rec(n, k)` renvoyant $\\binom{n}{k}$ à partir de la formule ci-dessus. Expliquer pourquoi la complexité de cette fonction est très mauvaise." ] }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "tags": [ - "cor" - ] - }, - "outputs": [], - "source": [ - "def binom_rec(n, k): # voir cours\n", - " if k == 0:\n", - " return 1\n", - " if n == 0:\n", - " return 0\n", - " return binom_rec(n - 1, k - 1) + binom_rec(n - 1, k)" - ] - }, { "cell_type": "code", "execution_count": 2, @@ -106,27 +76,6 @@ " return ..." ] }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "tags": [ - "cor" - ] - }, - "outputs": [], - "source": [ - "def binom_dp(n, k):\n", - " M = [[0]*(k + 1) for _ in range(n + 1)]\n", - " for i in range(0, n + 1):\n", - " M[i][0] = 1 # cas de base\n", - "\n", - " for i in range(1, n + 1):\n", - " for j in range(1, k + 1):\n", - " M[i][j] = M[i - 1][j - 1] + M[i - 1][j]\n", - " return M[n][k]" - ] - }, { "cell_type": "code", "execution_count": 5, @@ -168,27 +117,6 @@ " return aux(n, k)" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "cor" - ] - }, - "outputs": [], - "source": [ - "def binom(n, k):\n", - " d = {}\n", - " def aux(i, j):\n", - " if j == 0: return 1\n", - " if i == 0: return 0\n", - " if (i, j) not in d:\n", - " d[(i, j)] = aux(i - 1, j - 1) + aux(i - 1, j)\n", - " return d[(i, j)]\n", - " return aux(n, k)" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -216,24 +144,6 @@ "Comme on ne sait pas si $a_k$ est utilisée ou non, on a dans le cas général : $r(n, k) = \\min(..., ...)$." ] }, - { - "cell_type": "markdown", - "metadata": { - "tags": [ - "cor" - ] - }, - "source": [ - "Si $a_k$ est utilisée : il faut encore rendre $n - a_k$ euros avec les pièces $a_1$, ..., $a_k$ (on a le droit d'utiliser plusieurs fois $a_k$), d'où $r(n, k) = r(n - a_k, k) + 1$.\n", - "\n", - "Dans le cas général, on considère les deux possibilités et on conserve le minimum : \n", - "$$\n", - " r(n, k) = min(r(n, k - 1), r(n - a_k, k) + 1)\n", - "$$\n", - "\n", - "Remarque : on ne peut utiliser $a_k$ pour rendre $n$ euros que si $n \\geq a_k$. Si $n < a_k$, on a donc $r(n, k) = r(n, k - 1)$." - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -242,29 +152,6 @@ "On remplira une matrice `M` pour que `M[i][j]` contienne le nombre minimum de pièces pour rendre `i` euros en utilisant les `j` premières pièces de `L`." ] }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "tags": [ - "cor" - ] - }, - "outputs": [], - "source": [ - "def rendu(L, n):\n", - " k = len(L) # nombre de pièces\n", - " M = [[0]*(k + 1) for _ in range(n + 1)]\n", - " for i in range(1, n + 1):\n", - " M[i][0] = float(\"inf\")\n", - " for j in range(1, k + 1):\n", - " if i - L[j - 1] >= 0:\n", - " M[i][j] = min(M[i][j - 1], 1 + M[i - L[j - 1]][j])\n", - " else:\n", - " M[i][j] = M[i][j - 1]\n", - " return M[-1][-1]" - ] - }, { "cell_type": "code", "execution_count": 8, @@ -292,46 +179,6 @@ "**Question** : Réécrire la fonction précédente par mémoïsation plutôt que par programmation dynamique." ] }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "tags": [ - "cor" - ] - }, - "outputs": [ - { - "data": { - "text/plain": [ - "2" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "def rendu_memo(L, n):\n", - " k = len(L)\n", - " d = {}\n", - " def aux(i, j):\n", - " if (i, j) in d:\n", - " return d[(i, j)]\n", - " if i == 0:\n", - " return 0\n", - " if j == 0:\n", - " return float(\"inf\")\n", - " if i - L[j - 1] >= 0:\n", - " d[(i, j)] = min(aux(i, j - 1), 1 + aux(i - L[j - 1], j))\n", - " else:\n", - " d[(i, j)] = aux(i, j - 1)\n", - " return d[(i, j)]\n", - " return aux(n, k)\n", - "rendu_memo([1, 2, 5], 7)" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -360,19 +207,6 @@ "**Question** : Définir `M` en Python." ] }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "tags": [ - "cor" - ] - }, - "outputs": [], - "source": [ - "M = [[1, 0, 0, 0], [0, 0, 1, 1], [0, 1, 1, 1], [0, 1, 0, 1]]" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -387,24 +221,6 @@ "**Question** : Écrire une fonction `est_carre` telle que `est_carre(m, x, y, k)` détermine si la sous-matrice de `m` de taille $k \\times k$ et dont la case en haut à gauche a pour coordonnées (`x`, `y`) ne possède que des 1." ] }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "tags": [ - "cor" - ] - }, - "outputs": [], - "source": [ - "def est_carre(M, x, y, k):\n", - " for i in range(x, x + k):\n", - " for j in range(y, y + k):\n", - " if M[i][j] != 1:\n", - " return False\n", - " return True" - ] - }, { "cell_type": "code", "execution_count": 11, @@ -421,25 +237,6 @@ "**Question** : Écrire une fonction `contient_carre` telle que `contient_carre(m, k)` renvoie `true` si `m` contient un carré de 1 de taille $k$, `false` sinon." ] }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "tags": [ - "cor" - ] - }, - "outputs": [], - "source": [ - "def contient_carre(M, k):\n", - " n = len(M)\n", - " for i in range(n - k + 1):\n", - " for j in range(n - k + 1):\n", - " if est_carre(M, i, j, k):\n", - " return True\n", - " return False" - ] - }, { "cell_type": "code", "execution_count": 12, @@ -456,24 +253,6 @@ "**Question** : Écrire une fonction `max_carre1` telle que `max_carre1(m)` renvoie la taille maximum d'un carré de 1 contenu dans `m`." ] }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "tags": [ - "cor" - ] - }, - "outputs": [], - "source": [ - "def max_carre1(M):\n", - " n = len(M)\n", - " for k in range(n, 0, -1):\n", - " if contient_carre(M, k):\n", - " return k\n", - " return 0" - ] - }, { "cell_type": "code", "execution_count": 14, @@ -501,19 +280,6 @@ "**Question** : Quelle est la complexité de `max_carre1(m)` dans le pire cas ?" ] }, - { - "cell_type": "markdown", - "metadata": { - "tags": [ - "cor" - ] - }, - "source": [ - "- `est_carre(M, x, y, k)` est en $O(k^2)$. \n", - "- `contient_carre(M, k)` appelle O($n$) fois `est_carre`, donc est en $O(n^2 k^2)$. \n", - "- `max_carre1(M)` appelle `contient_carre` pour $k = 1, 2, ..., n$, donc est de complexité $\\sum_{k=1}^n O(n^2 k^2) = O(n^3 \\sum_{k=1}^n k^2)$. Comme $\\sum_{k=1}^n k^2 = \\frac{n(n+1)(2n+1)}{6} = O(n^3)$, la complexité totale est $\\boxed{O(n^6)}$.`" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -529,19 +295,6 @@ "**Question** : Que vaut `c[0][y]` et `c[x][0]` ?" ] }, - { - "cell_type": "markdown", - "metadata": { - "tags": [ - "cor" - ] - }, - "source": [ - "`c[0][y] = 0` si `m[0][y] = 0` et `c[0][y] = 1` sinon. \n", - "De même pour `c[x][0]`. \n", - "Remarque : `c[0][y]` et `c[x][0]` sont donc les mêmes valeurs que `m[0][y]` et `m[x][0]`, on peut donc initialiser `c` comme une copie de `m`." - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -549,17 +302,6 @@ "**Question** : Que vaut `c[x][y]` si `m[x][y] = 0` ?" ] }, - { - "cell_type": "markdown", - "metadata": { - "tags": [ - "cor" - ] - }, - "source": [ - "`c[x][y] = 0`." - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -574,25 +316,6 @@ "**Question** : En déduire une fonction `max_carre2` telle que `max_carre2(m)` renvoie la taille maximum d'un carré de 1 contenu dans `m`, ainsi que les coordonnées de la case en haut à gauche d'un tel carré." ] }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "tags": [ - "cor" - ] - }, - "outputs": [], - "source": [ - "def max_carre2(m):\n", - " c = m.copy()\n", - " for i in range(len(m)):\n", - " for j in range(len(m[0])):\n", - " if m[i][j] == 1:\n", - " c[i][j] = 1 + min(c[i - 1][j], c[i][j - 1], c[i - 1][j - 1])\n", - " return max(max(l) for l in c)" - ] - }, { "cell_type": "code", "execution_count": 16, @@ -620,18 +343,6 @@ "**Question** : Quelle est la complexité de `max_carre2(m)`, en fonction des dimensions de `m`? Comparer avec `max_carre1(m)`." ] }, - { - "cell_type": "markdown", - "metadata": { - "tags": [ - "cor" - ] - }, - "source": [ - "`max_carre2(m)` est en $\\boxed{O(n^2)}$ à cause des deux boucles `for` imbriquées. \n", - "C'est donc beaucoup mieux que `max_carre1(m)` qui est en $O(n^6)$." - ] - }, { "cell_type": "markdown", "metadata": {},