Differences

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

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
aa:lab:12 [2024/01/19 12:21]
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țaxiome pentru următorii operatori pe tipul ''​List''​:+Problemele NP-Complete despre care am discutat până acum la curs șla laborator au două trăsături principale:
  
-  * ''​append''​ (concatenarea unei liste la o alta; ''​append(Cons(8,​ Cons(12, Empty)), Cons(3, Cons(6, Empty))) = Cons(8, Cons(12, Cons(3, Cons(6, Empty))))''​) +  * nu au soluții eficiente cunoscute 
-  * ''​reverse''​ (inversează elementele dintr-o listă)+  * sunt relevante practic
  
-2Definiț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 × → Map''​ (un Map cu o nouă asociere cheie:​element) +    ​căutăm toate nodurile $ v \in Vcu $ grad(vK(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 × → 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ărinu neapărat ​K$ inițialmuchiiatunci 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 \times K$ muchii); putem întoarce direct răspunsul negativAltfelinvocă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(lEmpty) = l] +
-  * $math[\forall l_1, l_2, l_3 \in \texttt{List}. append(l_1, append(l_2, l_3)) = append(append(l_1l_2), l_3))] +
-  * $math[\forall l_1, l_2 \in \texttt{List}. length(append(l_1l_2)) = length(append(l_2,​ l_1))] +
-  * $math[\forall l_1, l_2 \in \texttt{List}reverse(append(l_1l_2)) = append(reverse(l_2),​ reverse(l_1))].+
  
  
 <​note>​ <​note>​
-Soluțiile acestui laborator se găsesc [[https://​ocw.cs.pub.ro/​ppcarte/​doku.php?​id=aa:​lab:​sol:​12|aici]]+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>​
 +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>​
 +
 +
 +==== 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.