nsi:terminales:dynamique:programmation_dynamique
Différences
Ci-dessous, les différences entre deux révisions de la page.
| Les deux révisions précédentesRévision précédenteProchaine révision | Révision précédente | ||
| nsi:terminales:dynamique:programmation_dynamique [2023/02/03 18:29] – supprimée - modification externe (Unknown date) 127.0.0.1 | nsi:terminales:dynamique:programmation_dynamique [2023/02/03 18:29] (Version actuelle) – ↷ Liens modifiés en raison d'un déplacement. goupillwiki | ||
|---|---|---|---|
| Ligne 1: | Ligne 1: | ||
| + | ====== Programmation dynamique ====== | ||
| + | |||
| + | ===== Origine du nom ===== | ||
| + | |||
| + | {{ : | ||
| + | |||
| + | Concept créé | ||
| + | |||
| + | <WRAP important> | ||
| + | |||
| + | ===== Principe ===== | ||
| + | |||
| + | <WRAP box> | ||
| + | Il s'agit de : | ||
| + | * **découper** un problème en sous-problèmes, | ||
| + | * puis à résoudre les sous-problèmes **du plus petit au plus grand** en **conservant les résultats** intermédiaires. | ||
| + | </ | ||
| + | |||
| + | <WRAP Important> | ||
| + | |||
| + | ===== Exemple de la suite de Fibonacci ===== | ||
| + | |||
| + | La suite de Fibonacci est définie par: | ||
| + | * $F(0) = 1$, | ||
| + | * $F(1) = 1$, | ||
| + | * pour $n \geqslant 2$, $F(n) = F(n-1) + F(n-2)$. | ||
| + | |||
| + | ==== Approche naïve ==== | ||
| + | |||
| + | On pourrait -- comme on l'a fait dans [[..: | ||
| + | |||
| + | <code python linenums> | ||
| + | # version naïve | ||
| + | def F(n): | ||
| + | if n <= 1: | ||
| + | return 1 | ||
| + | return F(n-1) + F(n-2) | ||
| + | </ | ||
| + | |||
| + | C'est simple et cela suit de près la définition mathématique de la suite. | ||
| + | |||
| + | Supposons que l'on veuille calculer $F(5)$. | ||
| + | |||
| + | {{ : | ||
| + | |||
| + | Cet arbre représente le calcul et les appels récursifs. | ||
| + | * Pour calculer $F(5)$ il faut calculer $F(4)$ et $F(3)$. | ||
| + | * $F(4)$ nécessite lui-même le calcul de $F(3)$ et $F(2)$. | ||
| + | * Et ainsi de suite... | ||
| + | |||
| + | On voit que cette façon d' | ||
| + | |||
| + | Imaginez la perte de temps pour calculer $F(1\, | ||
| + | |||
| + | Il faudrait donc organiser notre suite de calculs pour ne pas faire plusieurs fois le même calcul. C'est tout le principe de la programmation dynamique. | ||
| + | |||
| + | <WRAP tip> | ||
| + | |||
| + | ==== Approche programmation dynamique ==== | ||
| + | |||
| + | On sait que le calcul de $F(n)$ nécessitera de calculer tous les $F(i)$ avec $0 \leqslant i < n$. Le calcul de ces $F(i)$ sont autant de sous-problèmes que l'on va traiter dans le **sens croissant** en **sauvegardant les réponses**. | ||
| + | |||
| + | <code python linenums> | ||
| + | # version dynamique | ||
| + | def F(n): | ||
| + | tab_F = [1, 1] # on sait que F(0) = F(1) = 1 | ||
| + | for i in range(2, n+1): | ||
| + | F_i = tab_F[i-1] + tab_F[i-2] | ||
| + | tab_F.append(F_i) | ||
| + | return tab_F[-1] # renvoie le dernier calculé | ||
| + | </ | ||
| + | |||
| + | Dans cette fonction on augmente la taille du tableau ('' | ||
| + | |||
| + | <code python linenums> | ||
| + | # version dynamique améliorée | ||
| + | def F(n): | ||
| + | tab_F = [0]*(n+1) # tableau de n+1 éléments, tous à 0 | ||
| + | tab_F[0] = 1 # on sait que F(0) = 1 | ||
| + | tab_F[1] = 1 # on sait que F(1) = 1 | ||
| + | for i in range(2, n+1): | ||
| + | tab_F[i] = tab_F[i-1] + tab_F[i-2] # plus besoin de append | ||
| + | return tab_F[-1] # renvoie le dernier calculé | ||
| + | </ | ||
| + | |||
| + | L' | ||
| + | |||
| + | <WRAP tip>Dans le cas dynamique, on montre explicitement que l'on mémorise les résultats intermédiaires dans le tableau. Mais cela n' | ||
| + | |||
| + | <WRAP box> | ||
| + | * avec '' | ||
| + | * avec '' | ||
| + | * la version dynamique n'a pas ce problème. | ||
| + | </ | ||
| + | |||
| + | <WRAP tip> | ||
| + | * les calculs deviennent beaucoup plus longs, | ||
| + | * dans la version dynamique améliorée, | ||
| + | </ | ||
| + | |||
| + | ==== Comparaison des coûts temporels ==== | ||
| + | |||
| + | == Version récurrente == | ||
| + | |||
| + | On va noter $C_{rec}(n)$ on évaluation grossière du nombre d' | ||
| + | |||
| + | * On peut poser que $F(0)$ et $F(1)$ sont calculés en une instruction, | ||
| + | * Pour $n \geqslant 2$, $F(n) = F(n-1) + F(n-2)$, on peut donc estimer que le coût du calcul de $F(n)$ est égal au coût du calcul de $F(n-1)$ plus le coût de calcul de $F(n-2)$ plus l' | ||
| + | |||
| + | On peut prouver que, si $a = \frac{1 - \sqrt{5}}{2} \simeq -0,6$ et $b = \frac{1 + \sqrt{5}}{2} \simeq 1,6$ alors $C_{rec}(n) = \frac{2}{\sqrt{5}} \cdot \left(b^{n+1} - a^{n+1}\right) - 1$. | ||
| + | |||
| + | Donc pour $n \to +\infty$, $C_{req}(n) \simeq 1,4 \times 1,6^n$. C'est une croissance exponentielle. | ||
| + | |||
| + | <WRAP important> | ||
| + | |||
| + | == Version dynamique == | ||
| + | |||
| + | Dans sa version dynamique, on peut dire que le calcule de $F(n)$ nécessite | ||
| + | * la lecture de $F(n-1)$ dans le tableau, | ||
| + | * la lecture de $F(n-2)$ dans le tableau, | ||
| + | * le calcul de la somme, | ||
| + | * l' | ||
| + | |||
| + | Donc $C_{dyn}(n) \simeq 4\,n$. | ||
| + | |||
| + | <WRAP important> | ||
| + | |||
| + | == Comparaison == | ||
| + | |||
| + | Puisque la version récurrente est en $1,6^n$ et la version dynamique en $n$, quels que soient les coefficients, | ||
