From 03a74138930cb27ef9d435fc04026e35de97e609 Mon Sep 17 00:00:00 2001
From: fortierq Coefficient binomialQuestion
Que peut-on prendre comme cas de base ?
-Solution
-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.
Solution
-def binom_rec(n, k): # voir cours
- if k == 0:
- return 1
- if n == 0:
- return 0
- return binom_rec(n - 1, k - 1) + binom_rec(n - 1, k)
-
Solution
-def binom_dp(n, k):
- M = [[0]*(k + 1) for _ in range(n + 1)]
- for i in range(0, n + 1):
- M[i][0] = 1 # cas de base
-
- for i in range(1, n + 1):
- for j in range(1, k + 1):
- M[i][j] = M[i - 1][j - 1] + M[i - 1][j]
- return M[n][k]
-
Solution
-def binom(n, k):
- d = {}
- def aux(i, j):
- if j == 0: return 1
- if i == 0: return 0
- if (i, j) not in d:
- d[(i, j)] = aux(i - 1, j - 1) + aux(i - 1, j)
- return d[(i, j)]
- return aux(n, k)
-
Solution
-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\).
-Dans le cas général, on considère les deux possibilités et on conserve le minimum : -$\( - r(n, k) = min(r(n, k - 1), r(n - a_k, k) + 1) -\)$
-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)\).
-Question
En déduire une fonction rendu(L, n)
par programmation dynamique renvoyant le nombre minimum de pièces requises pour rendre n
euros, où L
est la liste des pièces.
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
.
Solution
-def rendu(L, n):
- k = len(L) # nombre de pièces
- M = [[0]*(k + 1) for _ in range(n + 1)]
- for i in range(1, n + 1):
- M[i][0] = float("inf")
- for j in range(1, k + 1):
- if i - L[j - 1] >= 0:
- M[i][j] = min(M[i][j - 1], 1 + M[i - L[j - 1]][j])
- else:
- M[i][j] = M[i][j - 1]
- return M[-1][-1]
-
rendu([1, 2, 5], 7)
@@ -777,32 +706,6 @@ Rendu de monnaieQuestion
Réécrire la fonction précédente par mémoïsation plutôt que par programmation dynamique.
Solution
-def rendu_memo(L, n):
- k = len(L)
- d = {}
- def aux(i, j):
- if (i, j) in d:
- return d[(i, j)]
- if i == 0:
- return 0
- if j == 0:
- return float("inf")
- if i - L[j - 1] >= 0:
- d[(i, j)] = min(aux(i, j - 1), 1 + aux(i - L[j - 1], j))
- else:
- d[(i, j)] = aux(i, j - 1)
- return d[(i, j)]
- return aux(n, k)
-rendu_memo([1, 2, 5], 7)
-
2
-
-
Définir M
en Python.
Solution
-M = [[1, 0, 0, 0], [0, 0, 1, 1], [0, 1, 1, 1], [0, 1, 0, 1]]
-
É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.
Solution
-def est_carre(M, x, y, k):
- for i in range(x, x + k):
- for j in range(y, y + k):
- if M[i][j] != 1:
- return False
- return True
-
assert(est_carre(M, 1, 2, 2) and not est_carre(M, 1, 1, 2))
@@ -849,18 +735,6 @@ Méthode naïveQuestion
É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.
Solution
-def contient_carre(M, k):
- n = len(M)
- for i in range(n - k + 1):
- for j in range(n - k + 1):
- if est_carre(M, i, j, k):
- return True
- return False
-
assert(contient_carre(M, 2) and not contient_carre(M, 3))
@@ -872,17 +746,6 @@ Méthode naïveQuestion
Écrire une fonction max_carre1
telle que max_carre1(m)
renvoie la taille maximum d’un carré de 1 contenu dans m
.
Solution
-def max_carre1(M):
- n = len(M)
- for k in range(n, 0, -1):
- if contient_carre(M, k):
- return k
- return 0
-
max_carre1(M)
@@ -899,34 +762,16 @@ Méthode naïveQuestion
Quelle est la complexité de max_carre1(m)
dans le pire cas ?
Solution
-est_carre(M, x, y, k)
est en \(O(k^2)\).
contient_carre(M, k)
appelle O(\(n\)) fois est_carre
, donc est en \(O(n^2 k^2)\).
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)}\).`
On va construire une matrice c
telle que c[x][y]
est la taille maximum d’un carré de 1 dans m
dont la case en bas à droite est m[x][y]
(c’est à dire un carré de 1 qui contient m[x][y]
mais aucun m[i][j]
si \(i > x\) ou \(j > y\)).
Par exemple, c[1][2] = 1
et c[2][3] = 2
pour la matrice \(M\) ci-dessus.
Question
Que vaut c[0][y]
et c[x][0]
?
Solution
-c[0][y] = 0
si m[0][y] = 0
et c[0][y] = 1
sinon.
-De même pour c[x][0]
.
-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
.
Question
Que vaut c[x][y]
si m[x][y] = 0
?
Solution
-c[x][y] = 0
.
Question
Montrer que, si m[x][y] = 1
, c[x][y] = 1 + min(c[x-1][y], c[x][y-1], c[x-1][y-1])
.
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é.
Solution
-def max_carre2(m):
- c = m.copy()
- for i in range(len(m)):
- for j in range(len(m[0])):
- if m[i][j] == 1:
- c[i][j] = 1 + min(c[i - 1][j], c[i][j - 1], c[i - 1][j - 1])
- return max(max(l) for l in c)
-
max_carre2(M)
@@ -963,11 +796,6 @@ Méthode naïveQuestion
Quelle est la complexité de max_carre2(m)
, en fonction des dimensions de m
? Comparer avec max_carre1(m)
.
Solution
-max_carre2(m)
est en \(\boxed{O(n^2)}\) à cause des deux boucles for
imbriquées.
-C’est donc beaucoup mieux que max_carre1(m)
qui est en \(O(n^6)\).