Académique Documents
Professionnel Documents
Culture Documents
Avancée
Cours n°2 : Programmation Dynamique
Master 1 – Génie Logiciel
Université AbouBakr Belkaïd Tlemcen
2016/2017
Mahfoud Houari
mahfoud.houari@gmail.com
hmahfoud.wordpress.com
Programmation Dynamique
Quelques paradigmes de programmation
Principe de la PRD :
1. C'est un schéma d'algorithmes introduit en 1950 par
Richard Bellman.
2. Appliquée pour résoudre des problèmes d'optimisation.
3. Basée sur le principe d'optimalité de Bellman :
La solution optimale de quelques problèmes s'appuie
sur les solutions optimales de leurs sousproblèmes.
4. Chaque sousproblème est résolu une seule fois et sa
solution optimale est enregistrée.
5. La solution globale est construite, d'une façon ascendante
ou descendante, en combinant les soussolutions.
Programmation Dynamique
Quelques paradigmes de programmation
Développement d'un algorithme de PRD :
●
Caractériser la solution optimale.
●
Trouver une définition récursive de la solution optimale.
●
Définir une structure pour enregistrer les soussolutions.
●
Résoudre les sousproblèmes d'une façon descendante (Top
down) ou ascendante (Bottomup).
●
Combiner les soussolutions pour avoir une solution
optimale du problème initial.
Programmation Dynamique
Quelques paradigmes de programmation
Exemple introductif :
Soit la suite de Fibonacci :
1 si n = 0 ou n = 1
Fn =
Fn1 + Fn2 sinon
Algorithme récursif :
int Fibo(int n){ //Supposons que n ≥ 0.
if (n == 0 || n == 1)
return 1 ;
else
return Fibo(n 1) + Fibo(n 2) ;
}
Quelle est sa complexité Cn ?
Programmation Dynamique
Quelques paradigmes de programmation
Exemple introductif :
Algorithme récursif :
int Fibo(int n){ //Supposons que n ≥ 0.
if (n == 0 || n == 1)
return 1 ;
else
return Fibo(n 1) + Fibo(n 2) ;
}
BCET : Cn = C1 = C0 = 1 ⇒ O(1).
WCET : Cn = 1 + Cn1 + Cn2 ⇒ O( ???? ).
Programmation Dynamique
Quelques paradigmes de programmation
Exemple introductif :
Arbre des coûts :
Cn
Cn1 + 1 + Cn2
C2
C1 + 1 + C0
1 1
Coût total : C'est le nombres des appels
= nombres des nœuds.
Programmation Dynamique
Quelques paradigmes de programmation
Exemple introductif :
Arbre des coûts :
Cn
Cn1 + 1 + Cn2
C2
C1 + 1 + C0
1 1
Remarque : c'est un ABR de hauteur = n 1.
Programmation Dynamique
Quelques paradigmes de programmation
Exemple introductif :
Arbre des coûts :
Cn
Cn1 + 1 + Cn2
C2
C1 + 1 + C0
1 1
Remarque : Le nombre de nœuds dans un ABR de
hauteur h est égal à 2h 1.
Programmation Dynamique
Quelques paradigmes de programmation
Exemple introductif :
Coût total de l'algorithme est ≤ 2n1 – 1 ⇒ O(2n).
Quelles classes de complexité ? ⇒ Exponentielle
Estil praticable ? ⇒ Il est impraticable
D'où vient cette inefficacité ?
Programmation Dynamique
Quelques paradigmes de programmation
Exemple introductif :
Arbre des coûts :
Fibo(5)
Fibo(4) Fibo(3)
Fibo(3) Fibo(2) Fibo(2) Fibo(1)
Fibo(2) Fibo(1) Fibo(1) Fibo(0) Fibo(1) Fibo(0)
D'où vient cette inefficacité ?
⇒ Dû au calcul répétitif des sousproblèmes
Programmation Dynamique
Quelques paradigmes de programmation
Exemple introductif :
D'où l'intuition de calculer une seule fois chaque souspb
Algorithme itératif :
int Fibo(int n){ //Supposons que n ≥ 0.
int [] resultats = new int[n + 1] ;
resultats[0] = 1 ;
resultats[1] = 1 ;
for (int i = 2 ; i ≤ n ; i++){
resultats[i] = resultats[i 1] + resultats[i 2] ;
}
return resultats[n] ;
}
Complexité temporelle : O(n) ⇒ Algorithme linéaire.
Complexité spatiale : O(n).
Programmation Dynamique
Quelques paradigmes de programmation
Application de la PRD pour le problème
de « Découpe de barres »
Programmation Dynamique
Quelques paradigmes de programmation
Description du problème :
Barre de longueur N
Découpes désirées (de longueur entière):
Découpe (cm) d1 d2 ... dk
Prix (pi) p1 p2 ... pk
Objectif :
Déterminer les découpes à réaliser afin de maximiser le
revenu total RN.
Programmation Dynamique
Quelques paradigmes de programmation
Instance du problème :
Barre de longueur 4
Découpes désirées :
Longueur 1 2 3 4 5 6 7 8 9 10
Prix (pi) 1 5 8 9 10 17 17 20 24 30
Découpes possibles :
9 1+8 5+5 8+1
1+1+5 1+5+1 5+1+1 1+1+1+1
Programmation Dynamique
Quelques paradigmes de programmation
Description récursive de la solution :
RN =max 1 ≤i ≤ N ( pi + RN − i )
Algorithme naïf (k ≤ N):
int CoupeBarre(int n, int[] T, int[] P, int k){
int R = 0 ;
for (int i = 0 ; i < k ; i++){
if(T[i] ≤ n){
R = max(R, P[i] + CoupeBarre(n T[i], T, P, k)) ;
}
}
return R ;
}
Découpes désirées :
Longueur 1 2 3 4 5 6 7 8 9 10
Prix (pi) 1 5 8 9 10 17 17 20 24 30
Arbre des appels récursifs (pour N=4) :
4
3 2 1 0
1 0 0 0
0 C(N) = C(0) + C(1) +….+ C(N1)
C(0) = C(1) = 1.
Programmation Dynamique
Quelques paradigmes de programmation
Revenons ici pour calculer la complexité :
Barre de longueur 4
Découpes désirées :
Longueur 1 2 3 4 5 6 7 8 9 10
Prix (pi) 1 5 8 9 10 17 17 20 24 30
Arbre des appels récursifs (pour N=4) :
4
3 2 1 0
1 0 0 0
0 Coût total = 1 + (1 – 2N1)/(1 – 2) = 2N1.
Programmation Dynamique
Quelques paradigmes de programmation
Description récursive de la solution :
RN =max 1 ≤i ≤ N ( pi + RN − i )
Algorithme naïf (k ≤ N):
int CoupeBarre(int n, int[] T, int[] P, int k){
int R = 0 ;
for (int i = 0 ; i < k ; i++){
if(T[i] ≤ n){
R = max(R, P[i] + CoupeBarre(n T[i], T, P, k)) ;
}
}
return R ;
}
Complexité recherchée est en O(2N1) ⇒ Exponentielle.
Programmation Dynamique
Quelques paradigmes de programmation
Solution avec la PRD (Topdown) :
Méthode descendante avec mémorisation :
Hashtable<Integer, Integer> Memo = new Hashtable<Integer, Integer>();
int CB_Memo(int n, int[] T, int[] P, int k){
if (Memo.containsKey(n))
return Memo.get(n) ;
int R = 0 ;
for (int i = 0 ; i < k ; i++){
if(T[i] ≤ n){
R = max(R, P[i] + CB_Memo(n T[i], T, P, k)) ;
}
}
Memo.put(n, R) ;
return R ;
}
Quelle est l'ordre de grondeur de sa complexité (temporelle et spatiale) ?
Programmation Dynamique
Quelques paradigmes de programmation
Solution avec la PRD (Topdown) :
Méthode descendante avec mémorisation :
Hashtable<Integer, Integer> Memo = new Hashtable<Integer, Integer>();
int CB_Memo(int n, int[] T, int[] P, int k){
if (Memo.containsKey(n))
return Memo.get(n) ;
int R = 0 ;
for (int i = 0 ; i < k ; i++){
if(T[i] ≤ n){
R = max(R, P[i] + CB_Memo(n T[i], T, P, k)) ;
}
}
Memo.put(n, R) ; Complexité spatiale en O(N).
return R ;
} Complexité temporelle en O(N²).
Programmation Dynamique
Quelques paradigmes de programmation
Remarque importante :
Lorsque vous décidez d'utiliser une structure
de données (ex. Hashtable) pensez à la
complexité des méthodes de cette structure
que vous allez utiliser (ex. put, get, containsKey)
Programmation Dynamique
Quelques paradigmes de programmation
Solution avec la PRD (Topdown) :
Exercice :
Modifiez l'algorithme pour qu'il retourne aussi la
solution optimale et pas uniquement la valeur
optimale
Programmation Dynamique
Quelques paradigmes de programmation
Remarque importante :
La PRD permet généralement de réduire une
complexité exponentielle à l'aide d'une
complexité spatiale
Programmation Dynamique
Quelques paradigmes de programmation
Critères d'applicabilité de la PRD :
Soussolutions optimales :
La solution optimale du problème doit être basée sur des
soussolutions optimales.
1. Problème du plus court chemin nonpondéré (Critère vérifié)
2. Problème du plus long chemin nonpondéré (Critère nonvérifié)
Chevauchement des sousproblèmes :
Deux sousproblèmes se chevauchent s'il s'agit du même
sousproblème qui apparaît au dessous de deux problèmes
différents (ex. C(2) est un sousproblème de C(4) et C(3)).
Aspect de mémorisation.