Vous êtes sur la page 1sur 18

Computer Science is no more about computers than astronomy is about telescopes.

(E. W. Dijkstra)

Chapitre 1 : Techniques de conception d’algorithmes

Lorsqu’un calcul s’arrête en un temps fini et que le résultat final fournit la réponse au problème
on dit alors que ce calcul est un algorithme.

1 Terminaison d’algorithmes.

Turing a prouvé, en 1936, que la terminaison est un problème indécidable. En d’autres termes, il
ne peut pas exister de mécanisme/algorithme capable de toujours prouver la terminaison d’un
programme. On ne pourrait donc jamais créer une méthode universelle qui prend un quelconque
programme en entrée et donne en sortie « se termine » ou « ne se termine pas » en un temps fini.
Autrement dit, dans certains cas il est possible de déterminer que le programme se termine ou qu’il ne
se terminera pas, mais il est impossible de le faire pour tous les programmes. Heureusement, depuis, on
a trouvé différentes techniques qui permettent de prouver la terminaison d’un grand nombre de
programmes.

Théorème : « Indécidabilité du problème de l’arrêt (Alan Turing, 1936) »


Il n’existe pas de programme permettant de dire si un algorithme termine toujours ou non.

Analysons un exemple qu’il est impossible de traiter, c’est-à-dire une fonction dont on ne sait pas
déterminer si elle va se terminer pour toutes ses entrées possibles. Cet exemple provient de la suite de
Collatz (ou Syracuse). Pour construire une suite de Collatz, il faut partir d’un nombre entier strictement
positif. Ensuite, s’il est pair il faut le diviser par 2, par contre, dans le cas d’un nombre impair, il faut le
multiplier par 3 et ajouter 1. Cette opération est répétée et produit une suite d’entiers positifs dont
chacun dépend de son prédécesseur. La conjoncture de Collatz déclare que la suite de Collatz de
n’importe quel entier strictement positif, atteint 1.

Soit l’algorithme de Syracuse ci-dessous.

Fonction Syr(n :entiers) :entiers


Var r := n;
Début
Tantque r> 1 faire
si r est pair alors
r :=r Div 2
sinon
r :=3*r + 1 ;
finSi
Syr :=r ;
finTantque
FinFonc

© Dr. Benamira A. 2020-MSTIC1 1


Computer Science is no more about computers than astronomy is about telescopes.
(E. W. Dijkstra)

En 1949, Alan Turing, publie une première preuve de terminaison basée sur une notion d’ordre,
appelée les ordinaux. Il publie cette preuve dans l’article « Checking a Large Routine ». Robert W.
Floyd formalise l’idée de Turing. Il décrit la méthode traditionnelle pour prouver la terminaison d’un
programme. Cette méthode se décompose en deux parties :
1. Rechercher un argument de terminaison : Le but de cette étape est de rechercher un argument de
terminaison sous la forme d’une fonction qui lie chaque état du programme à une valeur dans
une structure mathématique. Cette structure mathématique est appelée un ordre bien fondé.
2. Tester l’argument de terminaison : Si le résultat de la fonction trouvée diminue à chaque
transition de programme, l’argument de terminaison sera valide et le programme se terminera.
Formellement, soit F la fonction trouvée et le programme a une transition de l’état s1 à l’état s2,
alors il faut que F(s1) > F(s2). La fonction F est appelée fonction de rang.

1.1 Ordres Bien Fondés

Une relation d'ordre large est une relation réflexive, antisymétrique et transitive.
Soit E un ensemble et R une relation de E vers E :
 R est réflexive ssi pour tout x dans E on a R(x,x)
 R est réflexive ssi pour tout x dans E on a : not R(x,x)
 R est transitive ssi pour tout x,y,z dans E , dès que R(x,y) et R(y,z), on a aussi R(x,z)
 R est antisymétrique ssi pour tout x,y dans E , dès que R(x,y) et R(y,x), on a aussi x=y

On note en général les relations d'ordre large ≤ et les relations d'ordre strict <.
Une relation d'ordre est totale si tous les éléments sont deux à deux comparables. Sinon elle est
partielle.
Un ensemble ordonné (E,<) est bien fondé ssi tout sous ensemble non vide de E admet un plus
petit élément.
Cela signifie en particulier que E a au moins un plus petit élément. Si de plus, E est un bon ordre
(total), il en a exactement un.
On peut ordonner les éléments de N par « < ». Il y a un élément 0 qui est plus petit que tout le
monde.

1.2 Fonction de rang

Prouver la terminaison consiste à exhiber une fonction de rang (parfois nommée variant de
boucle).
Une fonction de rang à valeur dans (R,<) pour une boucle est une fonction entière qui dépend des
variables de la boucle et qui décroît strictement à chaque passage dans la boucle.
Pour valider une fonction de rang, on vérifie pour chaque passage dans la boucle les conditions
suivantes :
1. Elle est à valeur dans (R,<) à l’entrée du corps de la boucle juste après la condition d’arrêt
2. Elle est à valeur dans (R,<) juste avant l’instruction fin du corps de la boucle ;
3. Elle décroît strictement entre ces deux points.

© Dr. Benamira A. 2020-MSTIC1 2


Computer Science is no more about computers than astronomy is about telescopes.
(E. W. Dijkstra)

Excercice :
Proposer et valider une fonction de rang pour l’algorithme Produit.
.
Fonction Produit(a,b :entiers) :entiers
Var x := a; p :=0 ;
Début
Tantque x> 0 faire
p :=p + b ;
x:=x-1 ;
finTantque
Produit :=p ;
FinFonc

2 Correction des algorithmes


Nous nous sommes demandés jusqu’à présent si un algorithme termine. Pour le moment, ce que
nous ne savons pas faire, c’est montrer qu’un algorithme fait effectivement ce que l’on lui demande. Un
algorithme prend en entrée un certain nombre de données et retourne de nouvelles données. L’étude de
la correction partielle d’un algorithme ou d’un programme – on dit aussi la preuve du programme –
c’est l’étude de l’expression des sorties (sous forme des prédicats) en fonction des entrées (sous forme
des prédicats) afin de vérifier que si les entrées sont conformes, les sorties ont bien les valeurs
attendues.
Pour réaliser une étude de correction, on exprime à chaque étape de l’algorithme les valeurs des
variables, c’est-à-dire qu’on décrit symboliquement l’état mémoire.
L’étude de la correction partielle d’un algorithme, c’est faire la preuve que l’algorithme effectue
bien ce qu’on attend de lui. Si on a également montré la terminaison, on dit que l’algorithme est
totalement correct :
correction totale = correction partielle + terminaison.

La correction partielle fournit un système de preuves permettant la démonstration des propriétés.


Exemple 2.1 :

 Précondition : C'est une proposition portant sur l'état de la mémoire et que l'on pense vérifié
avant l'exécution d'un fragment de code.
 Postcondition : C'est une proposition portant sur l'état de la mémoire et que l'on pense vérifié
après l'exécution d'un fragment de code.

© Dr. Benamira A. 2020-MSTIC1 3


Computer Science is no more about computers than astronomy is about telescopes.
(E. W. Dijkstra)

 Spécification : Un programme est spécifié par une précondition et une post condition
déterminant les cas dans lesquels le programme va être exécuté et son résultat.

Exercice : Donner la spécification du code (DivNat) ci-dessous.


a := 0;b := x;
while (b >= y) do
begin
b := b - y;
a := a + 1;
end.

© Dr. Benamira A. 2020-MSTIC1 4


Computer Science is no more about computers than astronomy is about telescopes.
(E. W. Dijkstra)

2.1 Le langage d’assertions

© Dr. Benamira A. 2020-MSTIC1 5


Computer Science is no more about computers than astronomy is about telescopes.
(E. W. Dijkstra)

Nous ferons appel à des notations logiques simples et usuelles, dont voici un récapitulatif :

 True est la propriété vraie

 False est la propriété fausse

 ¬P signifie «non» p

 p/\q signifie p «et» q

 p\/q p signifie p «ou» q




p q signifie p «implique» q, c’est-à-dire que si p est vraie alors q aussi. Notez que si p est
fausse, alors est vraie quelque soit q.

 ∀ . p signifie «pour tout» x, p est vrai. En général x apparaît dans p.

 ∃. p signifie «il existe au moins un» x tel que p est vraie.

Cette liste respecte l’ordre standard de précédence, du symbole le plus fort (¬….) au symbole le plus
faible.

Exemple 2.2 :

© Dr. Benamira A. 2020-MSTIC1 6


Computer Science is no more about computers than astronomy is about telescopes.
(E. W. Dijkstra)

Définition 2.1 :

2. Règles de déduction

2.1 Rappel

Un arbre de déduction dans un système de déduction est une


combinaison finie de règles de la forme :

où chaque règle appliquée est une instance d’une règle. J est la racine de l’arbre, les jugements n’ayant
pas de prémisses sont les feuilles.

Un arbre de déduction complet est un arbre dans laquelle toutes les feuilles sont des instances
d’axiomes.

un jugement J est prouvable si il existe un arbre de déduction complet avec J à la racine. On parle alors
d’arbre de preuve pour J.

© Dr. Benamira A. 2020-MSTIC1 7


Computer Science is no more about computers than astronomy is about telescopes.
(E. W. Dijkstra)

2.2 Les règles de Hoare

Une règle de Hoare est une règle de déduction, c’est-à-dire une fraction de la forme :

Triplet .1 ....... Triplet n


Triplet

Les prémisses et la conséquence sont des triplets de Hoare. Une telle fraction se lit de la manière
suivante : "Si les triplets prémisses sont vrais, alors le triplet conclusion aussi".

Dans ce présent cours n’étudiera que la correction partielle des triplets.

Définition (correction partielle) Le triplet de Hoare suivant {Φ}C{Ψ} est vrai si pour tout état initial
vérifiant Φ, si l’exécution de C se termine, alors Ψ est vraie après l’exécution de C. On dit que le
programme C est partiellement correct par rapport à Φ et Ψ.

La correction totale s’écrit avec des {...}.

Définition (correction totale) Le triplet de Hoare suivant {Φ}C{Ψ} est vrai si pour tout état initial
vérifiant Φ, C se termine et Ψ est vraie après l’exécution de C. On dit que le programme C est
partiellement correct par rapport à Φ et Ψ.

La correction totale s’écrit avec des <…> (parfois avec des [...]) .

Théorème (Correction des règles de Hoare). Étant donné un triplet de Hoare donné T, s’il existe un
arbre de déduction de Hoare complet ayant T à sa racine, alors le triplet est vrai.

Nous allons définir et illustrer ce qu’est un arbre de déduction de Hoare dans la suite en détaillant
chaque règle.

Affectation

Soit le triplet, manifestement vrai, {z-y≥0} x :=z-y {x≥0}. La règle de déduction de l’affectation doit
permettre de prouver ce triplet donc la règle de l’affectation doit pouvoir s’appliquer comme suit :


{z − y ≥ 0 }x :=z − y {x ≥0 }

On voit que x≥0 est vrai après l’exécution du programme seulement si la même propriété est vraie pour
z-y au lieu de x. La règle de l’affectation exprime ceci de la manière suivante :

❑ Aff
{ɸ [ x :=av ] }x :=av {ɸ}

© Dr. Benamira A. 2020-MSTIC1 8


Computer Science is no more about computers than astronomy is about telescopes.
(E. W. Dijkstra)

où la notation Φ[ x :=av ] signifie « Φ dans laquelle x a été remplacé par av ». Si on lit cette règle de la
manière suivante elle devient claire : Pour que Φ soit vraie pour x après le programme x :=av , il fallait
qu’elle soit vraie pour av avant le programme.

Séquence

Pour la séquence d’instructions, la règle est relativement simple à construire :

{ɸ }C 1 {ɸ ' }{ɸ' }C 2 {Ψ }
Seq
{ɸ}C1 ; C2 {Ψ }

Conséquence

Les propriétés que l’on peut prouver directement à partir des règles vues jusqu’ici sont d’une forme très
contrainte, or il faut pouvoir déduire de ces propriétés celles qui nous intéressent. Ceci se fait par
simple déduction logique à partir des propriétés prouvées par les règles précédentes. Pour cela on
ajoute la règle suivante (notez que les deux implications ne sont pas dans le même sens) :

{ɸ ⇒ ɸ' }{ɸ' }C 2 {Ψ ' }{Ψ ' ⇒ Ψ }


Conseq
{ɸ }C {Ψ }

Ou

{ɸ ⇒ ɸ' }{ɸ' }C2 {Ψ } {ɸ }C 2 {Ψ ' }{Ψ ' ⇒ Ψ }


ConseqD ConseqG
{ɸ }C {Ψ } {ɸ }C {Ψ }

Exemple : Prouvez le triplet ci-dessous.

{x>=0} a:=0;b:=x {a+b>=0}

Conditionnelle

On ajoute ensuite la règle de la conditionnelle, qui exprime bien que la post-condition doit être vérifiée
dans les deux branches du if chacune sous la condition que le résultat du test est conforme à la branche.
En particulier si une des branches est impossible, alors la prémisse correspondante est trivialement
juste (règle CONSEQ).

© Dr. Benamira A. 2020-MSTIC1 9


Computer Science is no more about computers than astronomy is about telescopes.
(E. W. Dijkstra)

{ɸ ∧ B }C 1 {Ψ }{ɸ∧ ¬ B }C 2 {Ψ }
Cond
{ɸ }If B then C1 else C 2 {Ψ }

Exemple : Prouvez le triplet ci-dessous.


{True} if x<0 then x:=-x else x:=x; {x≥0}

While
Idée est d’exhiber un invariant de boucle, c’est-à-dire un prédicat P telle que :
1. (<pré-condition> P )
2. p est vrai après chaque itération

3. ( P ∧¬ B <post-condition>)

{P ∧ B }C {P }
Whil
{P }While B then C {P ∧ ¬ B }

3 Méthode diviser-pour-régner

Il existe de nombreuses façons de concevoir un algorithme. Le tri par insertion utilise une
approche incrémentale : après avoir trié le sous-tableau tab[1 . . j − 1], on insère l’élément tab[j] au bon
emplacement pour produire le sous-tableau trié tab[1 . . j].

Rappel : on prend deux éléments qu'on trie dans le bon ordre, puis un 3e qu'on insère à sa place
parmi les 2 autres, puis un 4e qu'on insère à sa place parmi les 3 autres, etc. C'est la méthode la plus
utilisée pour trier des dossiers, des cartes à jouer.
Le principe de l'algorithme est le suivant. On parcourt le tableau du début à la fin (i = 1à n-1), et à
l'étape i, on considère que les éléments de 0 à i -1 du tableau sont déjà triés. On va alors placer le i-ème
élément à sa bonne place parmi les éléments précédents du tableau, en le faisant « redescendre » jusqu'à
atteindre un élément qui lui est inférieur.
On donne ci-dessous l'algorithme de tri par insertion

Procedure triInsertion(tab :tableau[1..n] d’entiers){


i, j, val :entiers;
Debut
Pour (i allant de 2 à n) faire
val :=tab[i];
j :=i-1;
Tantque ((j > 0) et (tab[j] > val)) faire
tab[j+1] :=tab[j];
j :=j-1;
Fin
tab[j+1] := val;
Fin
Fin

© Dr. Benamira A. 2020-MSTIC1 10


Computer Science is no more about computers than astronomy is about telescopes.
(E. W. Dijkstra)

Cette section va présenter une autre approche de conception, baptisée « diviser pour régner ».
Nous utiliserons cette technique pour créer un algorithme de tri dont le temps d’exécution du cas le plus
défavorable sera très inférieur à celui du tri par insertion.
Nombre d’algorithmes utiles sont d’essence indexrécursivité récursive : pour résoudre le
problème, ils s’appellent eux-mêmes, de manière récursive, une ou plusieurs fois pour traiter des sous-
problèmes très similaires. Ces algorithmes suivent généralement une approche diviser pour régner : ils
séparent le problème en plusieurs sous-problèmes semblables au problème initial mais de taille
moindre, résolvent les sous-problèmes de façon récursive, puis combinent toutes les solutions pour
produire la solution du problème original.
Le paradigme diviser-pour-régner implique trois étapes à chaque niveau de la récursivité :
1. Diviser : le problème en un certain nombre de sous-problèmes.
2. Régner : sur les sous-problèmes en les résolvant de manière récursive. Si la taille d’un
sous-problème est suffisamment réduite, on peut toutefois le résoudre directement.
3. Combiner : les solutions des sous-problèmes pour produire la solution du problème
originel.
L’algorithme du tri par fusion suit fidèlement la méthodologie diviser-pour-régner. Intuitivement,
il agit de la manière suivante :
Diviser : Diviser la suite de n éléments à trier en deux sous-suites de n/2 éléments
chacune.
Régner : Trier les deux sous-suites de manière récursive en utilisant le tri par fusion.
Combiner : Fusionner les deux sous-suites triées pour produire la réponse triée.

Procedure triFusion(tab :tableau[1..n] d’entiers ; a,b :entiers )


Début
Si (b>a) alors
triFusion(tab,a,(a+b)div 2);
triFusion(tab,((a+b)div 2)+1,b);
fusion(tab,a,(a+b)div 2,b);
Finsi
Fin
(********************************************************************)
Procedure fusion(tab :tableau[1..n] d’entiers ; a,b,c :entiers ) ;
entier i,j,k; t1 : tableau[1.. b-a+1] d’entiers ; t2 : tableau[1.. c-b] d’entiers ;
Début
Pour (i allant de 0 à (longueur de t1)-1 ) faire
t1[i] := tab[a+i];
Finpour
Pour (j allant de 0 à (longueur de t1 )-1 pas 1) faire
t2[j] := tab[b+1+j];
Finpour
i := 0; j :=0; k := a;
Tantque (k <= c) faire
Si (i >= longueur de t1) alors
tab[k] := t2[j];
j :=j+1;
Sinon
Si (j >= longueur de t2) alors
tab[k] := t1[i];
i :=- i+1;
Sinon

© Dr. Benamira A. 2020-MSTIC1 11


Computer Science is no more about computers than astronomy is about telescopes.
(E. W. Dijkstra)

Si (t1[i] <= t2[j]) alors


tab[k] := t1[i];
i := i+1;
Sinon
tab[k] := t2[j];
j := j+1;
Finsi
Finsi
Finsi
k++;
Fintantque
Fin

24 6 23 14 1 13

triFusion(T,1,6)

24 6 23 14 1 13

triFusion(T,1,3) triFusion(T,4,6)

24 6 23 14 1 13
triFusion(T,1,2) triFusion(T,3,3) triFusion(T,4,5) triFusion(T,6,6)

24 6 23 14 1 13

Fusion(T,1,1,2) Fusion(T,3,4,5)
6 24
1 14

Fusion(T,1,2,3)
Fusion(T,3,4,6)
6 23 24
1 13 14

Fusion(T,1,3,6)

24 6 23 14 1 13

Fig.1.2 : Application du tri par fusion sur la suite d’entiers : 24, 6,23,14,1,13.

© Dr. Benamira A. 2020-MSTIC1 12


Computer Science is no more about computers than astronomy is about telescopes.
(E. W. Dijkstra)

4 Programmation dynamique
La programmation dynamique, comme la méthode diviser-pour-régner, résout des problèmes en
combinant des solutions de sous-problèmes. (« Programmation », dans ce contexte, fait référence à une
méthode tabulaire et non à l’écriture de code informatique.).
Comme nous l’avons vu à la section précédente, les algorithmes diviser-pour-régner partitionnent
le problème en sous-problèmes indépendants qu’ils résolvent récursivement, puis combinent leurs
solutions pour résoudre le problème initial. La programmation, quant à elle, peut s’appliquer même
lorsque les sous-problèmes ne sont pas indépendants, c’est-à-dire lorsque des sous-problèmes ont des
sous-sous-problèmes communs. Dans ce cas, un algorithme diviser-pour-régner fait plus de travail que
nécessaire, en résolvant plusieurs fois le sous-sous-problème commun. Un algorithme de
programmation dynamique résout chaque sous-sous-problème une seule fois et mémorise sa réponse
dans un tableau, évitant ainsi le recalcul de la solution chaque fois que le sous-sous-problème est
rencontré.
n
Nous allons nous intéresser au calcul du coefficient binomial C p. Une solution consiste à utiliser
la formule de Pascal, ce qui nous amène à écrire :

let rec binom (n,p)=match (n,p) with


(n, 0)-> 1
| (n, p) when (n = p)-> 1
| (n, p)-> binom (n-1,p-1) + binom (n-1, p) ;;

Malheureusement, cette fonction s’avère très peu efficace, même pour de relativement faibles
valeurs de n et p (à titre d’illustration, il faut 93 secondes à mon ordinateur pour calculer C 30
15. La raison
5
en est facile à comprendre : lorsqu’on observe par exemple l’arbre de calcul de C 2 on constate que de
nombreux appels récursifs sont identiques et donc superflus :

Nous pouvons par exemple constater que le calcul de C 52 nécessite de calculer deux fois C 31 et
trois fois C 21.

© Dr. Benamira A. 2020-MSTIC1 13


Computer Science is no more about computers than astronomy is about telescopes.
(E. W. Dijkstra)

4.1 La solution proposée par la programmation dynamique

La solution proposée par la programmation dynamique consiste à commencer par résoudre les
plus petits des sous-problèmes, puis de combiner leurs solutions pour résoudre des sous-problèmes de
plus en plus grands.
Concrètement, le calcul de C 52 se réalise en suivant le schéma suivant :

Pour réaliser ce type de solution on utilise souvent un tableau, ici un tableau bi-dimensionnel (n +
1)×(p + 1)(dont seule la partie pour laquelle j≤ i sera utilisée). Ce tableau sera progressivement rempli
par les valeurs des coefficients binomiaux, en commençant par les plus petits :

Il faut faire attention à bien respecter la relation de dépendance (modélisée par les flèches sur le
schéma ci-dessus) pour remplir les cases de ce tableau : la case destinée à recevoir la valeur de C ij. Ne
i− 1 i− 1
peut être remplie qu’après les cases destinées à recevoir C j −1 et C j .

© Dr. Benamira A. 2020-MSTIC1 14


Computer Science is no more about computers than astronomy is about telescopes.
(E. W. Dijkstra)

let binom n p =let c =Array.make_matrix (n+1) (p+1) 1 in


for i = 2 to n do
for j = 1 to min (i-1) p do
c.(i).(j) <- c.(i-1).(j) + c.(i-1).(j-1)
done ;
done ;
c.(n).(p) ;;

Concrètement, résoudre un problème par la programmation dynamique consiste à suivre les étapes
indiquées ci-dessous :
1. Obtenir une relation de récurrence liant la solution du problème global à celles de problèmes
locaux non indépendants ;
2. Initialiser d’un tableau à l’aide des conditions initiales de la relation obtenue au point
précédent ;
3. Remplir ce tableau en résolvant les problèmes locaux par taille croissante, à l’aide de la relation
obtenue au premier point.

Cette technique est fréquemment employée pour résoudre des problèmes d’optimisation : elle
s’applique dès lors que la solution optimale peut être déduite des solutions optimales des sous-
problèmes (c’est le principe d’optimalité de Bellman, du nom de son concepteur). Cette méthode
garantit d’obtenir la meilleure solution au problème étudié, mais dans un certain nombre de cas sa
complexité temporelle reste trop importante pour pouvoir être utilisée dans la pratique.
Dans ce type de situation, on se résout à utiliser un autre paradigme de programmation, la
programmation gloutonne. Alors que la programmation dynamique se caractérise par la résolution par
taille croissante de tous les problèmes locaux, la stratégie gloutonne consiste à choisir à partir du
problème global un problème local et un seul en suivant une heuristique (c’est à dire une stratégie
permettant de faire un choix rapide mais pas nécessairement optimal). On ne peut en général garantir
que la stratégie gloutonne détermine la solution optimale, mais lorsque l’heuristique est bien choisie on
peut espérer obtenir une solution proche de celle-ci.

Remarque : Un algorithme décrit une séquence d’opérations non ambiguës à réaliser sur des
données pour résoudre un problème. Lorsqu’un algorithme permet la recherche d’une solution à une
problématique, l’arbre du possible à explorer peut-être très grand et croitre de manière exponentielle
avec la taille du problème à traiter. Si cela devient gênant pour converger vers une solution en un temps
raisonnable, il peut être intéressant d’ajouter à l’algorithme certaines règles basées sur l’expérience

© Dr. Benamira A. 2020-MSTIC1 15


Computer Science is no more about computers than astronomy is about telescopes.
(E. W. Dijkstra)

ou l’intuition afin d’éliminer très tôt de grands pans de l’arbre de recherche. Cela permet d’écourter
d’autant l’exploration. Ce sont les heuristiques.
Comme une heuristique est toujours une hypothèse raisonnable, mais non garantie (si c’était
garanti, ce ne serait plus une heuristique), cela signifie aussi que la solution optimale peut être éliminée
dans la foulée. Elles sont utilisées quand l’obtention d’une bonne solution rapidement est plus
intéressante que trouver la meilleure solution en un temps extrêmement long.

4.2 Exemple : Plus longue sous-séquence commune


Une molécule d’ADN peut être vue comme séquence sur l’alphabet {a;c;g;t}. On dit qu’une
séquence S est une sous-séquence de S’ s’il existe un suite croissante d’indices i j , j = 1… |S| tel que Sj
= S’ij . La proximité entre espèce, ou entre membres d’une même espèce, peut être mesurée en estimant
des distances entre molécules d’ADN. Une distance possible est la longueur de la plus longue sous-
séquence commune (PLSSC) :
Le problème plus longue sous-séquence commune (plssc) : Entrée est X et Y deux séquences de
lettres dans un alphabet Σ; et l’objectif : trouver une sous-séquence commune à X et Y de longueur
maximale. Voir TP.

5 Algorithmes gloutons

Les algorithmes pour problèmes d’optimisation exécutent en général une série d’étapes, chaque
étape proposant un ensemble de choix. Pour de nombreux problèmes d’optimisation, la programmation
dynamique est une approche bien trop lourde pour déterminer les meilleurs choix ; d’autres algorithmes,
plus simples et plus efficaces, peuvent faire l’affaire. Un algorithme glouton fait toujours le choix qui
lui semble le meilleur sur le moment. Autrement dit, il fait un choix localement optimal dans l’espoir
que ce choix mènera à une solution globalement optimale. Cette section étudie les problèmes
d’optimisation qui peuvent se résoudre par des algorithmes gloutons.
Les algorithmes gloutons n’aboutissent pas toujours à des solutions optimales, mais ils y arrivent
dans de nombreux cas. Nous commencerons par examiner à la section suivante un problème simple
mais non trivial, à savoir le problème d’empaquetage, pour lequel un algorithme glouton calcule une
solution efficacement. Nous arriverons à l’algorithme glouton en commençant par étudier une solution
basée sur la programmation dynamique, puis en montrant que l’on peut toujours faire des choix
gloutons pour arriver à une solution optimale.

Exemple : empaquetage.

Soit un ensemble d’objets E = {1, . . . , n} de poids p1, . . . , pn, des boites de capacités C
Problème : placer les objets dans les boites en respectant leurs capacités en utilisant le moins
possible de boites.
Méthode : par choix successifs d’un objet et d’une boite
Choix glouton : placer le premier objet dans la première boîte où c’est possible.

© Dr. Benamira A. 2020-MSTIC1 16


Computer Science is no more about computers than astronomy is about telescopes.
(E. W. Dijkstra)

Un bon choix glouton : prendre l’objet de plus grand poids.

Le choix du paquet de plus grand poids n’est pourtant pas optimal.


Poids 6, 3, 3, 5, 5, 2.

© Dr. Benamira A. 2020-MSTIC1 17


Computer Science is no more about computers than astronomy is about telescopes.
(E. W. Dijkstra)

Exemples classiques: Les algorithmes de Prim et de Kruskal de calcul d'un arbre de recouvrement
d'un graphe de poids minimal, l'algorithme des plus courts chemins dans un graphe de Dijkstra, le code
de Huffman, de nombreuses versions de problèmes d'affectations de tâches...

Pour mettre au point un algorithme glouton, il faut donc :


1. Trouver un critère de sélection : souvent facile ... mais pas toujours
2. Montrer que le critère est bon, c.à.d. que la solution obtenue est optimale : souvent dur!
3. L'implémenter : en général facile et efficace !

Exemple : Le problème de sac à doc


Dans le problème de sac à dos, on a n objets disponibles, chacun en un exemplaire, l’objet i ayant
un poids ai et une valeur ci. Le but est d’emporter un sous-ensemble d’objets de valeur maximale, dans
un sac de poids total limité à B. On suppose que la somme des poids excède B, sinon le problème est
trivial (on prend tout !).

© Dr. Benamira A. 2020-MSTIC1 18

Vous aimerez peut-être aussi