Differences
This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision | |||
|
aa:lab:12 [2025/01/23 14:09] dmihai |
aa:lab:12 [2026/01/19 12:58] (current) dmihai |
||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | ====== TDA-uri și inducție structurală ====== | + | ====== Abordări practice pentru probleme NP-Complete ====== |
| - | | + | |
| - | 1. Definiți axiome pentru următorii operatori pe tipul ''List'': | + | Problemele NP-Complete despre care am discutat până acum la curs și la laborator au două trăsături principale: |
| - | * ''reverse'' (inversează elementele dintr-o listă) | + | * nu au soluții eficiente cunoscute |
| - | * ''filterEven'' (elimină elementele impare dintr-o listă) | + | * sunt relevante practic |
| - | 2. Definiți axiome pentru următorii operatori pe tipul ''BTree'': | + | Trebuie deci să avem o strategie de a le aborda, nu putem să le evităm. |
| + | În acest laborator vom studia o strategie puternică numită //"kernelizare"//. | ||
| + | Kernelizarea are la bază ideea că backtrackingul este inevitabil; dar în loc să aplicăm backtracking pe întreaga instanță a problemei, putem încerca a priori să //extragem miezul dificil al problemei//. | ||
| + | Această extragere constă în aplicarea unui algoritm determinist polinomial (deci //eficient//) care să reducă drastic dimensiunea problemei. | ||
| + | Este similar cu o reducere polinomială; dar în loc să transformăm inputul unei probleme ''A'' într-un input pentru o altă problemă ''B'', îl vom transofrma într-un input mai mic pentru aceeași problemă ''A''. | ||
| - | * ''mirror : BTree → BTree'' (arborele oglindit pe verticală, i.e. pentru orice nod, copilul stâng devine copilul drept și vice-versa) | + | ==== Vertex Cover ==== |
| - | * ''flatten : BTree → List'' (lista cu toate elementele din arbore; observați că există mai multe ordini posibile) | + | |
| - | 3. Definiți axiome pentru următorii operatori pe tipul ''Map'': | + | Vom implementa un algoritm simplu de kernelizare pentru problema Vertex Cover pe graful $ G = (V, E)$ și dimensiunea căutată $ K$, care constă în următorii pași: |
| - | * ''update : Map × K × V → Map'' (un Map cu o nouă asociere cheie:element) | + | * căutăm toate nodurile $ v \in V$ cu $ grad(v) > K$ (gradul unui nod este numărul de muchii adiacente nodului). Dacă există un cover de dimensiune $ K$ aceste noduri trebuie să facă parte din el. Putem să le reținem separat, apoi să le ștergem din graf și să decrementăm $ K$ pentru fiecare din ele. Căutăm în continuare un cover mai mic într-un graf mai mic. **Atenție!** După aplicarea acestei reguli, este posibil să o putem aplica din nou pe noul graf și noul $ K$. |
| - | * ''delete : Map × K → Map'' (șterge cheia și valoarea asociată) | + | |
| - | 4. Demonstrați următoarele propoziții, folosind inducție structurală: | + | * Căutăm toate nodurile $ v \in V$ cu $ grad(v) = 0$; acestea pot fi eliminate |
| - | * $math[\forall t \in \texttt{BTree}. size(t) = size(mirror(t))] | + | * Dacă graful obținut are mai mult de $ K^2$ (aici e vorba de $ K$-ul obținut prin transformări, nu neapărat $ K$ inițial) muchii, atunci graful nu poate conține un cover de dimensiune $ K$ (pentru că acum fiecare nod are grad maxim $ K$, deci $ K$ noduri pot acoperi maxim $ K \times K$ muchii); putem întoarce direct răspunsul negativ. Altfel, invocăm algoritmul de backtracking naiv pe noua instanță. |
| - | * $math[\forall t \in \texttt{BTree}. size(t) = length(flatten(t))] | + | |
| - | * $math[\forall l \in \texttt{List}. append(l, Empty) = l] | + | |
| - | * $math[\forall l_1, l_2, l_3 \in \texttt{List}. append(l_1, append(l_2, l_3)) = append(append(l_1, l_2), l_3))] | + | |
| - | * $math[\forall l_1, l_2 \in \texttt{List}. length(append(l_1, l_2)) = length(append(l_2, l_1))] | + | |
| - | * $math[\forall l_1, l_2 \in \texttt{List}. reverse(append(l_1, l_2)) = append(reverse(l_2), reverse(l_1))]. | + | |
| + | <note> | ||
| + | Pentru completitudine, trebuie să considerăm și situația $ K = 0$; în acest caz răspunsul e pozitiv dacă și numai dacă nu există muchii în graf. | ||
| + | </note> | ||
| <note> | <note> | ||
| - | Soluțiile acestui laborator se găsesc [[https://ocw.cs.pub.ro/ppcarte/doku.php?id=aa:lab:sol:12|aici]] | + | Ca să păstrăm analogia cu reduceri polinomiale (în care transformarea trebuie neapărat să producă o instanță a problemei și nu direct un răspuns) putem la ultimul pas, în caz că sunt mai mult de $ K^2$ muchii, să aplicăm backtracking naiv pe orice instanță despre care știm că răspunsul e negativ: e.g. întoarcem un graf cu două noduri, muchie între ele și $ K = 0$. |
| </note> | </note> | ||
| + | |||
| + | ==== Exerciții ==== | ||
| + | |||
| + | 1. Implementați o soluție prin backtracking naiv pentru problema $ \texttt{VERTEX COVER}$; trebuie ca punctul de intrare să fie o funcție ''solve_vertex_cover(<graph>, <k>)''. | ||
| + | |||
| + | 2. Implementați pașii de preprocesare discutați mai sus. | ||
| + | La final, aplicați funcția ''solve_vetex_cover'' pe graful și numărul obținut. | ||
| + | |||
| + | 3. Implementați reducerea $ \texttt{CLIQUE} \le_P \texttt{VERTEX COVER}$; trebuie ca punctul de intrare să fie o funcție ''solve_clique(<graph>, <k>)''. | ||
| + | După reducerea grafului, aplicați preprocesarea prin kernelizare descrisă la punctul 2. | ||
| + | |||
| + | |||
| + | Găsiți {{:aa:lab:graph-example.zip|aici}} un exemplu de graf în următorul format: | ||
| + | |||
| + | * pe prima linie, o literă ''U'' sau ''D'' indică dacă graful este neorientat (**U**ndirected) sau orientat (**D**irected). | ||
| + | * tot pe prima linie, seaparate cu un spațiu sunt numărul de noduri $ N$ și numărul de muchii $ M$ | ||
| + | * pe următoarele $ M$ linii sunt triplete: ID nod sursă, ID nod destinație, cost muchie (pentru grafuri neorientate, muchiile apar o singură dată, listate la nodul cu ID mai mic) | ||
| + | * nodurile sunt indexate de la 0. | ||
| + | |||
| + | Este un graf complet cu 29 de noduri; are acoperiri doar de 28 și de 29 de noduri. | ||