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.
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.c | Ordinea 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âmp | Ce e |
|---|---|
id | unic pentru fiecare eveniment; nu urmează nicio ordine |
timestamp | momentul evenimentului, în secunde de la 1 ian. 1970 |
ip | sursa: 10.0.0.1 = 0x0A000001; se compară ca număr |
port | portul destinație (22, 80, 443, …) |
severitate | 0 = informativ, 9 = critic |
categorie | tipul 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.
Cum lucrezi
-
Descarcă și dezarhivează kitul
Din browser — sortlab.zip — sau din terminal:
curl -O /kit/sortlab.zip unzip sortlab.zip cd sortlabsolutie.caici scrii tu — cele patru funcții, marcate cu TODOsortlab.hrestul programului: descărcarea testelor, cronometrul, verificarea, trimiterea, main. Nu-l modifica.README.mdpe scurt, ce e pe pagina asta -
Scrie funcțiile în
solutie.cCompletezi 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 însortlab.h.#include "sortlab.h" void sorteaza_loguri(Eveniment *v, size_t n) { /* TODO: v[0..n-1] crescător după timestamp */ } -
Compilează
gcc -O2 solutie.c -o lab-O2contează: fără el timpii ies de câteva ori mai mari, iar programul te avertizează. -
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 evenimenteNimic nu pleacă la server. Dacă rezultatul e greșit, programul îți arată primul eveniment pus greșit și regula pe care o încalcă.
-
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 lyraNumele î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 ... -
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 opritDacă 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ță.
Clasament
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.
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.