Vous êtes sur la page 1sur 24

Algorithmique 

Avancée
Cours n°2 : Programmation Dynamique

Master 1 – Génie Logiciel
Université Abou­Bakr 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 sous­problèmes.
4. Chaque sous­problè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 sous­solutions.
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 sous­solutions.

Résoudre  les  sous­problèmes  d'une  façon  descendante  (Top­
down) ou ascendante (Bottom­up).

Combiner  les  sous­solutions  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 =
 Fn­1 + Fn­2  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 + Cn­1 + Cn­2  ⇒ O( ???? ).
Programmation Dynamique
Quelques paradigmes de programmation
Exemple introductif :
Arbre des coûts :
Cn

Cn­1 + 1 + Cn­2

Cn­2 + 1 +     Cn­3                                   Cn­3 + 1 +    Cn­4

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

Cn­1 + 1 + Cn­2

Cn­2 + 1 +     Cn­3                                   Cn­3 + 1 +    Cn­4

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

Cn­1 + 1 + Cn­2

Cn­2 + 1 +     Cn­3                                   Cn­3 + 1 +    Cn­4

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 ≤  2n­1 – 1 ⇒ O(2n).

Quelles classes de complexité ? ⇒ Exponentielle

Est­il 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 sous­problèmes
Programmation Dynamique
Quelques paradigmes de programmation
Exemple introductif :
D'où l'intuition de calculer une seule fois chaque sous­pb

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 ;
}

Quelle  est  sa  complexité  temporelle  en  fonction  des 


appels récurfis ?
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    

 2      1    0  1      0 0

 1      0    0 0

0 C(N) = C(0) + C(1) +….+ C(N­1)
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    

 2      1    0  1      0 0

 1      0    0 0

0 Coût total = 1 + (1 – 2N­1)/(1 – 2) = 2N­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 ;
}

Complexité recherchée est en O(2N­1) ⇒ Exponentielle.
Programmation Dynamique
Quelques paradigmes de programmation
Solution avec la PRD (Top­down) :
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 (Top­down) :
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 (Top­down) :

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 :
Sous­solutions optimales :
La  solution  optimale  du  problème  doit  être  basée  sur  des 
sous­solutions optimales.

1. Problème du plus court chemin non­pondéré (Critère vérifié)
2.  Problème du plus long chemin non­pondéré (Critère non­vérifié)

Chevauchement des sous­problèmes :
Deux  sous­problèmes  se  chevauchent  s'il  s'agit  du  même 
sous­problème  qui  apparaît  au  dessous  de  deux  problèmes 
différents (ex. C(2) est un sous­problème de C(4) et C(3)).

Aspect de mémorisation.

Vous aimerez peut-être aussi