• Aucun résultat trouvé

Faciliter la r´esolution de certains probl`emes

d´efinition r´ecursive directe, mˆeme si elle reste possible, s’av`ere plus difficile `a concevoir, pour le programmeur, qu’une d´efinition it´erative.

8.2.1 Rang

Il s’agit de trouver le rang du premier ´el´ement d’une liste de type quel- conque v´erifiant une condition donn´ee. La sp´ecification de cette fonction est donc :

rang : (’a ->bool) ->’a list ->int

Si aucun ´el´ement de la liste ne v´erifie la condition, alors le r´esultat doit ˆetre ´egal `a 0.

8.2. Faciliter la r´esolution de certains probl`emes 101 Quelles sont les difficult´es d’une d´efinition r´ecursive directe? Apparem- ment, il est facile d’´etablir une ´equation :

rang C x ::r = si C x alors 1

sinon 1 + rang C r

Cette ´equation n’est pas valable lorsque la liste est vide. Dans ce cas, quelle d´efinition donner? Dans une liste vide, aucun ´el´ement ne v´erifie la condition, donc le r´esultat doit ˆetre 0. Selon cette analyse, le codage CAML sera le suivant :

# let rec rang C l = if vide l then 0

else if C (hd l) then 1

else 1+rang C (tl l) ;;

rang : (’a -> bool) -> ’a list -> int = <fun>

Cependant, cette d´efinition n’est pas correcte, comme le montre le proces- sus d’ex´ecution ci-dessous, avec C x = x<0 (rang du premier ´el´ement n´egatif dans une liste d’entiers):

rang C [2 ; 6 ; 1] = 1 + rang C [6 ; 1] = 1 + 1 + rang C [1] = 1 + 1 + 1 + rang C [] = 1 + 1 + 1 + 0 = 3

En fait, le probl`eme vient de ce que la liste vide constitue une condition d’arrˆet correspondant au cas o`u aucun ´el´ement ne v´erifie la condition (cas de parcours exhaustif). Dans ce cas, l’erreur vient de ce que la d´efinition propos´ee incr´emente le r´esultat obtenu lors de chaque retour d’appel. Il fau- drait donc disposer d’une information suppl´ementaire permettant d’effectuer un test, dans l’expression du r´esultat, selon qu’un appel sur la liste vide a eu lieu ou non. Plutˆot que d’essayer de r´esoudre directement ce probl`eme, nous allons examiner une solution it´erative.

Soit ` = [x1; x2; . . . ; xn] la liste argument, dans le cas o`u elle n’est pas vide.

Le parcours de cette liste avec gestion du rang met en œuvre trois suites :

rang : 1 2 . . . k − 1 k . . .

tete : x1 x2 . . . xk−1 xk . . . reste : [x2; . . . ; xn] [x3; . . . ; xn] . . . [xk; . . . ; xn] [xk+1; . . . ; xn] . . .

Invariant: tete = xrang, reste = [xrang+1; . . . ; xn], 1 ≤ rang ≤ n

Progression: rang tete reste = x :: r  −→ rang + 1 x r  

Valeurs initiales: rang = 1, tete = x1, reste = [x2; . . . ; xn]

Codage CAML # let rang iter C l =

if vide l then 0 else

let rec progression rang tete reste = if vide reste

then 0

else if C (hd reste) then rang

else progression (rang+1) (hd reste) (tl reste) in progression 1 x1 r1 ;;

rang iter : (’a -> bool) -> ’a list -> int = <fun>

Exemples de processus d’ex´ecution :

rang_iter (function x -> x<0) [5 ; 7; 4 ; 8] = progression 1 5 [7 ; 4 ; 8] = progression 2 7 [4 ; 8] = progression 3 4 [8] = progression 4 8 [] = 0 rang_iter (function x -> x<0) [5 ; 7; 4 ; -1 ; 8] = progression 1 5 [7 ; 4 ; -1 ; 8] = progression 2 7 [4 ; -1 ; 8] = progression 3 4 [-1 ; 8] = progression 4 (-1) [8] = 4

8.2.2 Probl`eme du Comit´e

Un comit´e est compos´e membres, chaque membre ´etant repr´esentant d’un parti A ou d’un parti B. Il y a, initialement, nA (resp. nB) membres du parti A (resp B). Le renouvellement du comit´e, annuel, s’effectue par rem- placement al´eatoire d’un des membres, selon l’algorithme suivant : un premier tirage au sort d´etermine le parti du membre sortant, puis un deuxi`eme ti- rage (ind´ependant du premier) d´etermine le parti du membre entrant. Un des

8.2. Faciliter la r´esolution de certains probl`emes 103 membres du parti sortant est alors remplac´e par un nouveau membre du parti entrant (le nombre total reste constant, soit N ). Le probl`eme consiste `a simu- ler l’´evolution de ce comit´e jusqu’`a ce qu’un des deux partis soit ´elimin´e; le r´esultat est alors le couple (nom du parti ´elimin´e, nombre d’ann´ees).

Les deux tirages sont effectu´es selon une loi pond´er´ee par le nombre res- pectif de repr´esentants des deux partis, avant remplacement. Autrement dit, le r´esultat de chacun des tirages est une variable al´eatoire Y pouvant ˆetre ´egale `a A ou B, avec les probabilit´es

P(Y = A) = xA

xA+ xB, P(Y = B) =

xB

xA+ xB

o`u xA et xB d´esignent respectivement le nombre de membres des partis A et B au moment du tirage. En CAML, on dispose de la fonction pr´ed´efinieran- dom int : int ->intqui d´elivre un entier tir´e au hasard selon la loi uniforme entre 0 et son argument. Le tirage peut donc ˆetre simul´e par l’ex´ecution de la fonction d´efinie ci-dessous :

# let tirage xA xB = let x=random int (xA+xB-1) in if x<xA then ‘A‘ else ‘B‘ ;;

tirage : int -> int -> char = <fun>

L’´evolution du comit´e est mod´elis´ee `a l’aide de trois suites :

xA = nombre de membres du parti A

xB = nombre de membres du parti B

compt = nombre d’ann´ees

Invariant:

xA = nombre de membres du parti A l’ann´ee compt

xB = nombre de membres du parti B l’ann´ee compt

xA + xB = N

Condition d’arrˆet : xA = 0 (r´esultat (‘A‘, compt) ou xB = 0 (r´esultat (‘B‘,compt)).

Progression : soit c1 et c2 les r´esultats des deux tirages au sort successifs. Selon les valeurs du couple (c1,c2) (il y en a quatre possibles) on incr´emente (resp. d´ecr´emente) de 1 les valeurs de xA et xB; dans tous les cas, compt augmente de 1.

Valeurs initiales : xA = nA, xB = nB,compt = 0 Codage en CAML

# let comite nA nB =

if xA = 0

then (‘A‘, compt) else if xB = 0

then (‘B‘, compt) else

let c1=tirage xA xB and c2=tirage xA xB in match (c1, c2) with

if c1=‘A‘ & c2=‘A‘ or c1=‘B‘ & c2= ‘B‘ then progression (compt+1) xA xB else if c1=‘A‘ & c2=‘B‘

then progression (compt+1) (xA-1) (xB+1) else (* c1=‘B‘, c2=‘A‘ *)

progression (compt+1) (xA+1) (xB-1) in progression 0 nA nB;;

comite : int -> int -> int*char = <fun>

Remarque : ce programme est un exemple qui se prˆeterait bien `a l’utilisa- tion des filtres. Bien que nous n’ayons pas souhait´e retenir ce style de program- mation dans ce cours, on montre ci-dessous ce que ¸ca donnerait (ceci illustre la concision de style en CAML; de plus, le programme est plus compr´ehensible). # let comite nA nB =

let rec progression compt = function (0, ) -> (‘A‘, compt)

| ( ,0) -> (‘B‘, compt) | (xA,xB) ->

let c1=tirage xA xB and c2=tirage xA xB in match (c1, c2) with

(‘A‘,‘A‘) | (‘B‘,‘B‘) -> progression (compt+1) (xA,xB)

(‘A‘, ‘B‘) -> progression (compt+1) ((xA-1), (xB+1)) (‘B‘, ‘A‘) -> progression (compt+1) ((xA+1), (xB- 1))

in progression 0 (nA, nB);;

> Toplevel input:

>...match (c1,c2) with

> (‘a‘, ‘a‘) — (‘b‘,‘b‘) -> progression (compt+1) (xa, xb) > —(‘a‘,‘b‘) -> progression (compt+1) ((xa-1),(xb+1)) > —(‘b‘,‘a‘) -> progression (compt+1) ((xa+1),(xb-1)) > Warning: pattern matching is not exhaustive

comite : int -> int -> int*char = <fun>

(le message warning n’est qu’un avertisssement, indiquant que tous les cas de valeurs de couples(c1,c2)n’ont pas ´et´e pr´evus. Si on veut ´eviter ce message, il suffit de rajouter un cas| -> failwith "tirage imprevu!").

8.3. Simulation d’it´erations 105