Vous êtes sur la page 1sur 19

Une mthode pour laborer des algorithmes itratifs

F. Didier-IREM Campus Luminy didier@irem.univ-mrs.fr Atelier 5a Sminaire des IREM et de la revue "Repre IREM" CIRM (Marseille-Luminy) 15 au 19 mars 2010

1 Construction de programmes itratifs par la mise en vidence de linvariant


La dmarche prsente tente, sans introduire un formalisme excessif, dapporter des lments de rponses aux questions fondamentales qui se posent en algorithmique : Comment laborer un algorithme ? Lalgorithme sarrte-t-il toujours ? Comment prouver quun algorithme rsout effectivement le problme pos ? Une des principales difcults dans llaboration dun algorithme est de contrler son aspect dynamique (cest dire la faon dont il se comporte en fonction des donnes quon lui fournit en entre), en ne considrant que son aspect statique (on na sous les yeux quune suite nie dinstructions). La mthode ci-dessous permet dapporter une aide la mise au point dalgorithmes itratifs et de contrler cet aspect dynamique. Lide principale de la mthode consiste raisonner en terme de situation alors que ltudiant dbutant, lui, raisonne en terme daction. Par exemple, la description dun algorithme de tri commence le plus souvent par des phrases du type : "je compare le premier lment avec le second, sil est plus grand je les change, puis je compare le second avec le troisime. . . . . . ". On nit par sy perdre et ce nest que trs rarement que lalgorithme ainsi explicit arrive tre cod correctement. Le fait de raisonner en terme de situation permet, comme on va le voir, de mieux expliciter et de mieux contrler la construction dun algorithme. Pour terminer cette introduction, on peut ajouter que cette faon de procder peut aussi savrer utile, dans le cas o un algorithme est connu, pour se convaincre ou convaincre un auditoire que lalgorithme rsout bien le problme pos.

1.1 La dmarche
On sait que ce qui caractrise une itration est son invariant. Trouver linvariant dune boucle nest pas toujours chose aise. Lide matresse de la mthode est de construire cet invariant paralllement llaboration de lalgorithme et non pas de concevoir lalgorithme itratif pour ensuite en rechercher linvariant. Etant donn un problme dont on souponne quil a une solution itrative, on va sefforcer de mettre en vidence les tapes suivantes : 1 Proposer une situation gnrale dcrivant le problme pos (hypothse de rcurrence). Cest cette tape qui est peut tre la plus dlicate car elle exige de faire preuve dimagination. On peut toujours supposer que lalgorithme (on le cherche !) a commenc "travailler" pour rsoudre le problme pos et quon larrte avant quil ait ni : On essaye alors de dcrire, de manire 2

trs prcise, une situation dans laquelle les donnes quil manipule puissent se trouver. Il est possible, comme on le verra sur des exemples, dimaginer plusieurs situations gnrales. 2 Chercher la condition darrt. A partir de la situation imagine en [1], on doit formuler la condition qui permet dafrmer que lalgorithme a termin son travail. La situation dans laquelle il se trouve alors, est appele situation nale. 3 Se "rapprocher" de la situation nale, tout en faisant le ncessaire pour conserver une situation gnrale analogue celle choisie en [1]. 4 Initialiser les variables introduites dans la description de la situation gnrale pour que celle-ci soit vraie au dpart ( cest dire avant que lalgorithme ait commenc travailler). Une fois cette tude conduite lalgorithme aura la structure suivante : [4] tant que non [2] faire [3] Cette faon de procder montre comment on prouve la validit de lalgorithme au fur et mesure de son laboration. En effet la situation gnrale choisie en [1] est en fait linvariant qui caractrise la boucle tantque. Cette situation est satisfaite au dpart cause de ltape [4], elle reste vraie chaque itration (tape [3]). Ainsi lorsque la condition darrt est atteinte cette situation nous permet dafrmer que le problme est rsolu. Cest galement en analysant ltape [3] quon peut prouver la terminaison de lalgorithme.

1.2 Tri dun tableau par insertion


Le premier exemple consiste tablir un algorithme permettant de classer suivant lordre croissant un tableau de n nombres. Essayons de trouver une situation gnrale dcrivant le problme pos. Pour cela supposons que lon arrte un algorithme de tri avant que tout le tableau soit class, on peut imaginer quil a commenc mettre de lordre et que par exemple T [0..i] est class. La notation T [0..i] dsigne les i + 1 premires composantes du tableau T . Pour illustrer cette situation on peut soit faire un dessin :
0 n-1

Partie classe

Soit procder, plus formellement, en dcrivant la situation par une formule logique : {j, j [0, i[ T [j] T [j + 1]} 3

[1]
0 n-1

Partie classe

[2] Lalgorithme a termin lorsque i = n 1. [3] Pour se rapprocher de la n, on incrmente dabord i de 1, mais si on veut conserver une situation analogue celle choisie, cela ne suft pas, car il faut que le tableau soit class entre 0 et i. Pour cela "on doit amener T [i] sa place dans T [0..i]". Ceci est un autre problme qui sera rsolu en utilisant la mme dmarche. [4] T [0..0] tant tri, linitialisation i 0 convient. Cette premire analyse nous conduit crire la squence suivante : i0 tant que non i = n 1 faire ii+1 "amener T [i] sa place dans T [0..i]" Linvariant :{j, j [0, i[ T [j] T [j + 1]} joint la condition darrt i = n 1 scrit la sortie de la boucle : {j, j [0, n 1[ T [j] T [j + 1]} Ce qui veut prcisment dire que tout le tableau est class. La dmonstration de la terminaison de cet algorithme est triviale car i est initialis avec 0 et chaque fois que ltape [3] est excute i est incrment de 1, la condition darrt i = n1 sera forcment vrie au bout de n tapes. Toute ralisation de "amener T [i] sa place dans T [0..i]" nous donne un algorithme de tri. Par hypothse on sait que le tableau est class entre 0 et i1, on suppose quun algorithme a commenc travailler et llment qui tait initialement en i sest "rapproch" de sa place par des changes successifs et se trouve en j lorsquon a interrompu lalgorithme. Sur le schma qui suit, llment qui tait initialement en i est matrialis par une toile. [1] Avec T [0..j 1] et T [j..i] classs.
0 n-1

*
j i

[2] Cest termin lorsque j = 0 ou T [j 1] T [j].

[3] Echanger T [j 1] et T [j] j j1 [4] Linitialisation j i satisfait la situation choisie en [1]. Ce qui nous conduit nalement crire lalgorithme de tri par insertion : i0 tant que i = n 1 faire ii+1 ji tant que j = 0 et T [j 1] > T [j] faire Echanger T [j 1] et T [j] j j1 On remarquera que dans cette ultime version les expressions boolennes qui suivent les tantque sont les ngations des conditions darrt. On aurait pu raisonner partir dune situation gnrale sensiblement diffrente de celle choisie : [1]
0 n-1

Partie classe

La nuance rside dans le fait que la partie trie est T [0..i 1], et non T [0..i] comme dans la premire version. Cette situation est tout aussi acceptable que la prcdente. En raisonnant avec celle-ci on obtient un algorithme, certes voisin du prcdent, mais comportant plusieurs modications. La condition darrt devient i = n, le corps de litration consiste "amener llment qui se trouve en i sa place dans T [0..i 1], puis vient lincrmentation de i, linitialisation devient i 1. Comme on peut le constater la situation choisie guide totalement lcriture de lalgorithme.

1.3 Tri dun tableau par slection


Comme il a dj t dit et constat la situation gnrale nest pas unique. Par exemple, toujours pour le mme problme, on peut faire une hypothse plus forte sur la partie classe en ajoutant quelle est aussi dnitivement en place. Ce qui se traduit formellement par une conjonction de formules logiques : 5

{j, j [0, i 1[ T [j] T [j + 1]} et {(j, j [0, i 1] et k, k [i, n 1]) T [j] T [k]} La premire formule indique que le tableau est class jusqu i 1, la seconde formule que cette partie est dnitivement en place. [1]
0 n-1

classe dfinitivement

[2] Cest termin lorsque i = n 1. [3] "Rechercher lindice k du plus petit lment de T [i..n 1]" Echanger T [i] et T [k]. Incrmenter i de 1. [4] Linitialisation i 0 convient. Ce qui nous conduit crire lalgorithme suivant : i0 tant que i = n 1 faire "Rechercher lindice k du plus petit lment de T [i..n 1]" Echanger T [i] et T [k] ii+1 Toute ralisation de "rechercher lindice k du plus petit lment de T [i..n1]" conduit un algorithme de tri. Lanalyse de ce sous problme est laisse au soin du lecteur. Cet algorithme se termine car la variable i est initialise avec 0 et est incrmente chaque fois que ltape 3 est excute, aprs n itrations la condition darrt i = n 1 est atteinte. A la sortie de litration i vaut donc n 1. Ainsi, en remplaant i par n 1 dans les formules logiques qui dcrivent la situation gnrale choisie, on obtient prcisment la dnition du fait que le tableau T est class, ce qui prouve la validit de notre algorithme. Revenons un instant sur ltape [4]. Pourquoi linitialisation i 0 satisfait-elle la situation choisie au dpart ? On peut justier ce choix soit intuitivement, soit formellement. Intuitivement : Lorsque i vaut 0, T [0..i 1] dsigne le tableau vide. On peut dire du tableau vide 6

quil a toutes les proprits que lon veut, puisquil ne possde aucun lment. En particulier, on peut dire que ses lments sont classs et en place. Formellement : A ce stade il est ncessaire de faire quelques rappels : a) Si P est fausse, limplication P Q est vraie. b) La proprit x, x est toujours fausse ( dsigne lensemble vide). Examinons maintenant la formule logique dcrivant la situation gnrale. {j, j [0, i 1[ T [j] T [j + 1]} et {(j, j [0, i 1] et k, k [i, n 1]) T [j] T [k]} Il faut quau dpart (i vaut 0) cette formule soit vraie. Pour quune conjonction de deux formules soit vraie il suft que chacune des deux formules soit vraie. Il est facile de constater que la premire formule est vraie lorsque i vaut 0. En effet cette formule est de la forme P Q, o P est de la forme x, x , car lorsque i vaut 0 lintervalle semi-ouvert [0..i 1[ possde zro lment. La seconde formule et les rappels a) et b) nous permettent encore de conclure.

1.4 La recherche dichotomique


Soit T [0..n 1] un tableau de n lments classs par ordre croissant. Le problme consiste tablir un algorithme qui permette de dire si un lment x appartient ou pas au tableau T . Lalgorithme recherch ne doit pas effectuer une recherche squentielle, mais doit utiliser le principe de la dichotomie : chaque tape de litration, lintervalle de recherche doit tre divis par deux. Pour rechercher un lment dans un tableau ayant un million dlments, au plus vingt comparaisons sufront ! Cet exemple de la recherche dichotomique est trs riche en soi, car les variations de situation sont multiples et la mthode permet de parfaitement contrler ce que lon crit. Il faut ajouter que les versions, la plupart du temps errones, que les tudiants crivent, peuvent tre plus facilement corriges si lon cherche mettre en vidence la situation gnrale partir de laquelle leur algorithme semble construit. 1.4.1 Version a [1]
0

n-1

- ou =

o + indique la zone des lments de T qui sont strictement plus grands que x et - ou = indique la zone des lments infrieurs ou gaux x. [2] Cest termin lorsque d < g [3] k (g + d) div 2 si T [k] x alors g k + 1 sinon d k 1 [4] Les initialisations g 0 et d n 1 conviennent. Ltape [3] conserve de faon vidente la situation choisie en [1]. Dautre part, chaque tape, soit g augmente, soit d diminue, la condition darrt sera toujours atteinte. On obtient : g0 dn1 tant que g d faire k (g + d) div 2 si T [k] x alors g k + 1 sinon d k 1 si d 0 et T [d] = x alors " trouv en d " sinon " pas trouv " Lorsque la situation nale est atteinte, il suft dexaminer les diverses possibits pour conclure. Les deux indices d et g ont chang de valeur :
0

n-1

- ou =

Seul d a chang de valeur :


0

n-1

Seul g a chang de valeur :


0

n-1

- ou =

d g

Attention, il ne faut pas oublier de tester si d < 0, car dans ce cas, accder T [d] dclencherait une erreur. 8

1.4.2 Version b Voici une autre situation, trs voisine de la prcdente, qui conduit un algorithme sensiblement diffrent. [1]
0

n-1

- ou =
g

?
d

[2] Cest termin lorsque d = g [3] k (g + d) div 2 si T [k] x alors g k + 1 sinon d k Cette tape conserve de faon vidente la situation choisie en [1] [4] Les initialisations g 0 et d n conviennent. On obtient : g0 dn tant que g = d faire k (g + d) div 2 si T [k] x alors g k + 1 sinon d k si g > 0 et T [g 1] = x alors " trouv en g-1 "sinon " pas trouv " 1.4.3 Version c Voici une autre situation, trs voisine des prcdentes, qui conduit un algorithme diffrent. [1]
0

n-1

- ou =

[2] Cest termin lorsque d = g + 1 [3] k (g + d) div 2 9

si T [k] x alors g k sinon d k Cette tape conserve de faon vidente la situation choisie en [1] [4]Les initialisations g 1 et d n conviennent. On obtient : g 1 dn tant que g + 1 = d faire k (g + d) div 2 si T [k] x alors g k sinon d k si g 0 et T [g] = x alors " trouv en g "sinon " pas trouv " 1.4.4 Version d Voici une autre situation, trs voisine des prcdentes, qui montre combien il est important de vrier tous les points avant de conclure la validit dun algorithme. [1]
0

n-1

- ou =

[2] Cest termin lorsque g = d [3] k (g + d) div 2 si T [k] x alors g k sinon d k 1 Cette tape conserve de faon vidente la situation choisie en [1] [4] Les initialisations g 1 et d n 1 conviennent. Tout semble parfaitement correct, et pourtant il y a une erreur. On nest pas assur que la condition darrt soit atteinte dans tout les cas. En effet, dans le cas o d devient gal g +1, k (g +d) div 2 est gal g et si le test T [k] x est satisfait linstruction g k ne permet pas g daugmenter et le programme boucle ! Pour tre sr de la terminaison, il faut crire g d 1 pour condition darrt. Lorsque cette condition darrt est atteinte il faut examiner attentivement toutes les situations nales possibles avant de conclure. La conclusion est beaucoup plus 10

dlicate crire. Voici lalgorithme modi : g 1 dn1 tant que g < d 1 faire k (g + d) div 2 si T [k] x alors g k sinon d k 1 si g 0 et T [g] = x alors " trouv en g" sinon si d 0 et T [d] = x alors " trouv en d" sinon "pas trouv" On peut aussi modier le calcul de k pour sassurer de larrt de lalgorithme. En posant k (g + d + 1) div 2 et en supposant g = d, on a les ingalits : g<kd Lorsque ltape 3 est excute soit g k, soit d k 1, dans tous les cas soit g augmente strictement, soit k diminue strictement. La condition darrt g = d sera donc toujours atteinte. On obtient lalgorithme : g 1 dn1 tant que g = d faire k (g + d + 1) div 2 si T [k] x alors g k sinon d k 1 si g 0 et T [g] = x alors " trouv en g" sinon "pas trouv" 1.4.5 Version e Voici encore une autre situation, qui conduit lcriture dun algorithme que lon trouve parfois dans les livres dinformatique. [1]
0

n-1

- ou =
g

+ ou =

[2] Cest termin lorsque g > d [3] k (g + d) div 2 11

si T [k] x alors g k + 1 si T [k] x alors d k 1 Cette tape conserve de faon vidente la situation choisie en [1] [4] Les initialisations g 0 et d n 1 conviennent. On obtient : g0 dn1 tant que g d faire k (g + d) div 2 si T [k] x alors g k + 1 si T [k] x alors d k 1 si g = d + 2 alors "trouv en k" sinon "pas trouv" 1.4.6 Version f Voici une dernire situation qui, comme dans la version prcdente, conduit interrompre litration dans le cas o la dichotomie "tombe pile" sur llment recherch. [1]
Trouve ou
0

n-1

[2] Cest termin lorsque g > d ou trouve [3] k (g + d) div 2 si T [k] = x alors trouve vrai sinon si T [k] < x alors g k + 1 sinon d k 1 Cette tape conserve de faon vidente la situation choisie en [1] [4] Les initialisations g 0, d n 1 et trouve f aux conviennent. On obtient :

12

g0 dn1 trouve f aux tant que g d et non trouve faire k (g + d) div 2 si T [k] = x alors trouve vrai sinon si T [k] < x alors g k + 1 sinon d k 1 si trouve alors "trouv en k" sinon " pas trouv"

1.5 Recherche dun plus petit lment dans un tableau


Soit T [0..n 1] un tableau de n lments. On cherche un algorithme qui aprs avoir parcouru le tableau T une seule fois permet de connatre la valeur du plus petit lment de ce tableau. On peut imaginer comme situation gnrale celle o lalgorithme a examin les i premires "cases" et repr parmi celles-ci la valeur min du plus petit lment. Ce qui conduit lalgorithme suivant. [1]
0

n-1

et {j, j [0, i[ min T [j]} [2] Cest termin lorsque i = n [3] si T [i] min alors min T [i] ii+1 [4] Les initialisations min T [0] et i 1 conviennent.

On obtient : min T [0] i1 tant que i = n faire si T [i] min alors min T [i] ii+1

13

1.6 Recherche de lemplacement du plus petit lment dans un tableau


Soit T [0..n 1] un tableau de n lments. On cherche un algorithme qui aprs avoir parcouru le tableau T une seule fois permet de connatre lindice de la "case" o se trouve un (il peut y en avoir plusieurs) plus petit lment de ce tableau. On peut imaginer comme situation gnrale celle o lalgorithme a examin les i premires "cases" et repr parmi celles-ci lindice k de lendroit o se trouve le plus petit lment. Ce qui conduit lalgorithme suivant. [1]
0

n-1

?
k

et {j, j [0, i[ T [k] T [j]} [2] Cest termin lorsque i = n [3] si T [i] T [k] alors k i ii+1 [4] Les initialisations k 0 et i 1 conviennent.

On obtient : k0 i1 tant que i = n faire si T [i] T [k] alors ki ii+1

1.7 Recherche dans un tableau C1 dune sous suite extraite gale un tableau C2 donn
Soit C1[0..n 1] un tableau de n lments et C2[0..p 1] un tableau de p lments. On cherche un algorithme qui rpond vrai si tous les lments du tableau C2 apparaissent dans le tableau C1 et ce dans le mme ordre dapparition, f aux sinon. Ainsi, deux lments contigus dans le tableau C2 ne sont pas forcment contigus dans le tableau C1. Les tableaux C1 et C2 sont quelconques et peuvent 14

contenir des nombres, des caractres, ou tout autre chose. Limportant est que lon puisse tester lgalit de deux objets entre eux. On peut imaginer la situation suivante : lalgorithme a commenc sa recherche, a dj trouv les j premiers objets du tableau C2 dans le tableau C1, a commenc rechercher C2[j] (matrialis par une toile) dans le tableau C1 et ne la pas trouv dans les cases qui se trouvent entre lemplacement du dernier objet du tableau C1 gal C2[j 1] et i 1. Ce qui nous conduit tablir la situation gnrale suivante : [1]
0

n-1

C1

...

...

...

...

...

P-1

C2

...

[2] Cest termin lorsque i = n ou j = p [3] si C2[j] = C1[i] alors j j + 1 ii+1 [4] Les initialisations i 0 et j 0 conviennent.

On obtient : i0 j0 tant que i = n et j = p faire si C2[j] = C1[i] alors j j+1 ii+1 si j = p alors Rsultat Vrai sinon Rsultat Faux

1.8 Partition dans un tableau


Soit T [0..n 1] un tableau de n lments et soit c llment T [0]. On cherche un algorithme qui par des changes successifs, permette de placer cet lment de manire telle que tous les lments qui lui sont infrieurs ou gaux soient placs 15

sa gauche et les autres sa droite. Cet lment de valeur c serait donc sa place si lon classait le tableau par ordre croissant. On impose de nexaminer quune seule fois les lments et de ne pas utiliser de tableau auxiliaire. Le tableau T doit rester globalement invariant (i.e. aucun lment ne disparat et aucun nouveau ne sintroduit), mais on nexige pas que les deux parties de part et dautre soient classes. On peut imaginer la situation ci-dessous, o ltoile matrialise llment c qui sert raliser la partition. La zone des lments qui lui sont infrieurs ou gaux est indique par - ou = et celle des lments strictement suprieurs par +.

1.8.1 Version a [1]


0 n-1 C

- ou =

+
I

[2] Cest termin lorsque i = n [3] si T [i] > c alors i i + 1 sinon changer T [j + 1] et T [i] changer T [j + 1] et T [j] j j+1 ii+1 [4] Les initialisations c T [0], j 0 et i 1 conviennent. On obtient : c T [0] j0 i1 tant que i = n faire si T [i] c alors changer T [j + 1] et T [i] changer T [j + 1] et T [j] j j+1 ii+1

16

1.8.2 Version b [1]


0 n-1

- ou =

?
i

[2] Cest termin lorsque i = j [3] si T [j + 1] > c alors changer T [j + 1] et T [i] ii1 sinon changer T [j + 1] et T [j] j j+1 [4] Les initialisations c T [0], j 0 et i n 1 conviennent. On obtient : c T [0] j0 in1 tant que i = j faire si T [j + 1] > c alors changer T [j + 1] et T [i] ii1 sinon changer T [j + 1] et T [j] j j+1 1.8.3 Version c On choisit de ne pas propager llment T [0], on ne le mettra sa place quune fois la partition ralise. [1]
0 n-1

- ou =
j

?
i

17

[2] Cest termin lorsque i < j [3] si T [j] > c alors changer T [j] et T [i] ii1 sinon j j+1 [4] Les initialisations c T [0], j 1 et i n 1 conviennent. On obtient : c T [0] j1 in1 tant que i j faire si T [j] > c alors changer T [j] et T [i] ii1 sinon j j+1 T [0] T [i] T [i] c

1.9 Recherche du k me plus petit lment dans un tableau


On peut rordonner partiellement le tableau T en modiant le tri par slection pour mettre leur place les k plus petits lments. Le rsultat sera llment T [k 1] (le tableau tant index partir de 0). On obtient un algorithme en O(kn). Remarquons que cet algorithme nous fournit llment mdian dune suite de nombre en O(n2 ). On peut rpondre de manire plus performante au problme pos en utilisant lalgorithme de partition dans un tableau. Pour cela, on modiera lalgorithme qui effectue la partition du tableau par rapport au premier lment en le gnralisant. Plus prcisment au lieu de partitionner le tableau T entre les indices 0 et n 1 par rapport T [0], on crira un algorithme qui partitionne une partie du tableau T entre les indices g(gauche) et d(droite) par rapport T [g]. Pour rendre plus lisible lalgorithme on suppose que lappel de la fonction partition(T, g, d) renverra pour rsultat lemplacement p 18

de llment T [g] qui a servi effectuer la partition. Cet lment est donc " sa place" si lon ne considre que la portion du tableau T entre les indices g et d. Lide est la suivante : On effectue une premire partition du tableau T entre les indices 0 et n 1, si la position p renvoye est gale k 1, cest termin, sinon on demande nouveau une partition, soit sur la partie gauche entre les indices 0 et p 1, soit sur la partie droite entre les indices p + 1 et n 1 suivant que p > k 1 ou que p < k 1. On ritre ce processus tant que le rsultat de la partition est diffrent de k 1. Ce qui nous conduit crire lalgorithme suivant : g0 dn1 p partition(T, g, d) tant que p = k 1 faire si p > k 1 alors p partition(T, g, p 1) sinon p partition(T, p + 1, d) rsultat T [k 1] En quoi cet algorithme est-il plus performant que le prcdent qui utilisait le tri par slection ? Pour avoir une ide de son comportement lorsque n est trs grand, supposons n gal 2q et que chaque appel la fonction partition divise par 2 lespace de recherche. On effectuera ainsi n comparaisons lors du premier appel puis 2q1 + 2q2 + ... + 2 + 1 comparaisons ( dans le pire des cas) par la suite, soit 2q 1. Ainsi lefcacit de ce second algorithme est en O(2n).

19