Vous êtes sur la page 1sur 18

Algorithmique Avancée et 

Complexité
Programmation Dynamique
Université Abou­Bakr Belkaïd ­ Tlemcen
2019/2020

Dr. Mahfoud Houari


mahfoud.houari@gmail.com
Programmation Dynamique
Quelques paradigmes de programmation

Programmation Dynamique
(Dynamic Programming)
Programmation Dynamique
Quelques paradigmes de programmation
Critère de chevauchement de sous­problèmes :
Exemple :

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)

Fibo(1)   Fibo(0)

Conséquence :  le  calcul  répétitif  des  sous­problèmes 


mène souvent à une complexité exponentielle !!!
Programmation Dynamique
Quelques paradigmes de programmation
Critère de chevauchement de sous­problèmes :
Exemple :

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)

Fibo(1)   Fibo(0)

Solution :  enregistrer  la  solution  de  chaque  sous­


problème afin de le calculer une seule fois.
Programmation Dynamique
Quelques paradigmes de programmation
Exemple introductif – Fonction de Fibonacci

Méthode naïve : Méthode PRD ascendante :
int Fibo(int N){ 
int Fibo(int N){  int [] memo = new int[N + 1] ;
memo[0] = 1 ;
if(N==0 || N==1) memo[1] = 1 ; 
return 1 ; for (int i = 2 ; i ≤ N ; i++){
else memo[i] = memo[i ­ 1] +
return Fibo[N­1] +  memo[i ­ 2] ;
Fibo[N­2] ; }
} return memo[N] ;
}

WCTC : O(2N). WCTC : O(N).
WCSC : O(1). WCSC : O(N).
Programmation Dynamique
Quelques paradigmes de programmation
Principe de la Programmation Dynamique :
1. Basée sur le même principe de l'approche DpR :
La solution optimale d'un problème peut se déduire 
à partir des solutions optimales de ses sous­problèmes.

2.  Un  problème  de  taille  N  nécessite  la  résolution  de  sous­
problèmes de taille inférieure à N (Pas forcément une division).

3. Chaque sous­problème est résolu une seule fois et sa solution 
optimale est enregistrée.

4.  La  solution  globale  est  construite,  d'une  façon  ascendante 


(Bottom­up) ou  descendante (Top­Down) en combinant les sous­
solutions.
Programmation Dynamique
Quelques paradigmes de programmation
Autre solution dynamique pour la Fibonacci :
Méthode descendante :
HashMap<Integer, Integer> Memo = new HashMap<Integer, Integer>();
int Fibo(int N){
if (Memo.containsKey(N))
      return Memo.get(N) ;
else {
int R ;
if (N == 0 || N == 1)
R = 1 ;
else 
R = Fibo(N­1) + Fibo(N­2) ;
Memo.put(N, R) ;
return R ;
WCTC : O(?)
}
WCSC : O(?)
}
Programmation Dynamique
Quelques paradigmes de programmation
Autre solution dynamique pour la Fibonacci :
Arbre des Appels Récursifs de la méthode descendante:

Fibo(6) O(1)

Fibo(5)          Fibo(4) O(1) + O(1)

               Fibo(4)                              Fibo(3)                  
O(1) + O(1)

Fibo(3)                        Fibo(2)                                           
O(1) + O(1)

Fibo(2)                     Fibo(1)                                                   
O(1) + O(1)

O(1) + O(1)
Fibo(1)   Fibo(0)                                                                         
Programmation Dynamique
Quelques paradigmes de programmation
Autre solution dynamique pour la Fibonacci :
Méthode descendante :
HashMap<Integer, Integer> Memo = new HashMap<Integer, Integer>();
int Fibo(int N){
if (Memo.containsKey(N))
      return Memo.get(N) ;
else {
int R ;
if (N == 0 || N == 1)
R = 1 ;
else 
R = Fibo(N­1) + Fibo(N­2) ;
Memo.put(N, R) ;
return R ;
WCTC : O(N)
} ⇒ O(N) le coût de la méthode
WCSC : O(N)
  C
} ⇒ O(N) la taille de Memo
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
Définition formelle de la solution :
Sol(N, D, P) = Max(Pi + Sol(N – D[i], D, P))
Pour i allant de 1 jusqu'au nombre des découpes

Méthode naïve :
int CB_Naif(int N, int[] D, int[] P){
if (N == 0) return 0 ;
int R = 0 ;
for (int i = 0 ; i < D.length ; i++){
   if(D[i] ≤ N){
 R = Math.max(R, P[i] + CB_Naif(N ­ D[i], D, P)) ;
   }
}
WCTC : O( ???? )
return R ;
WCSC : O( ???? )=
}
Programmation Dynamique
Quelques paradigmes de programmation
Les découpes possibles au pire des cas pour N = 4 :

4
1 4
2 3
3 2 1 0    

 2      1    0  1      0 0

 1      0    0 0

C(N) = C(0) + C(1) +….+ C(N­1) + O(|D|)
C(0) = O(1) (aucune découpe possible).
Programmation Dynamique
Quelques paradigmes de programmation
Définition formelle de la solution :
Sol(N, D, P) = Max(Pi + Sol(N – D[i], D, P))
Pour i allant de 1 jusqu'au nombre des découpes

Méthode naïve :
int CB_Naif(int N, int[] D, int[] P){
if (N == 0) return 0 ;
int R = 0 ;
for (int i = 0 ; i < D.length ; i++){
   if(D[i] ≤ N){
 R = Math.max(R, P[i] + CB_Naif(N ­ D[i], D, P)) ;
   }
}
WCTC : O( 2N )
return R ;
WCSC : O( N )=
}
Pourquoi cette inefficacité ?
Programmation Dynamique
Quelques paradigmes de programmation
Schéma d'une solution PRD descendante :

HashMap<Type_Problème, Type_Sol> Memo = new ….;

Type_Sol PRD(Paramètres){
S'il existe une clé pour le problème actuel
Alors Retourner la solution correspondante à partir du 
Memo ;
Sinon :

Calculer la solution du problème actuel ; Code la solution 
naïve

Enregistrer la solution obtenue dans la Memo
Retourner la solution obtenue ;
}
Programmation Dynamique
Quelques paradigmes de programmation
Solution avec la PRD :
Méthode descendante :
HashMap<Integer, Integer> Memo = new HashMap<Integer, Integer>();
Memo.put(0, 0) ;
int CB_PRD(int N, int[] D, int[] P){
if (Memo.containsKey(N))
      return Memo.get(N) ;

Solution naïve
int R = 0 ;
for (int i = 0 ; i < D.length ; i++){
if(D[i] ≤ N){
R = Math.max(R, P[i] + CB_PRD(N ­ D[i], D, P)) ;
}
}
Memo.put(N, R) ;
return R ;
}
Programmation Dynamique
Quelques paradigmes de programmation
Solution avec la PRD :
Méthode descendante :
HashMap<Integer, Integer> Memo = new HashMap<Integer, Integer>();
Memo.put(0, 0) ;
int CB_PRD(int N, int[] D, int[] P){
if (Memo.containsKey(N))
      return Memo.get(N) ;

Solution naïve
int R = 0 ;
for (int i = 0 ; i < D.length ; i++){
if(D[i] ≤ N){
R = Math.max(R, P[i] + CB_PRD(N ­ D[i], D, P)) ;
}
}
Memo.put(N, R) ; WCTC : O(N² + N.|D|)
return R ;
WCSC : O(N)=
}