This is an old revision of the document!


Tema2 - Count-distinct problem

Responsabili:

Data publicării : 26 martie, ora: 21:00

Deadline: 16 aprilie, ora 23:55

Modificări şi actualizări

Obiective

În urma realizării acestei teme:

  • veţi învăţa să lucraţi cu Dictionare
  • vă veţi familiariza cu rezolvarea unei probleme reale prin structuri de date din ce in ce mai eficiente

Introducere

Problema estimarii cardinalitatii (a numararii elementelor distincte) este, in esenta, gasirea numarului de elemente unice dintr-o colectie de elemente care se pot repeta.

Pentru primele doua subpuncte, vom rezolva o problema si mai restrictiva: gasirea numarului de aparitii pentru fiecare element. Pentru restul, vom vedea ca acest lucru e mai greu realizabil cand vine vorba de volume mari de date si, de aceea, ne vom rezuma la gasirea numarului de elemente distincte.

I. Vector de frecventa - 25p

La intrare se dau numere intre 0 si 2000000. Gasiti numarul de aparitii ale fiecarui element, utilizand un vector de frecventa.

Un vector de frecventa este un vector care are pe pozitia i numarul de aparitii ale elementului i.

Se garanteaza ca numarul de aparitii ale oricarui element este mai mic decat 256.

II. Hashtable cu open addressing - 25p

La intrare se dau siruri de caractere. Gasiti numarul de aparitii ale fiecarui sir folosind un Hashtable cu politica de rezolvare a conflictelor de tip open addressing prin linear probing.

Aceasta politica presupune ca, in momentul in care bucketul unde trebuie realizata insertia este deja ocupat, se va cauta secvential o pozitie libera incepand cu bucketul urmator.

Evident, daca si aceasta pozitie este ocupata, vom cauta prima pozitie libera in continuare.

Daca se va ajunge la finalul listei de bucketuri, se va continua de la inceput.

Se garanteaza existenta a cel putin unui bucket liber in momentul fiecarei operatii de insertie.

Evident, daca in momentul unei operatii de selectie nu gasim cheia in bucketul in care ne-am astepta, vom continua cautarea secvential, aplicand un procedeu similar cu cel din momentul insertiei.

Se garanteaza ca lungimea maxima a oricarui este maxim 100 de caractere.

Se garanteaza ca numarul de aparitii ale oricarui element este mai mic decat 256.

III. Estimatori probabilistici - 30p

In cerintele anterioare am observat ca putem calcula cu exactitate numarul de elemente distincte (si numarul lor de aparitii) retinand, intr-un fel sau altul, fiecare element unic (ca pozitie intr-un vector, respectiv ca cheie intr-un hashtable). Din pacate, in aplicatiile din lumea reala, aceasta strategie nu este sustenabila.

Sa ne imaginam urmatoarea situatie: Youtube afiseaza pentru fiecare videoclip numarul de vizualizari. Pentru ca acest sistem sa nu fie abuzat (spre exemplu de un bot care vizioneaza acelasi videoclip incontinuu pentru a-i spori view count-ul), trebuie sa tina cont si de numarul de utilizatori unici care au accesat clipul.

Daca ar retine un id pentru fiecare utilizator, ar avea nevoie de o structura de date cu milioane de intrari, si asta doar pentru un singur clip. Tinand cont ca exista peste 31 de milioane de canale (iar multe dintre ele au peste 100 de clipuri), acest lucru iese din discutie.

Cum putem totusi sa ne indeplinim obiectivul?

Problema la abordarea anterioara este faptul ca foloseste o cantitate de memorie proportionala cu numarul de utilizatori distincti O(n). In cautarea unei solutii mai bune, va trebui sa obtinem o complexitate a spatiului mai mica (O(sqrt(n)), O(logn), O(1) etc.)

Solutia gasita (ce urmeaza a fi implementata in tema) aduce cu sine un sacrificiu din punct de vedere al preciziei: din moment ce nu stim exact ce utilizatori am avut, numarul total de utilizatori unici nu va mai fi unul precis, ci doar aproximativ.

In practica, acest lucru nu este un dezavantaj prea mare (intrucat rareori e relevanta diferenta dintre 1m vizualizari si 1.1m vizualizari).

In ilustrarea functionarii algoritmului HyperLogLog, vom incepe de la o serie de principii simple pe care le vom pune cap la cap, ajungand la descrierea algoritmului final.

1. Probabilistic counting

Sa presupunem ca generam un numar la intamplare.

Probabilitatea ca numarul sa inceapa cu un bit 0 este 1/2 (deoarece poate incepe fie cu 0, fie cu 1).

Probabilitatea ca numarul sa inceapa cu 2 biti 0 este 1/4 (deoarece poate incepe cu 00, 01, 10 sau 11).

Similar, probabilitatea ca numarul sa inceapa cu 3 biti 0 este 1/8. Astfel, pentru a intalni un numar care sa inceapa cu 3 biti 0, va trebui sa generam, in medie, 8 numere.

Privind aceasta observatie in sens invers, daca am generat numere aleatoare si secventa cea mai lunga de 0 de la inceputul oricarui numar a fost de lungime 3, atunci avem urmatoarele posibilitati: - am generat cel putin 8 numere - am avut noroc si a trebuit sa generam mai putin de 8 numere

Evident, pentru valori mici precum 2 sau 3 biti consecutivi, exista o sansa semnificativa sa generam numarul mai rapid (chiar din prima incercare), dar cu cat valorile devin mai mari, cu atat scade aceasta sansa.

In principiu, daca am primit valori aleatoare la intrare, si numarul maxim de biti 0 consecutivi initiali ai oricarui numar este x, putem spune ca am primit intre 2^x si 2^(x+1) valori.

In viata reala, valorile primite la intrare nu vor fi neaparat valori aleatoare. Mai mult, nu toate valorile de intrare vor fi numere intregi. Din acest motiv, vom trece aceste valori printr-o functie de hash. O functie de hash buna ar trebui sa ofere la iesire valori uniform distribuite.

2. LogLog

Daca vrem sa imbunatatim performanta algoritmului nostru va trebui sa: - Atenuam efectul negativ al generarii rapide unui numar cu multi biti de 0 initiali - Oferim estimari mai granulare decat puterile lui 2

O idee de rezolvare a primei probleme este sa impartim numerele in mai multe bucketuri. Cea mai usoara modalitate de a face acest lucru este sa impartim fiecare numar in 2 parti: prima parte va fi folosita pentru a determina bucketul, iar a doua va fi folosita ca pana acum.

Singura diferenta fata de metoda precedenta este ca acum vom face maximul de zerouri consecutive pentru fiecare bucket si nu pentru toate numerele.

Pentru a agrega aceste maxime vom folosi media geometrica.

3. HyperLogLog

Pentru a aduce algoritmul in forma finala, va trebui sa mai facem cateva ajustari matematice la procedeul descris anterior.

In primul rand, vom folosi o medie similara cu media armonica in loc de cea geometrica.

In al doilea rand, vom utiliza un factor de atenuare alfa, pentru a imbunatati eroarea de aproximare.

E reprezinta estimarea finala

m reprezinta numarul total de bucketuri

Z reprezinta media, calculata dupa urmatoarea formula:

alfa_m reprezinta factorul de atenuare, calculat in functie de m dupa urmatoarea formula:

Precizări

Rezolvati fiecare cerinta utilizand structura de date ceruta. Nerespectarea acestui lucru va aduce la anularea punctajului pentru cerinta respectiva.

Având în vedere ca a 3-a parte a temei presupune implementarea unei structuri de date probabilistice, checkerul ofera punctajul daca raspunsul vostru se incadreaza intr-o marja de eroare de 10% fata de raspunsul corect.

Checker

* checkerul va fi publicat in scurt timp

Temele vor fi trimise pe vmchecker. Atenție! Temele trebuie trimise în secțiunea Structuri de Date (CA).

Arhiva trebuie să conțină:

  • surse
  • fișierul Makefile din arhiva cu checkerul
  • fișier README care să conțină detalii despre implementarea temei

Punctaj

Atenite! O temă care nu compilează va primi 0 puncte.

  1. 80p teste
  2. Fiecare test este verificat cu valgrind. Dacă un test are memory leaks, nu va fi punctat.
  3. 20p README + comentarii/claritate cod (ATENȚIE! Fișierul README trebuie făcut explicit, cât să se înțeleagă ce ați făcut în sursă, dar fără comentarii inutile și detalii inutile).
  4. Se acordă 20% din punctajul obținut pe teste, ca bonus pentru coding style. De exemplu, pentru o temă care obține maxim pe teste, se pot obține 20p bonus dacă nu aveți erori de coding style. Pentru o temă ce trece 18 teste din 20, se pot obține 18p dacă nu aveți erori de coding style.
  5. O temă care obține 0p pe vmchecker este punctată cu 0.
  6. Temele au deadline hard. Prin urmare, o temă trimisă dupa deadline este punctată cu 0.

Nu copiați! Toate soluțiile vor fi verificate folosind o unealtă de detectare a plagiatului. În cazul detectării unui astfel de caz, atât plagiatorul cât și autorul original (nu contează cine e) vor primi punctaj 0 pe toate temele!

De aceea, vă sfătuim să nu vă lăsați rezolvări ale temelor pe calculatoare partajate (la laborator etc), pe mail/liste de discuții/grupuri etc.

sd-ca/teme/tema2-2020.1585249474.txt.gz · Last modified: 2020/03/26 21:04 by gabriel_danut.matei
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