Differences

This shows you the differences between two versions of the page.

Link to this comparison view

pp:23:teme:haskell-structuri-functionale [2023/04/11 15:02]
bot.pp created
pp:23:teme:haskell-structuri-functionale [2023/05/04 10:44] (current)
bot.pp
Line 2: Line 2:
  
   * Data publicării:​ 11.04.2023   * Data publicării:​ 11.04.2023
-  * Data ultimei modificări: ​11.04.2023 +  * Data ultimei modificări:​ 04.05.2023 
-  * Deadline hard: ziua laboratorului 10+  * Deadline hard: ziua laboratorului 10 (11 pentru seria CB)
   * [[https://​curs.upb.ro/​2022/​mod/​forum/​view.php?​id=173248|Forum temă]]   * [[https://​curs.upb.ro/​2022/​mod/​forum/​view.php?​id=173248|Forum temă]]
   * [[https://​vmchecker.cs.pub.ro/​ui/#​PP|vmchecker]]   * [[https://​vmchecker.cs.pub.ro/​ui/#​PP|vmchecker]]
Line 20: Line 20:
 Tema este împărțită în **3 etape**: Tema este împărțită în **3 etape**:
  
-  * una pe care o veți rezolva după laboratorul 7, cu deadline în ziua laboratorului 8 +  * una pe care o veți rezolva după laboratorul 7, cu deadline în ziua laboratorului 8 (9 pentru seria CB) 
-  * una pe care o veți rezolva după laboratorul 8, cu deadline în ziua laboratorului 9 +  * una pe care o veți rezolva după laboratorul 8, cu deadline în ziua laboratorului 9 (10 pentru seria CB) 
-  * una pe care o veți rezolva după laboratorul 9, cu deadline în ziua laboratorului 10.+  * una pe care o veți rezolva după laboratorul 9, cu deadline în ziua laboratorului 10 (11 pentru seria CB).
  
 **Deadline-ul depinde de semigrupa în care sunteți repartizați**. **Restanțierii care refac tema și nu refac laboratorul beneficiază de ultimul deadline**, și anume în zilele de 02.05, 09.05, respectiv 16.05. **Deadline-ul depinde de semigrupa în care sunteți repartizați**. **Restanțierii care refac tema și nu refac laboratorul beneficiază de ultimul deadline**, și anume în zilele de 02.05, 09.05, respectiv 16.05.
Line 41: Line 41:
   * **evaluare leneșă**, implicită în Haskell, pentru decuplarea conceptuală a transformărilor din cadrul unei secvențe.   * **evaluare leneșă**, implicită în Haskell, pentru decuplarea conceptuală a transformărilor din cadrul unei secvențe.
  
-Modulul de interes din schelet este ''​%%BinaryNumber%%'',​ care conține **reprezentarea** numerelor binare și **operațiile** pe care trebuie să le implementați:​+Modulul de interes din **schelet** este ''​%%BinaryNumber%%'',​ care conține **reprezentarea** numerelor binare și **operațiile** pe care trebuie să le implementați:​
  
   * tipul ''​%%BinaryNumber%%''​ definește reprezentarea numerelor binare   * tipul ''​%%BinaryNumber%%''​ definește reprezentarea numerelor binare
Line 51: Line 51:
   * funcția ''​%%multiply%%''​ realizează înmulțiri propriu-zise.   * funcția ''​%%multiply%%''​ realizează înmulțiri propriu-zise.
  
-Găsiți detaliile despre **funcționalitate** și **constrângeri ​de implementare**,​ precum și **exemple**,​ direct în schelet. Aveți de completat definițiile care încep cu ''​%%*** TODO ***%%''​.+Găsiți detaliile despre **funcționalitate** și despre ​**constrângerile ​de implementare**,​ precum și **exemple**,​ direct în schelet. Aveți de completat definițiile care încep cu ''​%%*** TODO ***%%''​.
  
 Pentru **rularea testelor**, încărcați în interpretor modulul ''​%%TestBinaryNumber%%''​ și evaluați ''​%%main%%''​. Pentru **rularea testelor**, încărcați în interpretor modulul ''​%%TestBinaryNumber%%''​ și evaluați ''​%%main%%''​.
  
 Este suficient ca arhiva pentru **vmchecker** să conțină modulul ''​%%BinaryNumber%%''​. Este suficient ca arhiva pentru **vmchecker** să conțină modulul ''​%%BinaryNumber%%''​.
 +
 +===== Etapa 2 =====
 +
 +În această etapă, veți începe implementarea unei **cozi de priorități**,​ utilizând **//​heap//​-uri binomiale**,​ prezentate mai jos. Coada va expune operații precum **adăugarea** unei chei, alături de prioritatea asociată, **combinarea** a două cozi, **determinarea** cheii cu prioritate **minimă**,​ precum și **eliminarea** acesteia. Toate operațiile vor avea complexitate **logaritmică** în dimensiunea cozii. Din moment ce ne concentrăm pe prioritățile **minime**, vorbim implicit despre un //min priority queue// sau //​min-heap//​.
 +
 +Construcțiile și mecanismele noi de limbaj pe care le veți exploata în rezolvare, pe lângă cele din etapa 1, sunt:
 +
 +  * **tipurile de date utilizator**.
 +
 +Înainte de prezentarea //​heap//​-urilor binomiale, introducem mai întâi conceptul de **arbore binomial**, care stă la baza primelor. Aceștia se construiesc recursiv, ca în fig. 1 de mai jos:
 +
 +  * cel mai simplu arbore binomial nevid este cel cu un **singur nod**
 +  * atașându-i unui arbore existent o **copie** a sa (din perspectivă structurală,​ nu neapărat a conținutului) ca **cel mai din stânga copil**, se obține tot un arbore binomial.
 +
 +{{ :​pp:​23:​teme:​haskell:​binomial-trees.png |{{:​binomial-trees.png|Arbori binomiali}}
 +
 +Fig. 1. Arbori binomiali. Adaptare după [[https://​www.oreilly.com/​library/​view/​c-data-structures/​9781788833738/​ae9c1988-22e7-4a85-8285-da0b8f4cada5.xhtml|oreilly.com]].
 +
 +Introducem de asemenea următoarele **atribute** ale unui arbore binomial:
 +
 +  * **rangul** reprezintă numărul de copii ai rădăcinii
 +  * **dimensiunea** este numărul total de noduri, egal cu 2 ridicat la puterea rangului.
 +
 +Astfel, în fig. 1:
 +
 +  * primul arbore are rangul 0 și dimensiunea 1
 +  * atașând la un arbore de rang ''​%%r-1%%''​ și dimensiune ''​%%2^(r-1)%%''​ o copie de-a sa, obținem un arbore de rang ''​%%r%%''​ și dimensiune ''​%%2^r%%'';​ nodurile bicolore surprind creșterea rangului unui nod de la ''​%%r-1%%''​ la ''​%%r%%''​ în urma atașării copiei.
 +
 +În fig. 1, se observă mai mulți arbori binomiali, cu ranguri între 0 și 4, și că **înălțimea** unui arbore este egală cu **rangul** său. De asemenea, transpare o proprietate care derivă din modalitatea de construcție:​ copiii unui nod de rang ''​%%r%%''​ au rangurile ''​%%r-1%%'',​ ''​%%r-2%%'',​ ..., ''​%%1%%'',​ ''​%%0%%'',​ exact în această ordine **descrescătoare**. De exemplu, copiii rădăcinii de rang 4 au rangurile 3, 2, 1, 0. **Numele** de arbore binomial vine de la faptul că, pe nivelul ''​%%i%%''​ din arborele de rang ''​%%r%%'',​ numărul de noduri este dat de **coeficientul binomial** „combinări de ''​%%r%%''​ luate câte ''​%%i%%''​”.
 +
 +Continuăm cu prezentarea **//​heap//​-urilor binomiale**. Acestea nu sunt decât **liste de arbori binomiali**,​ cu constrângerea suplimentară că arborii trebuie să respecte și **proprietatea de //heap//**, i.e. rădăcina are **prioritate mai mică** decât copiii și analog pentru subarbori. Având în vedere faptul că toți arborii binomiali au dimensiuni puteri ale lui 2, principala întrebare este cum **distribuim** elementele //​heap//​-ului în cadrul acestor arbori, presupunând că numărul total de elemente nu este putere a lui 2. Aici intervine **reprezentarea binară a numerelor** din etapa 1. Dacă **dimensiunea** //​heap//​-ului este ''​%%n%%'',​ având reprezentarea binară ''​%%[b_0,​ b_1, ..., b_m]%%'',​ unde ''​%%m = [lg n]%%''​ (parte întreagă),​ atunci, pentru fiecare bit ''​%%0%%'',​ vom avea un arbore **vid**, și pentru fiecare bit ''​%%b_r%%''​ egal cu ''​%%1%%''​ vom avea un arbore de **rang ''​%%r%%''​**,​ și implicit dimensiune ''​%%2^r%%''​.
 +
 +**Exemplificăm** ideea de mai sus cu un //heap// cu 13 elemente, **reprezentarea binară a dimensiunii** fiind ''​%%[1,​ 0, 1, 1]%%''​. Aceasta înseamnă că avem un arbore de rang 0 (dimensiune 1), niciun arbore de rang 1, un arbore de rang 2 (dimensiune 4) și un arbore de rang 3 (dimensiune 8), ca în fig. 2. Observați de asemenea respectarea **proprietății de //heap//** de către fiecare dintre cei trei arbori.
 +
 +{{ :​pp:​23:​teme:​haskell:​binomial-heap.png |{{:​binomial-heap.png|Heap binomial}}
 +
 +Fig. 2. //Heap// binomial. Adaptare după [[https://​www.bartleby.com/​subject/​engineering/​computer-science/​concepts/​binomial-heap|bartleby.com]].
 +
 +Vom vedea în continuare cum unele operațiile asupra //​heap//​-urilor binomiale le oglindesc pe cele asupra **numerelor binare**. De exemplu, operația de **inserare** a unui element corespunde **incrementării** unui număr binar. La fel cum, în cazul **numerelor binare**, operația de **incrementare** presupune poziționarea unui **bit 1** în dreptul primului bit al reprezentării numărului de incrementat și modificarea succesivă a biților curent și următori, în caz de transport, operația de **inserare** a unui element într-un **//heap// binomial** presupune poziționarea unui **arbore de rang 0 (dimensiune 1)** în dreptul primului arbore existent în //heap// și modificarea succesivă a arborelui curent și eventual a următorilor. Dacă este destul de clar ce înseamnă a aduna doi biți, rămâne de clarificat ce înseamnă a **„aduna”** doi arbori binomiali:
 +
 +  * adunarea oricărui arbore cu un arbore **vid** produce primul arbore
 +  * adunarea a doi arbori **nevizi** cu **același rang** conduce la **atașarea** unui arbore ca cel mai din stânga copil al celuilalt, adică tocmai principiul recursiv de construcție a arborilor binomiali; arborele rezultant, cu rang mai mare cu 1 decât al arborilor constituenți,​ devine **transport**.
 +
 +De remarcat că **niciodată** nu vom fi puși în situația de a aduna doi arbori nevizi cu ranguri diferite. Rămâne de ales arborele care joacă rolul de **copil** al celuilalt; din moment ce trebuie conservată **proprietatea de //heap//**, **copil** va deveni arborele cu **prioritatea mai mare**.
 +
 +**Exemplificăm inserarea** unui nou element în //heap//-ul din fig. 2. Ne vom concentra pe pozițiile de rang 0 și 1, întrucât cele de rang 2 și 3 nu vor fi afectate. Rezultatul se observă în fig. 3:
 +
 +  * dacă prioritatea noului element este **mai mică** (7), arborele **existent** (9) devine copilul acestuia (fig. 3, stânga)
 +  * dacă prioritatea noului element este **mai mare** (11), **noul** arbore devine copil al celui existent (9) (fig. 3, dreapta).
 +
 +În ambele cazuri, rezultă un **transport** în forma unui arbore de rang 1, care îl **înlocuiește** pe cel vid din //heap//-ul original; de asemenea, dispare arborele de rang 0. Dacă în //heap//-ul original ar fi existat deja un arbore de rang 1, procesul de atașare și propagare a transportului ar fi **continuat recursiv**, către poziția de rang 2 ș.a.m.d.
 +
 +{{ :​pp:​23:​teme:​haskell:​binomial-heap-insert.png |{{:​binomial-heap-insert.png|Inserare}}
 +
 +Fig. 3. Inserarea în //heap//-ul binomial din fig. 2.
 +
 +Cum reprezentarea binară a dimensiunii //​heap//​-ului conține un număr logaritmic de biți, deci un număr logaritmic de arbori (vizi sau nevizi), iar atașarea unui arbore la altul se face în timp constant, **inserarea** are complexitate **logaritmică**.
 +
 +Având în vedere că toți arborii respectă **proprietatea de //heap//**, i.e. rădăcina este elementul cu prioritate minimă din fiecare arbore, operația de **determinare** a elementului cu **prioritate minimă** din întregul //heap// presupune identificarea **rădăcinii** cu prioritate minimă. În exemplul din fig. 2, prioritatea minimă este 5, aferentă rădăcinii arborelui de rang 2. Dacă există **mai multe** rădăcini cu prioritate minimă, se alege cea care apare **prima** în lista de arbori. Urmând un raționament similar celui din paragraful anterior, rezultă că și această operație are complexitate **logaritmică**.
 +
 +Ultima operație implementată în această etapă este cea de **combinare** (engl. //merge//) a două //​heap//​-uri. Aceasta o oglindește pe cea de **adunare** a două numere binare din etapa 1. La fel cum, în cazul **numerelor binare**, se adună mai întâi biții de pe poziția 0, apoi 1 etc., în cazul **//​heap//​-urilor binomiale**,​ se începe cu adunarea arborilor de pe poziția de rang 0, apoi rang 1 ș.a.m.d., ținând cont bineînțeles de eventualul **transport**. Din motive similare celor de mai sus, și această operație are complexitate **logaritmică**.
 +
 +Modulul de interes din **schelet** este ''​%%BinomialHeap%%'',​ care conține **reprezentarea** **arborilor binomiali** și a **//​heap//​-urilor binomiale**,​ precum și **operațiile** descrise mai sus:
 +
 +  * tipul ''​%%BinomialTree p k%%'',​ deja definit, desemnează **arbori binomiali** având noduri cu **priorități** de tip ''​%%p%%''​ și **chei** de tip ''​%%k%%'';​ în mod evident, **prioritățile** trebuie să fie **ordonabile**
 +  * tipul ''​%%BinomialHeap p k%%'',​ deja definit, denotă **//​heap//​-uri binomiale** având elemente cu priorități de tip ''​%%p%%''​ și chei de tip ''​%%k%%''​
 +  * funcția ''​%%attach%%''​ permite **atașarea** unui arbore ca cel mai din stânga copil al altuia, în vederea conservării **proprietății de //heap//**
 +  * **//​heap//​-ul vid**, ''​%%emptyHeap%%''​
 +  * funcțiile ''​%%insertTree%%''​ și ''​%%insert%%''​ permit **inserarea** unui nou element într-un //heap//
 +  * funcția ''​%%findMin%%''​ **determină** elementul cu **prioritate minimă**
 +  * funcțiile ''​%%mergeTrees%%''​ și ''​%%merge%%''​ permit **combinarea** a două //​heap//​-uri.
 +
 +Găsiți detaliile despre **funcționalitate** și despre **constrângerile de implementare**,​ precum și **exemple**,​ direct în schelet. Aveți de completat definițiile care încep cu ''​%%*** TODO ***%%''​.
 +
 +Pentru **rularea testelor**, încărcați în interpretor modulul ''​%%TestBinomialHeap%%''​ și evaluați ''​%%main%%''​.
 +
 +Este suficient ca arhiva pentru **vmchecker** să conțină modulul ''​%%BinomialHeap%%''​.
 +
 +===== Etapa 3 =====
 +
 +În această etapă, veți continua să implementați anumite operații asupra //​heap//​-urilor binomiale, în continuarea celor din etapa 2.
 +
 +Construcțiile și mecanismele noi de limbaj pe care le veți exploata în rezolvare, pe lângă cele din etapa 2, sunt:
 +
 +  * **polimorfismul ad-hoc**
 +  * **clasele**.
 +
 +Ultima operație fundamentală este de **eliminare** a cheii de **prioritate minimă** din //heap//. Am lăsat-o la final, deoarece utilizează operația de **combinare** (''​%%mergeTrees%%''​) implementată în etapa 2. Eliminarea presupune **înlăturarea primului arbore** cu rădăcină de prioritate minimă din lista de arbori ai //​heap//​-ului (prin înlocuirea lui cu ''​%%EmptyTree%%''​) și apoi **combinarea** (''​%%mergeTrees%%''​) noii liste de arbori cu lista de subarbori (orfani) ai rădăcinii tocmai înlăturate. Având în vedere că lista de arbori ai //​heap//​-ului este ordonată **crescător** în raport cu rangul, iar lista de subarbori orfani este ordonată **descrescător** (conform structurii arborilor binomiali), este necesară **inversarea** ultimeia înainte de combinarea celor două liste! Având la bază operația de combinare, rezultă că și cea de eliminare are complexitate **logaritmică**.
 +
 +Scheletul etapei 3 se găsește tot în modulul ''​%%BinomialHeap%%'',​ în continuarea operațiilor din etapa 2, începând cu linia 243:
 +
 +  * funcția ''​%%isolate%%''​ este ajutătoare,​ pregătind terenul pentru următoarea operație
 +  * funcția ''​%%removeMin%%''​ înlătură prima cheie de prioritate minimă din //heap//
 +  * instanțele clasei ''​%%Show%%''​ pentru tipurile ''​%%BinomialTree p k%%''​ și ''​%%BinomialHeap p k%%''​ oferă reprezentări mai lizibile, sub formă de șir de caractere ale celor două categorii de structuri; observați că a fost necesară eliminarea ''​%%deriving Show%%''​ din definiția tipurilor, pentru evitarea conflictului de instanțe
 +  * instanțele clasei ''​%%Functor%%''​ pentru constructorii de tip ''​%%BinomialTree p%%''​ și ''​%%BinomialHeap p%%''​ generalizează funcționala ''​%%map%%''​ pe aceste categorii de structuri
 +  * pentru **bonus**, instanța clasei ''​%%Foldable%%''​ pentru constructorul de tip ''​%%BinomialTree p%%''​ generealizează funcționala ''​%%foldr%%''​ pe aceste structuri.
 +
 +Găsiți detaliile despre **funcționalitate** și despre **constrângerile de implementare**,​ precum și **exemple**,​ direct în schelet. Aveți de completat definițiile care încep cu ''​%%*** TODO ***%%''​.
 +
 +Pentru **rularea testelor**, încărcați în interpretor modulul ''​%%TestBinomialHeap%%''​ și evaluați ''​%%main%%''​.
 +
 +Este suficient ca arhiva pentru **vmchecker** să conțină modulul ''​%%BinomialHeap%%''​. Veți avea nevoie de implementarea funcției ''​%%mergeTrees%%''​ din etapa 2.
  
 ===== Precizări ===== ===== Precizări =====
Line 65: Line 167:
  
   * [[https://​ocw.cs.pub.ro/​courses/​_media/​pp/​23/​teme/​haskell/​etapa1.zip|Schelet etapa 1]]   * [[https://​ocw.cs.pub.ro/​courses/​_media/​pp/​23/​teme/​haskell/​etapa1.zip|Schelet etapa 1]]
 +  * [[https://​ocw.cs.pub.ro/​courses/​_media/​pp/​23/​teme/​haskell/​etapa2.zip|Schelet etapa 2]]
 +  * [[https://​ocw.cs.pub.ro/​courses/​_media/​pp/​23/​teme/​haskell/​etapa3.zip|Schelet etapa 3]]
  
 ===== Changelog ===== ===== Changelog =====
 +
 +  * 22.04 (ora 16:10) Publicare etapa 2, doar enunț și schelet; urmează și checker-ul.
 +  * 24.04 (ora 22:45) Publicare checker etapa 2.
 +  * 26.04 (ora 10:55) Actualizare checker etapa 2. Și ''​%%BinomialHeap%%''​ instanțiază acum ''​%%Eq%%''​ pentru facilitarea noilor teste.
 +  * 28.04 (ora 14:50) Actualizare checker etapa 2 pt verificări mai flexibile. Dacă ați încărcat deja etapa 2 pe vmchecker, NU este necesară reîncărcarea.
 +  * 30.04 (ora 10:30) Publicare etapa 3, doar enunț și schelet; urmează și checker-ul.
 +  * 30.04 (ora 22:35) Actualizare checker etapa 2, în urma unor probleme semnalate de voi. Se verifică acum corespondența corectă dintre rangul arborilor și poziția acestora în lista heap-ului.
 +  * 04.05 (ora 10:45) Publicare checker etapa 3.
  
  
pp/23/teme/haskell-structuri-functionale.1681214553.txt.gz · Last modified: 2023/04/11 15:02 by bot.pp
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0