teaching.
Acasă / Tehnici de sortare

Tehnici de sortare

Sistemul de gestiune a evenimentelor sortează aceleași evenimente de securitate în patru feluri. Tu scrii câte o funcție C pentru fiecare. Algoritmul îl alegi singur — după regulă, după cum arată datele și după testele care pică. Fiecare rulare intră live în clasament.

Enunț

Cerința

Completezi patru funcții în fișierul solutie.c. Fiecare primește un tablou v de n evenimente și trebuie să-l lase sortat pe loc, după regula ei:

#Completezi în solutie.cOrdinea cerută

Ce sortezi: structura Eveniment

Toate cele patru funcții sortează același tip de date, definit în sortlab.h — 16 octeți pe eveniment.

typedef struct {
    uint32_t id;          /* identificator unic          */
    uint32_t timestamp;   /* secunde Unix (UTC)          */
    uint32_t ip;          /* IPv4 sursă, ca număr        */
    uint16_t port;        /* portul destinație           */
    uint8_t  severitate;  /* 0 (informativ) … 9 (critic) */
    uint8_t  categorie;   /* tipul evenimentului, 0..15  */
} Eveniment;

void sorteaza_loguri(Eveniment *v, size_t n);
CâmpCe e
idunic pentru fiecare eveniment; nu urmează nicio ordine
timestampmomentul evenimentului, în secunde de la 1 ian. 1970
ipsursa: 10.0.0.1 = 0x0A000001; se compară ca număr
portportul destinație (22, 80, 443, …)
severitate0 = informativ, 9 = critic
categorietipul evenimentului; nu intră în nicio regulă, dar rămâne lipit de eveniment

Sortezi evenimente întregi: când muți unul, se mută cu toate câmpurile lui. Algoritmii îi scrii tu — fără qsort sau alte sortări din biblioteci; poți folosi malloc/free și orice funcții ajutătoare. În exemplele de mai jos, timestamp-urile sunt scurtate, ca să se citească ușor.

Pas cu pas

Cum lucrezi

  1. Descarcă și dezarhivează kitul

    Din browser — sortlab.zip — sau din terminal:

    curl -O /kit/sortlab.zip
    unzip sortlab.zip
    cd sortlab
    solutie.caici scrii tu — cele patru funcții, marcate cu TODO
    sortlab.hrestul programului: descărcarea testelor, cronometrul, verificarea, trimiterea, main. Nu-l modifica.
    README.mdpe scurt, ce e pe pagina asta
  2. Scrie funcțiile în solutie.c

    Completezi corpul celor patru funcții. Funcțiile ajutătoare (comparatoare, interschimbări…) le scrii deasupra lor, în același fișier, și le poți folosi la mai multe cerințe. Nu scrie main — e deja în sortlab.h.

    #include "sortlab.h"
    
    void sorteaza_loguri(Eveniment *v, size_t n) {
        /* TODO: v[0..n-1] crescător după timestamp */
    }
  3. Compilează

    gcc -O2 solutie.c -o lab

    -O2 contează: fără el timpii ies de câteva ori mai mari, iar programul te avertizează.

  4. Verifică local, fără server

    ./lab --local --n 20              # toate cerințele, 20 de evenimente aleatoare
    ./lab --local --cerinta 2         # doar cerința 2, cu 100.000 de evenimente

    Nimic nu pleacă la server. Dacă rezultatul e greșit, programul îți arată primul eveniment pus greșit și regula pe care o încalcă.

  5. Rulează pe server și intră în clasament

    ./lab "Numele Tău"                # toate cele 4 cerințe × 5 teste
    ./lab "Numele Tău" --cerinta 2    # doar cerința 2
    ./lab "Numele Tău" --cerinta 2 --test lyra

    Numele îl alegi tu. Programul descarcă fiecare test, îți apelează funcția, cronometrează doar apelul ei și trimite rezultatul. Arată cam așa:

    Cerința 2 — Alertele, pe severitate: sorteaza_alerte().
    Regula: severitate descrescător, stabil.
      test cygnus
            descărcat 1.0 MB comprimat în 0.4 s (nu intră în timp)
            sortez 100000 evenimente cu sorteaza_alerte()...  (blocat? Ctrl+C)
            [ OK ]  4.812 ms — Corect, în 4.812 ms.  (locul 3 în clasament)
      test draco
            ...
  6. Citește rezultatul și reia

    [ OK ]sortat corect, în limita de timp
    [GREȘIT]nu respectă regula — programul îți spune primul eveniment greșit
    [TIMP DEPĂȘIT]corect, dar peste limita de timp
    [EXPIRAT]rezultatul n-a mai ajuns: programul a crăpat (Segmentation fault) sau l-ai oprit

    Dacă o sortare se blochează, oprește-o cu Ctrl+C — serverul marchează singur testul. Apoi modifici, recompilezi și rulezi din nou: în clasament contează cea mai bună rulare pe fiecare cerință.

Live

Clasament

se conectează… trimiteri închise arhivă
Încă nimeni în clasament. Primul care rulează ia locul 1.
test trecut greșit peste limită crash sau blocat rulează acum ★ 9.8 mscel mai rapid pe cerință
Reguli

Cum se evaluează

Testele

Fiecare cerință trece prin cinci teste cu nume de cod: . Fiecare e generat proaspăt pentru tine (nimeni nu primește aceleași evenimente de două ori) și fiecare ascunde altceva — unele sunt gândite anume să doboare implementările naive. Ce are special fiecare, afli rulând. Sau uitându-te la date.

Evenimentele sortate nu pleacă înapoi: programul calculează un hash al rezultatului, iar serverul îl compară cu hash-ul sortării corecte, calculat dinainte.

Clasamentul

Pe fiecare cerință contează cea mai bună rulare: întâi câte teste a trecut, apoi timpul total pe ele. Teste trecute în rulări diferite nu se adună — ținta e un singur algoritm care rezistă la toate cele cinci.

Scorul tău adună cele patru cerințe: întâi testele trecute (cel mult 20), apoi timpul total. Un test care a crăpat apare cu 💥 la limită + 20 s de la pornirea sortării.

Timpul se măsoară pe calculatorul tău, deci e comparabil doar aproximativ între colegi.

Pentru curioși

API

Kitul face exact cererile de mai jos, prin curl. Le poți repeta de mână, dintr-un alt limbaj sau dintr-un script. Erorile vin ca {"eroare": "…"} cu status 4xx.

POST/api/rulari

Pornește o rulare pe o cerință (loguri, alerte, prioritati, conexiuni). Id-ul primit e secret — cine îl știe poate trimite rezultate în numele rulării. regula e ordinea cerută, compact: câmp + sens (+ crescător, - descrescător), în ordinea priorității.

curl -X POST /api/rulari \
     -H 'Content-Type: application/json' \
     -d '{"utilizator": "Ion P.", "cerinta": "prioritati"}'

→ 201 {"rulare": "9f2c41…", "teste": ["cygnus", "draco", "lyra", "orion", "vega"],
       "regula": "severitate-,timestamp+", "stabil": false, "n": 100000, "limita_ms": 400, …}

GET/api/rulari/{rulare}/{test}

Evenimentele unui test, binar: o linie de antet text, apoi cele n evenimente pe coloane — câmp cu câmp, în ordinea din struct —, iar fiecare coloană pe planuri de octeți: întâi octetul 0 (cel mai puțin semnificativ) al tuturor evenimentelor, apoi octetul 1 și așa mai departe. Așa se comprimă mult mai bine: cu Accept-Encoding: gzip (curl --compressed), răspunsul vine cam la 60%.

curl --compressed -o date.bin /api/rulari/9f2c41…/lyra

SORTLAB3 prioritati lyra 100000 400\n      ← magic, cerință, test, n, limita_ms
id:         n octeți 0 · n octeți 1 · n octeți 2 · n octeți 3
timestamp:  n octeți 0 · … · n octeți 3
ip:         n octeți 0 · … · n octeți 3
port:       n octeți 0 · n octeți 1
severitate: n octeți
categorie:  n octeți                        ← în total 16 · n octeți

O a doua descărcare a aceluiași test (de exemplu după o eroare de rețea) înlocuiește testul, până la trimiterea rezultatului.

POST/api/rulari/{rulare}/{test}/start

Datele au ajuns, începe sortarea: din acest moment curge limita de timp, deci o rețea lentă nu se socotește. Fără anunț, serverul pornește ceasul de la descărcare, cu o toleranță mai mare.

POST/api/rulari/{rulare}/{test}

Rezultatul: hash-ul tabloului sortat (16 cifre hex) și timpul sortării, în milisecunde.

curl -X POST /api/rulari/9f2c41…/lyra \
     -H 'Content-Type: application/json' \
     -d '{"hash": "00c3f1a29b7e4d10", "timp_ms": 84.512}'

→ 200 {"status": "acceptat", "mesaj": "Corect, în 84.512 ms.", "loc": 3, "rezolvate": 1, "total": 5}

status e unul dintre acceptat, gresit, timp_depasit, timp_invalid, expirat.

Hash-ul

Două părți, combinate prin XOR. Prima: un hash polinomial modulo 264 peste cheile cerinței, eveniment cu eveniment, în ordinea din rezultat — deci evenimentele egale pe toate cheile pot sta în orice ordine între ele. La cerința stabilă (2) se hash-uiesc id-urile, deci ordinea e fixată complet. A doua: amprenta, o sumă de mix64 pe evenimentele întregi, care nu depinde de ordine, dar prinde orice eveniment pierdut, dublat sau alterat. Implementarea completă e în sortlab.h.

/* cerința 3: cheile sunt severitate, apoi timestamp */
uint64_t h = 0;
for (size_t i = 0; i < n; ++i) {
    h = h * 1000003ULL + v[i].severitate + 1;
    h = h * 1000003ULL + v[i].timestamp + 1;
}
h ^= amprenta(v, n);                /* Σ mix64(eveniment), modulo 2^64 */
printf("%016llx", (unsigned long long)h);

GET/api/clasament?sesiune={id}

Clasamentul și ultimele rezultate. Fără sesiune, cea curentă. Tot de aici: GET /api/cerinte (regulile) și GET /api/sesiuni.