• Aucun résultat trouvé

6 Diviser pour régner

N/A
N/A
Protected

Academic year: 2022

Partager "6 Diviser pour régner"

Copied!
8
0
0

Texte intégral

(1)

Version

préliminaire

6.1 Le tri fusion

6.1.1 Partage de listes 6.1.1.1 Code

( s p l i t : ’ a l i s t > ’ a l i s t ’ a l i s t

( s p l i t l ) partage l a l i s t e l en deux l i s t e s de mêmes longueur : s i l e s t de longueur n , ( s p l i t l ) re tou rn e un couple ( l1 , l 2 ) t e l que l1@l2 a l e s mêmes é l é ments que l , avec même m u l t i p l i c i t é et

l 1 et l 2 sont de longueurs r e s p e c t i v e s n ’ et n ’ ’ où n ’ vaut n /2 , a r r o n d i par exc è s et n ’ ’ vaut n /2 , a r r o n d i par dé f a u t .

)

let rec split l = match l with

| [] -> [], []

| x::r -> let l1, l2 = split r in x::l2, l1

;;

6.1.1.2 Correction

On peut montrer que pour toute liste l de longueur n, l’appel split l termine en effectuantn appels récursifs supplémentaires par récurrence surn. On montre en même temps que cet appel retourne deux listes l1 etl2 de longueurs respectives �n�et�n�.

6.1.1.3 Complexité

De plus, à chaque appel desplit, on effectue uniquement un nombre borné d’opéra- tions qui sont toutes de temps constant, sauf l’éventuel appel récursif. On en déduit que l’exécution de split lprend un temps n+ 1×O(n) =O(n).

6.1.2 Fusion de listes triées 6.1.2.1 Code

(∗ merge : ’ a l i s t > ’ a l i s t ’ a l i s t

( merge l 1 l 2 ) re to urn e une l i s t e contenant l e s mêmes é l é ments que l , avec même m u l t i p l i c i t é .

De plus , s i l 1 et l 2 sont des l i s t e s t r i é es par ordre c r o i s s a n t ,

(2)

Version

préliminaire

a l o r s ( merge l 1 l 2 ) e s t une l i s t e t r i é e .

)

let rec merge l1 l2 = match l1, l2 with

| [], l -> l

| l, [] -> l

| x1::r1, x2::r2 -> if x1 <= x2 then x1 :: merge r1 l2 else x2 :: merge l1 r2

;;

6.1.2.2 Correction

Montrons que pour toutes listes l1 et l2 triées par ordre croissant, l’appel fusion l1 l2 termine et retourne une liste l triée par ordre croissant dont les éléments sont les mêmes que ceux del1@l2 (avec même multiplicité).

Pour cela, notons pour n∈ N, P(n) l’assertion «pour toutes listes l1 et l2 triées par ordre croissant dont la somme des longueurs vautn, la fonction fusion l1 l2 termine et retourne une listeltriée par ordre croissant dont les éléments sont les mêmes que ceux del».

— MontronsP(0). Deux listes dont la somme des longueurs vaut0sont nécessairement vides. Orfusion [] []retourne[]qui possède les mêmes éléments que[]@[]

et qui est triée. On a doncP(0).

— Montrons queP est héréditaire. Pour cela, considérons n∈Nquelconque vérifiant P(n) et montrons P(n+ 1). Considérons donc deux listes l1 et l2 triées par ordre croissant dont la somme des longueurs vaut n+ 1. On peut alors distinguer trois cas :

— Si l1 est vide, fusion l1 l2 retourne l2, qui est triée par ordre croissant et possède les mêmes éléments quel1@l2.

— Si l2 est vide, on a le même résultat.

— Si ni l1, ni l2 ne sont vides. Alors, il y a deux sous-cas :

— Si x1 ≤ x2, alors fusion l1 l2 retourne le résultat de l’évaluation de x1

:: fusion r1 l2. Orr1 etl2 sont triées et la somme des longueurs de r1 etl2 vautn. CommeP(n) est vrai,fusion r1 l2 retourne une liste ltriée ayant les mêmes éléments que r1@l2.x1 :: fusion r1 l2 retourne donc une liste ayant les mêmes éléments quex1::r1@l2, donc que l1@l2. De plus, l1 et l2 sont triées donc x1 minore tous les éléments de r1 et x2 tous ceux de l2. Comme de plus x1 ≤x2,x1 minore tous les éléments de r1@l2, donc de l, qui est triée. Donc 1x::l est triée. L’appel fusion l1 l2 termine et retourne une liste triée contenant les mêmes éléments que l1@l2.

— Si x1 > x2, on montre de même que l’appel fusion l1 l2 termine et re- tourne une liste triée contenant les mêmes éléments que l1@l2.

P est donc héréditaire.

On en déduit queP(n) est vrai pour tout entier n.

Soitl1 etl2 deux listes triées. Notonsn la somme de leur longueur. On déduit du fait qu’on aP(n), que l’appelfusion l1 l2 termine et retourne une liste triée contenant les

(3)

Version

préliminaire

mêmes éléments quel1@l2.

6.1.2.3 Complexité

On peut noter qu’à chaque appel defusion, on effectue uniquement un nombre borné d’opérations, qui sont toutes de temps constant, sauf l’appel récursif. On en déduit que la complexité temporelle de l’exécution defusion l1 l2 est unO(n)oùnest la somme des longueurs del1 etl2.

6.1.3 Tri fusion 6.1.3.1 Code

(∗ t r i _ f u s i o n : ’ a l i s t > ’ a l i s t

( t r i _ f u s i o n l ) t r i e l a l i s t e l par ord re c r o i s s a n t , i . e . re tou rn e une l i s t e t r i é e par ordre c r o i s s a n t ayant l e s mêmes é l é ments que l , avec même m u l t i p l i c i t é .

)

let rec tri_fusion l = match l with

| [] -> []

| [_] -> l

| _ -> let l1, l2 = split l in merge (tri_fusion l1) (tri_fusion l2)

;;

6.1.3.2 Correction

Par l’absurde, supposons que le code detri fusionne respecte pas sa spécification, c’est-à-dire qu’il existe au moins une liste l telle que tri fusion l ne termine pas ou ne retourne pas une liste triée par ordre croissant ayant les mêmes éléments que l et choisissons lde longueur minimale.

On a alors trois cas :

— l est vide et tri fusion l retourne [] qui est une liste triée ayant les mêmes éléments que l. C’est absurde.

— lcontient un unique élément, et tri fusion l retournel qui est triée et possède les mêmes éléments qu’elle-même. C’est absurde.

— l contient au moins deux éléments. Notons n sa longueur. Alors notons l1 et l2 les listes retournées par l’évaluation de split l. l1 et l2 sont respectivement de longueurs�n/2�et�n/2�. Commen≥2,n−�n/2�=�n/2� ≥1, doncn >�n/2� ≥

�n/2�.l1 etl2 sont donc de longueurs strictement inférieures à celle de l. Comme l est un contre-exemple de longueur minimale à la spécification de tri fusion, tri fusion li, pouri= 1,2, retourne donc une listelitriée ayant même éléments queli. Ortri fusion lretourne une liste qui est obtenue par fusion de l1 etl2. Cette liste possède donc les mêmes éléments que l1@l2, donc quel1@l2, et de plus elle est triée, ce qui est absurde.

(4)

Version

préliminaire

Donc dans tous les cas, on obtient une absurdité.

On en déduit que le code detri fusion respecte sa spécification : pour toute liste l,tri fusion ltermine et retourne une liste triée par ordre croissant ayant les mêmes éléments quel.

6.1.3.3 Complexité

6.1.3.3.1 Équation à résoudre Notons, pour n ∈ N, CT F(n) le temps de calcul mis dans le cas le pire partri fusion l pour une listel de longueur n.

Les opérations effectuées par tri fusionsur une liste l de longueurn≥2 sont un filtrage (en temps constant), un appel à split l en temps O(n), un appel à fusion sur deux listesl1 etl2 de longueurs respectives�n/2�et�n/2�, ce qui demande un temps O(�n/2� +�n/2�) = O(n), et deux appels récursifs à tri fusion sur des listes de longueurs �n/2� et �n/2�, dont les temps de calcul sont donc au plus respectivement CT F(�n/2�) etCT F(�n/2�).

Le temps de calcul detri fusion lest donc au plusO(n)+O(1)+O(n)+CT F(�n/2�)+

CT F(�n/2�).

Cette inégalité étant vérifiée pour toute liste de longueur l, le temps de calcul de tri fusionsur une liste de longueur ndans le cas le pire vérifie :

CT F(n)≤C��n 2

��

+C��n 2

��

+O(n)

6.1.3.3.2 Résolution Ou bien on suppose queCT F est croissante, ou bien on introduit plutôt CT F (n), le temps de calcul detri fusion dans le cas le pire pour une liste de longueurau plus n.

On a, pour toutn∈N,CT F(n)≤CT F (n)≤CT F (n+ 1)(pour toute liste de longueur n,tri fusionmet un temps au plus égal àCT F (n)et pour toute liste de longueur au plusn,tri fusionmet un temps au plusCT F (n+ 1)).

DoncCT F majore CT F et est croissante.

De plus, on a :

CT F (n)≤C��n 2

��+C��n 2

��+O(n)

Il suffit de reprendre la démonstration précédente pour se convaincre que c’est le cas.1 On peut maintenant essayer de calculerCT F pour des valeurs particulières. Pourk∈N, posonsuk=CT F (2k).

Alors, à partir d’un certain ranguk≤2uk1+C2k où C est une constante.

Une solution particulière de l’équation homogène ∀k ∈ N uk = 2uk−1 est la suite

�2k

k∈N. Posons doncλk=uk2k pour k∈N.

1. Une autre possibilité est de remarquer que pour tout n N, CT F (n) = maxi∈[[0,n]](CT F(i)) et d’effectuer un travail (pénible) sur les inégalités.

(5)

Version

préliminaire

On a alors :

λk2k≤2kλk1+C2k APCR

λk≤λk1+C APCR

λk−λk1 ≤C APCRk0

λk−λk0 ≤(k−k0)C pourk≥k0 λk≤(k−k0)C+λk0 pourk≥k0

λk=O(k) uk=O(k2k)

Or pour toutn, on an≤2log2n etCT F est croissante. Donc, on a : CT F(n)≤CT F (n)≤CT F

2log2n

=ulog2n=O�

�log2n�2log2n� Or O�

�log2n�2log2n

=O(nlog2n)etCT F(n)≥0. Donc,finalement :

CT F(n) =O(nlogn)

6.1.4 Conclusion sur le tri fusion

Le tri fusion est un exemple ultra-classique du paradigme «diviser pour régner». Il est moins naturel que le tri par insertion que nous avons étudié précédemment, et est donc un peu plus délicat à programmer. Cette difficulté supplémentaire valait-elle le coup ?

Le tri par insertion possède une complexité temporelle en O(n2) pour une liste de longueur n. Le tri fusion possède quant à lui une complexité O(nlogn). On peut donc dire qu’on a gagné un facteurn/lognmais ce n’est pas très parlant.

Essayons de quantifier ce gain en regardant le temps de calcul de programmes qui de- manderaient respectivementn,nlog2n,n2,2netn!instructions pour un même problème de taillen, en supposant que le temps de calcul d’une instruction est d’une nano-seconde.

Le tableau 6.1 donne ces temps de calcul pour différentes valeurs den. On voit que les complexités linéarithmique (O(nlogn)) et quadratique (O(n2)) diffèrent vite.

6.2 Tri par pivot

6.2.1 Généralités

Ce tri est aussi appelé tri rapide (ou quicksort).

Remarque 6.1. Ce nom risque de vous induire en erreur : si le tri par pivot est effecti- vement très rapide en pratique dans les meilleurs cas, il est en pratique délicat à mettre en œuvre si on ne veut pas se trouver dans le pire des cas, où il se comporte plutôt mal.

(6)

Version

préliminaire

n nns nlog2nns n2 ns 2nns n! ns

10 10 ns 30 ns 102ns 1µs 3 ms

20 20 ns 90 ns 4×102ns 1×101ms 8×101ans 30 30 ns 102ns 9×102ns 1 s 8×1015ans 40 40 ns 2×102ns 2µs 2×101min

50 50 ns 3×102ns 3µs 10 jours

60 60 ns 4×102ns 4µs 40 ans

70 70 ns 4×102ns 5µs 4×104ans 80 80 ns 5×102ns 6µs 4×107ans 90 90 ns 6×102ns 8µs 4×1010ans 102 102ns 7×102ns 10µs

103 1µs 10µs 1 ms

104 10µs 1.3×102µs 100 ms

105 102µs 1.7 ms 10 s

106 1 ms 2.0×101ms 1.7×101min 107 10 ms 2.3×102ms 28 h 108 100 ms 2.7 s 1×102jours 109 1 s 3×101s 3×101ans

Table6.1 – Temps d’exécution pour quelques complexités classiques

Le tri par pivot consiste, lorsque la liste�à trier est assez grande, à effectuer les étapes suivantes :

1. On choisit dans� un élément quelconque v, qui sera appelé pivot, et on construit les listes u1 etu2 des autres éléments de � contenant respectivement les éléments inférieurs ou égaux xet ceux strictement supérieurs à x.

2. On trie récursivement u1 et u2, ce qui donne deux listes triées, appelées respecti- vement v1 etv2.

3. Il ne reste plus alors qu’à renvoyerv1 @ (x :: v2).

6.2.2 Code tri pivot Exercice 6.2

On rappelle qu’il existe une fonction filter : (α list → bool) → α list → α list dans le moduleList : filter p l retourne la sous-liste des éléments x de l tels que p x soit le booléen true.

Quelle est sa complexité temporelle et spatiale ?

(7)

Version

préliminaire

6.2.2.1 Fonction de partionnement d’une liste On introduit la fonction suivante :

partition : α × α list → α list ×α list

partition x l retourne deux listesu etvtelles queu@v etl possèdent les mêmes éléments (avec la même multiplicité) et tous les éléments deu sont inférieurs ou égaux àx et tous les éléménts de v sont strictement supérieurs àx

let partition x l =

letu = filter (funy → y ≤ x) l in letv = filter (funy → y > x) l in (u, v)

Exercice 6.3

Quelle est la complexité de la fonction partition en espace et en temps ? quicksort : α list → α list

quicksort l retourne une liste u triée ayant les mêmes éléments que l avec la même multiplicité.

let rec quicksort l = matchl with

| [ ] → [ ]

| x :: r → let(u1, u2) = partition x r in let v1 = quicksort u1 in let v2 = quicksort u2 in v1 @ (x ::v2)

6.2.3 Complexité

Considérons une liste lde longueur n, avec n >0. Notre fonction partitionne l privée de son premier élément, pour obtenir deux listes u1 et u2. En notant n1 et n2 leurs longueurs respectives, on a n1+n2 =l−1.

Le coût en tempsquicksort l, aveclnon vide est égal à la somme des coûts des deux appels récursifs, plus celui du partitionnement et de la concaténation, plus quelques coûts en temps constants (filtrage de la liste, destructuration du couple renvoyé parpartition, cons).

Le coût en temps de partition étant dominé par la taille de la liste passée en argu- ment, on peut écrire qu’il existe deux constantes α etβ telles que le temps de calcul de partition x r est majoré parα|r|+β pour toute lister et toutx.

Il existe donc une constanteγ telle que le temps de calcul dequicksort l est majoré par α(n−1) +β+γ, plus le temps de calcul des appels récursifs.

Notons N le nombre d’appels total à quicksort effectués lors de l’évaluation de quicksort l et S la somme des longueurs des listes lors de tous ces appels. Le temps d’exécution dequicksort l est majoré parαS+ (β+γ−α)N.

OrαS+ (β+γ−α)N ≤C(S+N), où C= max(α,β+γ−α).

(8)

Version

préliminaire

Donc le temps d’exécution dequicksort l est dominé parN +S.

Notons, pourn∈N, lors de l’évaluation dequicksort l, oùlest une liste de longueur n, la somme du nombre total d’appels à quicksort et de la somme des longueurs de toutes les listes sur lesquellesquicksortdans le cas le pire (c’est-à-dire pour une listel de longueurnpour laquelle cette somme est maximale).

Pour toute valeur den >0, il existek∈[[0, n[[vérifiantP(n)≤P(k)+P(n−1−k)+n. De plusP(0) = 1.

On montre alors que pour tout n, on a P(n) ≤ (n+ 1)2. En effet supposons que ce ne soit pas le cas et considérons unnminimal constituant un contre-exemple. Si n= 0, c’est absurde, car P(0) = 1 ≤ (n+ 1)2. Si n > 0, alors il existe k ∈ [[0, n[[ vérifiant P(n)≤P(k) +P(n−1−k) +n. On ak < netn−1−k < n, donc par maximalité de n,P(k)≤(k+ 1)2 etP(n−1−k)≤(n−1−k+ 1)2.

On en déduit

P(n)≤(k+ 1)2+ (n−k)2+n

≤k2+ 2k+ 1 +n2−2nk+k2+n

≤n2+ 2n+ 1 + 2k2+ 2k−2nk−n

≤(n+ 1)2+ 2k(k+ 1−n)−n

≤(n+ 1)2

Le temps d’exécution de quicksort l, dans le cas le pire, est donc O(n2).

Cette majoration est serrée, comme on peut s’en convaincre en regardant ce qu’il se passe lorsqu’on donne une liste triée à notre fonctionquicksort.

On peut montrer que, dans le cas le meilleur, le tri rapide trie une liste de taille nen O(nlogn).

Enfin, on peut montrer que, si la liste ne comporte aucun doublon et que l’on choisit à chaque appel le pivot au hasard uniformément dans la liste, alors l’espérance du temps de calcul estO(nlogn).

Références

Documents relatifs

(Tri rapide) Dans l'algorithme de tri rapide, on choisit récursivement un pivot, dans cet exercice le premier élément de la liste, et on place à sa gauche les éléments qui lui

Logiciel Applicatif, informatique L’aspect logiciel couvre l’ensemble des composants logiciels qui automatisent une partie des actions du système. Physique Déploiement À

Pour que l’urbanisation puisse apporter ses avantages en terme d’alignement stratégique, il faut que chaque partie s’approprie le concept de processus et soit capable de

o Pilotage dynamique (quelle version de X utilise quelle version de Y) D eu xiè m e P ar tie :In fra st ru c tu re d ’in té g ra tio n.. Intégration par le « front office » -

comprendre implicitement que les coûts récurrents d’une DSI sont proportionnels à la taille du parc applicatif. • Le premier levier de réduction des coûts d’une DSI est

Deux types de tableaux de bord sont à considérer : Le tableau de bord de conduite de projet pour piloter la mise en place de la structure de gouvernance et les tableaux

— dans le cas où n est une puissance de 2, et donc les moitiés toujours de taille égale, s’il y a deux majoritaires différents avec le mêmne nombre d’occurences, alors il ne peut

La figure 7 illustre les trois premiers appels récursifs de la fonction récursive multiply dont le code est présenté figure 2, avec pour paramètres x = 105 et y = 253 (pour des