algorithmes de tri interne - wordpress.com · 2020. 2. 13. · tri ?ift2015 h2020 ?udem ?miklós...

28
Tri ? IFT2015 H2020 ? UdeM ? Miklós Csűrös A LGORITHMES DE TRI INTERNE

Upload: others

Post on 24-Jan-2021

0 views

Category:

Documents


0 download

TRANSCRIPT

  • Tri ? IFT2015 H2020 ? UdeM ? Miklós Csűrös

    ALGORITHMES DE TRI INTERNE

  • Clés comparables

    Tri ? IFT2015 H2020 ? UdeM ? Miklós Csűrös ii

    On a un fichier d’éléments avec clés comparables— on veut les ranger selon l’ordre les clés

    Clés comparables en Java :

    public interface Comparable{

    int compareTo(T autre_objet);}

    public interface Comparator{

    int compare(T un_objet, T autre_objet);}

    x.compareTo(y) est négatif si x précède y, ou positif si x suit y dans l’ordre«naturel» des éléments

  • Tri interne

    Tri ? IFT2015 H2020 ? UdeM ? Miklós Csűrös iii

    Tri interne : tout le fichier est en mémoire(représenté par un tableau ou une liste chaînée)

    Tri externe : fichier stocké partiellement ou entièrement en mémoire externe(disque)— accès à mémoire externe est couteux. . .

    Entrée : tableau A[] de données — on veut le trier

    Solutions :

    N tri par sélection (selection sort)N tri par insertion (insertion sort)N tri fusion (Mergesort)N tri par tas (Heapsort)N tri rapide (Quicksort)

  • Créer le désordre

    Tri ? IFT2015 H2020 ? UdeM ? Miklós Csűrös iv

    comment ordonner un tableau au hasard ?= comment créer une permutation uniformement distribuée

    récurrence pour la permutation uniforme des éléments d’un ensemble A :un élément a ∈ A pour placer au premier + permutation de A \ {a}

    SHUFFLE(A[0..n− 1]) // met les éléments de A dans un ordre aléatoireU1 for i← 0,1, . . . , n− 1 doU2 j ← i+ rnd(n− i) // rnd(m) entier pseudo-aléatoire sur 0..m− 1U3 échanger A[i]↔ A[j] //j ∈ {i, i+ 1, . . . , n− 1}

    �on peut défaire un tri en temps linéaire

  • Tri par sélection

    Tri ? IFT2015 H2020 ? UdeM ? Miklós Csűrös v

    Algo TRI-SELECTION(A[0..n− 1])S1 for i← 0,1, . . . , n− 2 doS2 minidx← iS3 for j ← i+ 1, . . . , n− 1 doS4 if A[j] < A[minidx] then minidx← j

    // maintenant A[minidx] = min{A[i], . . . , A[n]}S5 if i 6= minidx then échanger A[i]↔ A[minidx]

    Complexité :

    ? comparaison d’éléments n(n−1)2 ∼ n2/2 toujours ;

    ? échange d’éléments [ligne S5] ≤ (n− 1) foisTemps de calcul : toujours quadratique�mais pas nécessairement une mauvaise idée si l’échange est beaucoup plus cherque la comparaison

  • Tri par insertion

    Tri ? IFT2015 H2020 ? UdeM ? Miklós Csűrös vi

    public void isort(double[] A){

    for (int i=1; i

  • Tri par tas

    Tri ? IFT2015 H2020 ? UdeM ? Miklós Csűrös vii

    HEAPSORT(A[1..n]) // tri du tableau A[1..n]H1 for i← bn/2c, . . . ,1 do sink(A[i], i) // heapifyH2 while n > 1 doH3 échanger A[1]↔ A[n]H4 sink(A[1],1)H5 n← n− 1

    Complexité : ∼ 2n lgn comparaisons et ∼ n lgn échanges au pire

    Mémoire : tri en place (aucun tableau auxiliare nécessaire)

  • Tri fusion

    Tri ? IFT2015 H2020 ? UdeM ? Miklós Csűrös viii

    9 6 3 0 2 1 8 7 5 4

    9 6 3 0 2 1 7 5 4

    division 50-50% sans réarrangement

    récursion

    0 2 3 6 9 1

    récursion

    4 5 7 8

    fusionner en O(n)

    8

    n/2 éléments n/2 éléments

    0 1 2 3 4 5 6 7 8 9

    Algo MERGESORT(A[0..n− 1], g, d) // premier appel avec g = 0, d = n// récursion pour trier le sous-tableau A[g..d− 1]

    M1 if d− g < 2 then return // cas de base : tableau vide ou un seul élémentM2 m←

    ⌊(d+ g)/2

    ⌋// m est au milieu

    M3 MERGESORT(A, g,m) // trier partie gaucheM4 MERGESORT(A,m, d) // trier partie droiteM5 FUSION(A, g,m, d) // fusion des résultats

  • Fusion «en place»

    Tri ? IFT2015 H2020 ? UdeM ? Miklós Csűrös ix

    FUSION(A, g,m, d) doit fusionner les sous-tableauxA[g..m−1] etA[m..d−1]avec m =

    ⌊(d+ g)/2

    ⌋, et placer le résultat dans A[g..d− 1]

    solution : utiliser un tableau auxiliare aux[0..n − 1], et copier aux[g..d − 1] ←A[g..d− 1] pour la fusion

    Algo FUSION(A[ ], g,m, d) // fusion «en place» pour A[g..m− 1] et A[m..d− 1]F1 for i← g, g + 1, . . . , d− 1 do aux[i]← A[i]F2 i← g ; j ← m ; k ← gF3 while i < m && j < d doF4 if aux[i] ≤ aux[j] then A[k]← aux[i] ; i← i+ 1; k ← k + 1F5 else A[k]← aux[j] ; j ← j + 1; k ← k + 1F6 while i < m do A[k]← aux[i] ; i← i+ 1; k ← k + 1F7 while j < d do A[k]← aux[j] ; j ← j + 1; k ← k + 1 // n’est pas nécessaire

    � à noter que la boucle de la Ligne F7 n’est pas nécessaire car A[j..d − 1] =aux[k..d− 1]

  • Fusion — arrangement bitonique

    Tri ? IFT2015 H2020 ? UdeM ? Miklós Csűrös x

    4 6 8 9 1 2 5 7

    4 6 8 9 7 5 2 1

    A

    aux

    trié trié

    bitonique

    Algo FUSION(A[ ], g,m, d) // fusion «en place» pour A[g..m− 1] et A[m..d− 1]F1 for i← g, g + 1, . . . ,m− 1 do aux[i]← A[i]F2 for j ← m,m+ 1, . . . , d− 1 do aux[m+ d− 1− j]← A[j]F3 i← g ; j ← d− 1 ; k ← gF4 while k < d do // meilleure condition : i < mF5 if aux[i] ≤ aux[j] then A[k]← aux[i] ; i← i+ 1; k ← k + 1F6 else A[k]← aux[j] ; j ← j − 1; k ← k + 1

  • Mergesort — efficacité

    Tri ? IFT2015 H2020 ? UdeM ? Miklós Csűrös xi

    Nombre de comparaisons : ∼ n lgn au pire

    O(n)

    O(n)

    O(n)

    O(n)

    lg n niveaux de récurrences

    Meilleur temps : ∼ 12n lgn comparaisons (déjà trié : fusion avec ∼ n/2 compa-raisons)Moyen temps : ∼ n lgn (fusion avec n− 2 comparaisons en moyenne)

    Mémoire : tableau auxiliaire (n éléments)

  • Tri binaire

    Tri ? IFT2015 H2020 ? UdeM ? Miklós Csűrös xii

    Clés : 0 ou 1

    0 0 1 0 0 1 0 1 1 1

    i j

    échanger

    TRI01(A[0..n− 1]) // tri binaireB1 i← 0 ; j ← n− 1B2 loopB3 while i < j && A[i] = 0 do i← i+ 1B4 while i < j && A[j] = 1 do j ← j − 1B5 if i < j then échanger A[i]↔ A[j]B6 else return

    � tri en temps linéaire si seulement 2 clés

  • Tri rapide

    Tri ? IFT2015 H2020 ? UdeM ? Miklós Csűrös xiii

    1. choisir un élément ( pivot ), le placer dans sa case finale i, mettre tous les élé-ments inférieurs en A[0..i−1] et tous les éléments supérieurs en A[i+ 1..n−1]

    partition en O(n)

    ≤ p ≥ p

    p

    récursion récursion

    aucune combinaison nécessaire

    2. trier A[0..i− 1] et A[i+ 1..n− 1] (récurrence)

  • Partition

    Tri ? IFT2015 H2020 ? UdeM ? Miklós Csűrös xiv

    � suivre la technique du tri binaire avec deux indices i, j pour balayer4 2 8 1 3 9 1 7 2 6 8 5pivot = 5

    4 2 8 1 3 51 7 2 6 89

    4 2 2 1 3 51 7 8 6 89

    4 2 2 1 3 59 7 8 6 81

    4 2 2 1 3 95 7 8 6 81

    échanger

    échanger

    fin de balayer

    remettre le pivot

    i j

    ≤5

    4 2 2 1 3 1 97 8 6 8

    ≥5

    partition complète

  • Quicksort

    Tri ? IFT2015 H2020 ? UdeM ? Miklós Csűrös xv

    Algo QUICKSORT(A[0..n− 1], g, d) // tri de A[g..d− 1]Q1 if d− g ≤ 1 then return // cas de baseQ2 i← PARTITION(A, g, d)Q3 QUICKSORT(A, g, i)Q4 QUICKSORT(A, i+ 1, d)

    Algo PARTITION(A, g, d) // partition de A[g..d− 1]P1 chosir le pivot p← A[d− 1]P2 i← g − 1 ; j ← d− 1P3 loop // condition terminale à P6P4 do i← i+ 1 while A[i] < pP5 do j ← j − 1 while j > i et A[j] > pP6 if i ≥ j then sortir de la boucle (continuer à P8)P7 échanger A[i]↔ A[j]P8 échanger A[i]↔ A[d− 1] // mettre le pivot en placeP9 return i

  • Profondeur de la pile

    Tri ? IFT2015 H2020 ? UdeM ? Miklós Csűrös xvi

    Il y a 2 appels récursifs, mais 1 est terminal→ on peut le faire en une boucle, et faire juste 1 appel récursif

    Algo QUICKSORT_ITER(A[0..n− 1], g, d) // tri de A[g..d− 1]QI1 while d− g > 1 doQI2 i← PARTITION(A, g, d)QI3 QUICKSORT_ITER(A, g, i)QI4 g ← i+ 1 // boucler au lieu de l’appel récursif

    ? on peut également appeler QUICKSORT_ITER(A, i+1, d) et faire d← idans la boucle

    ? si on décide de faire l’appel récursif sur le plus petit des deux sous-tableaux(comparer i− g et d− i)⇒ profondeur ≤ 1 + blgnc sûrement

    Usage de mémoire : logarithmique (mais non pas constante) au pire avec cet astuce⇒ tri en place

  • Tri hybride

    Tri ? IFT2015 H2020 ? UdeM ? Miklós Csűrös xvii

    Génie algorithmique : tri par insertion est rapide quand n est petit

    ? Mergesort — approche hybride : utiliser insertion sort sur [g..d−1] quandle sous-tableau devient petit

    Algo MERGESORT(A[0..n− 1], g, d) // premier appel avec g = 0, d = nM1 if d− g < seuil thenM2 for i← g + 1, g + 2, . . . , d− 1 doM3 x← A[i] ; j ← d− 1M4 while j > g && A[j − 1] > x do A[j]← A[j − 1] ; j ← j − 1M5 A[j]← xM6 return

    // ... cas récursif comme usuel

    ? Quicksort — approche hybride : retourner de la récurrence immédiatementquand d− g < seuil+ lancer tri par insertion une fois à la toute fin sur le tableau entier

  • Tri rapide — performances

    Tri ? IFT2015 H2020 ? UdeM ? Miklós Csűrös xviii

    Nombre de comparaisons quand le pivot est le i-ème élément :

    C(n) = (n− 1) + C(i− 1) + C(n− i).

    La récurrence dépend de l’indice i du pivot.

    pivot i récurrence solution

    Meilleur cas n/2 2 · C((n− 1)/2)

    )+ (n− 1) ∼ n lgn

    Pire cas 0, n− 1 C(n− 1) + (n− 1) ∼ n2/2Moyen cas aléatoire EC(n) = 2EC(i) + (n− 1) ∼ 2n lnn? le meilleur cas donne une récurrence essentiellement identique à celle de

    mergesort? le pire cas arrive quand on a un tableau trié au début !? on examinera le cas moyen

  • Choix du pivot

    Tri ? IFT2015 H2020 ? UdeM ? Miklós Csűrös xix

    Deux choix performent très bien en pratique : médiane ou aléatoire.

    P1.1 k ← g + rnd(d− g) // pivot choisi au hasardP1.2 p← A[k]P1.3 A[k]← A[d− 1]P1.4 A[d− 1]← p

    ? ainsi, on a un algorithme randomisé : temps de calcul est une variablealéatoire

    ? distribution du temps de calcul est la même pour toute entrée de mêmetaille

    ? temps en espérance pour une entrée avec l’algo randomisé est le mêmeque l’espérance du temps de l’algo déterministe sur une entrée aléatoire(SHUFFLE)

  • Médiane de trois

    Tri ? IFT2015 H2020 ? UdeM ? Miklós Csűrös xx

    choisir 3 éléments, prendre leur médiane comme pivot

    P1.1 if d ≥ g + 2 thenP1.2 if A[g] > A[d− 2] then échanger A[g]↔ A[d− 2]P1.3 if A[d− 1] > A[d− 2] then échanger A[d− 1]↔ A[d− 2]P1.4 if A[g] > A[d− 1] then échanger A[g]↔ A[d− 1]P1.5 p← A[d− 1] // A[g] ≤ A[d− 1] ≤ A[d− 2]

    et on se sert des sentinelles qui sont maintenant en place à A[g], A[d− 2] :

    P2’ i← g; j ← d− 2P5’ do j ← j − 1 while A[j] > p

  • Tri rapide – analyse

    Tri ? IFT2015 H2020 ? UdeM ? Miklós Csűrös xxi

    Déf. Soit D(n) le nombre moyen de comparaisons avec un pivot aléatoire, où nest le nombre d’éléments dans un tableau A[· · · ].

    Lemme. On a D(0) = D(1) = 0, et

    D(n) = n− 1 +1

    n

    n−1∑i=0

    (D(i) +D(n− 1− i)

    )

    = n− 1 +2

    n

    n−1∑i=0

    D(i).

    Preuve. Supposons que le pivot est le i-ème plus grand élément de A. Le pivotest comparé à (n−1) autre éléments pour la partition. Les deux partitions sont detailles (i − 1) et (n − 1 − i). Or, i prend les valeurs 1,2, . . . , n avec la mêmeprobabilité. �

  • Performance moyenne (cont.)

    Tri ? IFT2015 H2020 ? UdeM ? Miklós Csűrös xxii

    Par le lemme précédent,

    nD(n)− (n− 1)D(n− 1) =(n(n− 1) + 2

    n−1∑i=0

    D(i))

    −(

    (n− 1)(n− 2) + 2n−2∑i=0

    D(i))

    = 2(n− 1) + 2D(n− 1).D’où on a

    D(n)

    n+ 1=D(n− 1)

    n+

    2n− 2n(n+ 1)

    =D(n− 1)

    n+

    4

    n+ 1−

    2

    n

    =

    (4

    2+

    4

    3+

    4

    4+ · · ·+

    4

    n+ 1

    )−(

    2

    1+

    2

    2+

    2

    3+ · · ·+

    2

    n

    )

    = 2

    (1

    1+

    1

    2+ · · ·+

    1

    n

    )︸ ︷︷ ︸

    nombre harmonique Hn

    −4 +4

    n+ 1

  • Performance moyenne (cont.2)

    Tri ? IFT2015 H2020 ? UdeM ? Miklós Csűrös xxiii

    Donc,

    D(n) = 2(n+ 1)Hn − 4n

    avec le n-ième nombre harmonique

    Hn = 1 +1

    2+

    1

    3+ · · ·

    1

    n= lnn+ γ +O(1/n) ∼ lnn

    où γ = limn→∞(Hn − lnn) = 0.5772 · · · est la constante d’Euler.

    Théorème. Le tri rapide avec pivotage aléatoire fait

    D(n) ∼ 2n lnn ≈ 1.39n lgn

    comparaisons en moyenne pour trier n éléments.

  • Le minimum de comparaisons dans un tri

    Tri ? IFT2015 H2020 ? UdeM ? Miklós Csűrös xxiv

    Méthode : imaginer l’arbre de décision de toutes exécutions possibles? nœuds internes : comparison entre 2 éléments? 2 enfants : branchement selon vrai ou faux dans la comparaison? nœuds externes : permutation finale (résultat du tri)

  • Arbre de décision : insertion sort, n = 4

    Tri ? IFT2015 H2020 ? UdeM ? Miklós Csűrös xxv

    A[0]

  • Minimum de comparaisons

    Tri ? IFT2015 H2020 ? UdeM ? Miklós Csűrös xxvi

    ? l’arbre a au moins n! nœuds externes (au moins 1 par permutation)? la hauteur de l’arbre est le nombre maximal de comparaisons sur n éléments? or, pour un arbre binaire avec m nœuds externes,

    hauteur ≥ lgm ≥ lg(n!) = lg 1 + lg 2 + · · ·+ lgn

    formule de Stirling :

    n! =√

    2πn(n

    e

    )n︸ ︷︷ ︸

    formule de Stirling

    × exp(θn

    12n

    )︸ ︷︷ ︸

    erreur de l’ordre 1/n

    {0 < θn < 1}

    alors

    lg(n!) = n lgn− n lg e+O(logn) ∼ n lgn

    Théorème. Tout algorithme déterministe de tri doit faire au moins ∼ n lgncomparaisons au pire pour trier n éléments.

  • Nos tris

    Tri ? IFT2015 H2020 ? UdeM ? Miklós Csűrös xxvii

    Méthode de tri interne comparaisons espace de travailtri par sélection (selection sort) ∼ n2/2 (toujours) O(1)tri par insertion (insertion sort) ∼ n2

    2(pire), ∼ n2

    4(moyen), ∼ n (meilleur) O(1)

    tri fusion (Mergesort) ∼ n lgn (toujours) Θ(n)tri par tas (Heapsort) ∼ 2n lgn (pire), Θ(n logn) (toujours) O(1)tri rapide (Quicksort) ∼ 2n lnn (moyen), n2/2 (pire) O(logn)borne inférieure ∼ n lgn (pire)

    Adaptation au tri de liste chaînée : tous sauf tri par tas

    Théorème. Le tri fusion est asymptotiquement optimal.

  • Sélection / médiane : QuickSelect

    Tri ? IFT2015 H2020 ? UdeM ? Miklós Csűrös xxviii

    chercher k-ème plus petit élément dans A[0..n− 1]

    Idée de clé : après avoir appellé i ← PARTITION(A,0, n), on trouve le k-èmeélément en A[0..i−1] si k < i ou en A[i+1..n−1] si k > i. En même temps,on réorganise le tableau pour que A[k] soit le k-ème plus petit élément.

    Algo SELECTION(A[0..n− 1], g, d, k)S1 if d− g ≤ 2 then // cas de base : 1 ou 2 élémentsS2 if d = g + 2 et A[d− 1] < A[g] then échanger A[g]↔ A[d− 1] // 2 élémentsS3 return A[k]S4 i← PARTITION(A, g, d)S5 if k = i then return A[k] // on l’a trouvéS6 if k < i then return SELECTION(A, g, i, k) // continuer à la gaucheS7 if k > i then return SELECTION(A, i+ 1, d, k) // continuer à la droite