Adloun

Probleme – Deux usages d'un tas de taille

Exercice · niveau 3 (difficile) · informatique (MP2I/MPI), chapitre 11 — Arbres de recherche, tas et files de priorité

Énoncé

Corrigé

1. Le tas des plus grands est un tas MINIMUM. C'est le point contre-intuitif du problème. On maintient un tas de taille au plus contenant les plus grands éléments vus jusqu'ici ; sa racine est donc le plus petit des retenus, c'est-à-dire précisément le candidat à l'éviction. Un tas maximum donnerait le plus grand des retenus, dont on n'a rien à faire.


(* Renvoie les k plus grands éléments du flot, dans un ordre quelconque.
   Précondition : k >= 1. Mémoire : O(k), indépendante de n. *)
(* fp_vide, ajoute, extrait, minimum, taille, contenu : la file de priorité
   de l'exercice sur la file de priorité de ce chapitre, un tas MINIMUM
   de couples (priorité, valeur). *)
let k_plus_grands flot k =
  let f = fp_vide () in
  Array.iter (fun x ->
    if taille f < k then ajoute f x x
    else if x > minimum f then begin    (* x bat le plus faible des retenus *)
      ignore (extrait f);
      ajoute f x x
    end) flot;
  contenu f

2. La preuve. Invariant : après avoir lu les premiers éléments, le tas contient exactement les plus grands d'entre eux.

Complexité : en temps, et surtout en mémoire, indépendante de . C'est ce dernier point qui compte : le flot peut être un fichier de dix téraoctets, ou n'exister que le temps de passer.

3. La fusion de listes triées. On place dans un tas minimum le premier élément de chaque liste, accompagné de son origine :


(* Fusionne k listes triées en une seule, triée. N = longueur totale.
   Complexité : O(N log k) en temps, O(k) en mémoire supplémentaire. *)
let fusion listes =
  let k = Array.length listes in
  let f = fp_vide () in
  Array.iteri (fun i l -> if l <> [||] then ajoute f l.(0) (i, 0)) listes;
  let sortie = ref [] in
  while not (est_vide f) do
    let (v, (i, j)) = extrait f in
    sortie := v :: !sortie;
    if j + 1 < Array.length listes.(i) then
      ajoute f listes.(i).(j+1) (i, j+1)     (* on RECHARGE la liste servie *)
  done;
  Array.of_list (List.rev !sortie)

Invariant : le tas contient, pour chaque liste non épuisée, exactement son plus petit élément non encore sorti. Le minimum du tas est donc le minimum global des éléments restants — c'est la correction du procédé. Terminaison : chaque tour sort un élément définitivement ; le variant est le nombre d'éléments non encore sortis. Complexité : extractions et au plus insertions dans un tas de taille , soit .

4. Les mesures. Mesuré, entiers tirés uniformément, :

tempsmémoire supplémentaire
tout trier, puis prendre les premiers s
tas minimum de taille s

Un facteur , et les dix mêmes valeurs — vérifié élément par élément. Le rapport théorique est ; le reste vient de ce que le tas ne fait presque rien : après quelques milliers d'éléments, la condition x &gt; minimum f est fausse presque toujours, et le tour se réduit à une comparaison.

Pour la fusion, mesuré : listes de éléments, , fusion en s, résultat vérifié identique au tri complet. Le gain théorique est : la fusion par tas fait trois fois moins de comparaisons qu'un tri de tout le paquet. Et elle ne demande que de mémoire, ce qui est la vraie raison de son emploi : c'est ainsi qu'on trie un fichier plus grand que la mémoire, en le coupant en morceaux triables, puis en fusionnant.

Les autres exercices de ce chapitre Le cours du chapitre

Un blocage sur cet exercice ? Le tuteur d'Adloun guide par questions, sans donner la réponse.