From 4ad44b7993990173d8294df982b2a89e2ea7881e Mon Sep 17 00:00:00 2001 From: Quentin Fortier Date: Sun, 12 Nov 2023 19:41:17 +0000 Subject: [PATCH 1/3] 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": {}, From e6f9782fd39fd13933726b0af38828361a5f483e Mon Sep 17 00:00:00 2001 From: Quentin Fortier Date: Mon, 13 Nov 2023 09:53:53 +0100 Subject: [PATCH 2/3] Update _toc.yml --- _toc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_toc.yml b/_toc.yml index 2546adf3..62fa79d3 100644 --- a/_toc.yml +++ b/_toc.yml @@ -35,5 +35,5 @@ parts: file: algo/prog_dyn/td/matrice_prog_dyn.pdf - tp: algo/prog_dyn/tp/tp1/tp_prog_dyn.ipynb - tp: algo/prog_dyn/tp/tp2/tp_sac_dos.ipynb - - tp: algo/prog_dyn/seam_carving/seam_carving.ipynb + - cor: algo/prog_dyn/seam_carving/seam_carving.ipynb root: intro From 5473564bbe288d958e14e00774620e4d0c00ca44 Mon Sep 17 00:00:00 2001 From: Quentin Fortier Date: Mon, 13 Nov 2023 08:54:43 +0000 Subject: [PATCH 3/3] Add seam_carving.ipynb --- .../prog_dyn/seam_carving/seam_carving.ipynb | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/files/dl/algo/prog_dyn/seam_carving/seam_carving.ipynb b/files/dl/algo/prog_dyn/seam_carving/seam_carving.ipynb index ead7cda4..8df721b2 100644 --- a/files/dl/algo/prog_dyn/seam_carving/seam_carving.ipynb +++ b/files/dl/algo/prog_dyn/seam_carving/seam_carving.ipynb @@ -124,6 +124,35 @@ "**Question** : Écrire une fonction `matrice_gradient(m)` qui renvoie `g` à partir de `m`. On pourra utiliser, au choix, une liste de listes ou un tableau `numpy` (en utilisant, par exemple, `np.zeros((..., ...))` pour créer `g`). On pourra utiliser `abs` pour la valeur absolue." ] }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "tags": [ + "cor" + ] + }, + "outputs": [], + "source": [ + "def matrice_gradient(m):\n", + " n, k = np.shape(m)\n", + " g = np.zeros((n, k))\n", + " g[0][0] = abs(m[1][0] - m[0][0]) + abs(m[0][1] - m[0][0])\n", + " g[n-1][k-1] = abs(m[n-1][k-1] - m[n-2][k-1]) + abs(m[n-1][k-1] - m[n-1][k-2])\n", + " g[0][k-1] = abs(m[1][k-1] - m[0][k-1]) + abs(m[0][k-1] - m[0][k-2])\n", + " g[n-1][0] = abs(m[n-1][0] - m[n-2][0]) + abs(m[n-1][1] - m[n-1][0])\n", + " for i in range(1, n-1):\n", + " g[i][0] = abs(m[i+1][0] - m[i-1][0]) + abs(m[i][1] - m[i][0])\n", + " g[i][k-1] = abs(m[i+1][k-1] - m[i-1][k-1]) + abs(m[i][k-1] - m[i][k-2])\n", + " for j in range(1, k-1):\n", + " g[0][j] = abs(m[1][j] - m[0][j]) + abs(m[0][j+1] - m[0][j-1])\n", + " g[n-1][j] = abs(m[n-1][j] - m[n-2][j]) + abs(m[n-1][j+1] - m[n-1][j-1])\n", + " for i in range(1, n-1):\n", + " for j in range(1, k-1):\n", + " g[i][j] = abs(m[i+1][j] - m[i-1][j]) + abs(m[i][j+1] - m[i][j-1])\n", + " return g" + ] + }, { "cell_type": "code", "execution_count": 4, @@ -206,6 +235,28 @@ " return np.array(m2) # conversion en matrice numpy" ] }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "tags": [ + "cor" + ] + }, + "outputs": [], + "source": [ + "def enlever_chemin(m, c):\n", + " n, p = np.shape(m)\n", + " m2 = []\n", + " for i in range(n):\n", + " l = []\n", + " for j in range(p):\n", + " if j != c[i]:\n", + " l.append(m[i][j])\n", + " m2.append(l)\n", + " return np.array(m2)" + ] + }, { "attachments": {}, "cell_type": "markdown", @@ -233,6 +284,27 @@ "**Question** : Écrire une fonction `dp_chemin(g)` qui renvoie une matrice `d` de même dimension que `g` telle que `d[i][j]` est l'énergie minimum d'un chemin depuis le haut de l'image jusqu'à un pixel `i, j`." ] }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "tags": [ + "cor" + ] + }, + "outputs": [], + "source": [ + "def dp_chemin(g):\n", + " d = np.zeros(g.shape)\n", + " d[0] = g[0]\n", + " for i in range(1, g.shape[0]):\n", + " d[i, 0] = g[i, 0] + min(d[i-1, 0], d[i-1, 1])\n", + " d[i, -1] = g[i, -1] + min(d[i-1, -1], d[i-1, -2])\n", + " for j in range(1, g.shape[1]-1):\n", + " d[i, j] = g[i, j] + min(d[i-1, j-1], d[i-1, j], d[i-1, j+1])\n", + " return d" + ] + }, { "cell_type": "code", "execution_count": 9, @@ -272,6 +344,24 @@ "**Question** : Écrire une fonction `min_energie_bas(d)` qui renvoie le numéro de colonne `j` du pixel de la dernière ligne d'énergie minimale, c'est-à-dire tel que `d[n - 1][j]` est minimal, où `d` est la matrice renvoyée par la fonction `dp_chemin(m)` et `n` est le nombre de lignes de `d`." ] }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "tags": [ + "cor" + ] + }, + "outputs": [], + "source": [ + "def min_energie_bas(d):\n", + " j_mini = 0 # indice du minimum de d sur la dernière ligne\n", + " for j in range(1, d.shape[1]):\n", + " if d[-1, j] < d[-1, j_mini]:\n", + " j_mini = j\n", + " return j_mini" + ] + }, { "cell_type": "code", "execution_count": 11, @@ -317,6 +407,29 @@ " return c[::-1] # pour inverser c" ] }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "tags": [ + "cor" + ] + }, + "outputs": [], + "source": [ + "def min_chemin(m):\n", + " d = dp_chemin(matrice_gradient(m))\n", + " c = [min_energie_bas(d)]\n", + " for i in range(len(m) - 2, -1, -1):\n", + " j = c[-1]\n", + " mini = j\n", + " for k in range(j - 1, j + 2):\n", + " if k >= 0 and k < len(d[i]) and d[i, k] < d[i, j]:\n", + " mini = k\n", + " c.append(mini)\n", + " return c[::-1] # pour inverser c" + ] + }, { "cell_type": "code", "execution_count": 14, @@ -362,6 +475,23 @@ "- Enlever le chemin `c` de `m`" ] }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "tags": [ + "cor" + ] + }, + "outputs": [], + "source": [ + "def seam_carving(m, n):\n", + " for i in range(n):\n", + " c = min_chemin(m)\n", + " m = enlever_chemin(m, c)\n", + " return m" + ] + }, { "cell_type": "code", "execution_count": 16,