This shows you the differences between two versions of the page.
pa:laboratoare:laborator-04 [2021/03/15 08:49] miruna_elena.banu [Gardurile lui Gigel] |
pa:laboratoare:laborator-04 [2024/04/16 17:38] (current) radu.nichita |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Laborator 4: Programare Dinamică (continuare) ====== | + | ====== Laborator 04: Programare Dinamică (2/2) ====== |
- | Responsabili: | + | |
- | * [[neatudarius@gmail.com|Darius-Florentin Neațu (2017-2021)]] | + | |
- | * [[radunichita99@gmail.com | Radu Nichita (2021)]] | + | |
- | * [[cristianolaru99@gmail.com | Cristian Olaru (2021)]] | + | |
- | * [[mirunaelena.banu@gmail.com | Miruna-Elena Banu (2021)]] | + | |
- | * [[maraioana9967@gmail.com | Mara-Ioana Nicolae (2021)]] | + | |
- | * [[stefanpopa2209@gmail.com | Ștefan Popa (2018-2020)]] | + | |
- | + | ||
- | Autori: | + | |
- | * [[neatudarius@gmail.com|Darius-Fforentin Neațu (2018)]] | + | |
- | * [[visanr95@gmail.com|Radu Vișan (2018)]] | + | |
- | * [[cristb@gmail.com|Cristian Banu (2018)]] | + | |
- | * [[razvan.ch95@gmail.com|Răzvan Chițu (2018)]] | + | |
===== Obiective laborator ===== | ===== Obiective laborator ===== | ||
Line 102: | Line 88: | ||
* explicație: $X =(BC)$ generează $3 * 4 * 5 = 60$ înmulțiri, $(AX)$ generează $2 * 3 * 5 = 30$ de înmulțiri | * explicație: $X =(BC)$ generează $3 * 4 * 5 = 60$ înmulțiri, $(AX)$ generează $2 * 3 * 5 = 30$ de înmulțiri | ||
- | Rezultatul optim se obține pentru cea de a treia parantezare: $(AB)C$. | + | Rezultatul optim se obține pentru prima parantezare: $(AB)C$. |
</spoiler> | </spoiler> | ||
Line 165: | Line 151: | ||
== Numire recurență === | == Numire recurență === | ||
$dp[i][j]$ = **numărul minim de înmulțiri scalare** cu care se poate obține produsul $M_i * M_{i+1} * ... *{M_j}$ | $dp[i][j]$ = **numărul minim de înmulțiri scalare** cu care se poate obține produsul $M_i * M_{i+1} * ... *{M_j}$ | ||
+ | |||
+ | Răspunsul la problemă este **dp[1][n]** . | ||
== Găsire recurență == | == Găsire recurență == | ||
Line 186: | Line 174: | ||
<spoiler Implementare C++> | <spoiler Implementare C++> | ||
<code cpp> | <code cpp> | ||
- | // kInf este valoarea maximă - "infinitul" nostru | + | // INF este valoarea maximă - "infinitul" nostru |
- | const unsigned long long kInf = std::numeric_limits<unsigned long long>::max(); | + | const auto INF = std::numeric_limits<unsigned long long>::max(); |
// T = O(n ^ 3) | // T = O(n ^ 3) | ||
Line 194: | Line 182: | ||
// dp[i][j] = numărul MINIM înmulțiri scalare cu codare, poate fi calculat produsul | // dp[i][j] = numărul MINIM înmulțiri scalare cu codare, poate fi calculat produsul | ||
// matriceal M_i * M_i+1 * ... * M_j | // matriceal M_i * M_i+1 * ... * M_j | ||
- | vector<vector<unsigned long long>> dp(n + 1, vector<unsigned long long> (n + 1, kInf)); | + | vector<vector<unsigned long long>> dp(n + 1, vector<unsigned long long> (n + 1, INF)); |
// Cazul de bază 1: nu am ce înmulți | // Cazul de bază 1: nu am ce înmulți | ||
Line 242: | Line 230: | ||
<note> | <note> | ||
- | **ATENȚIE!** La PA, în general, vom folosi convenția $ expresie \ \% \ kMod $, care va fi detaliată în capitolul următor din acest laborator. | + | **ATENȚIE!** La PA, în general, vom folosi convenția $ expresie \ \% \ MOD $, care va fi detaliată în capitolul următor din acest laborator. |
</note> | </note> | ||
Line 418: | Line 406: | ||
* se poate obține $S = O(1)$ folosind exponențiere pe matrice! | * se poate obține $S = O(1)$ folosind exponențiere pe matrice! | ||
- | ===== Tehnici folosite in DP ===== | + | ===== Tehnici folosite în DP ===== |
- | De multe ori este nevoie sa folosim cateva tehnici pentru a obtine performanta maxima cu recurenta gasita. | + | De multe ori, este nevoie să folosim câteva tehnici pentru a obține performanța maximă cu recurența găsită. |
- | In laboratorul 3 se mentiona tehnica de memoizare (in prima parte a laboratorului). In acesta ne vom rezuma la cum putem folosi cunostintele de lucru matricial pentru a favoriza implementarea unor anumite tipuri de recurente. | + | În prima parte a laboratorului 3 se menționa tehnica de memoizare. În acesta, ne vom rezuma la cum putem folosi cunoștințele de lucru matriceal pentru a favoriza implementarea unor anumite tipuri de recurențe. |
- | ==== Exponentiere pe matrice pentru recurente liniare ==== | + | ==== Exponențiere pe matrice pentru recurențe liniare ==== |
- | === Recurente liniare=== | + | === Recurențe liniare === |
- | O recurenta liniara in contextul laboratorului de DP este de forma: | + | O recurență liniară, în contextul laboratorului de DP, este de forma: |
* $dp[i] = \sum_{k=1}^{KMAX} c_k * dp[i - k]$ | * $dp[i] = \sum_{k=1}^{KMAX} c_k * dp[i - k]$ | ||
- | * pentru **KMAX o constanta** | + | * pentru **KMAX o constantă** |
- | * de obicei, KMAX este foarte mica comparativ cu dimensinea n a problemei | + | * de obicei, KMAX este foarte mică comparativ cu dimensiunea n a problemei |
* $c_k$ constante reale (unele pot fi nule) | * $c_k$ constante reale (unele pot fi nule) | ||
- | O astfel de recurenta ar insemna ca pentru a calcula ** costul problemei i **, imbinam costurile problemelor $i - 1, i-2, ...., i-k$, fiecare contribuind cu un anumit coeficient $c_{1}, c_{2}, ..., c_{k}$. | + | O astfel de recurență ar însemna că, pentru a calcula **costul problemei i**, îmbinăm costurile problemelor $i - 1, i - 2, ...., i - k$, fiecare contribuind cu un anumit coeficient $c_{1}, c_{2}, ..., c_{k}$. |
- | <spoiler Complexitate recurente liniara> | + | <spoiler Complexitate recurențe liniară> |
- | Presupunand ca nu mai exista alte specificatii ale problemei si ca avand cele KMAX cazuri de baza (primele KMAX valori ar trebui stiute/deduse prin alte reguli), atunci un algoritm poate implementa recurenta de mai sus folosind 2 cicluri de tip for (for i = 1 : n, for k = 1 : KMAX ...). | + | Presupunând că nu mai există alte specificații ale problemei și că, având cele KMAX cazuri de bază, (primele KMAX valori ar trebui știute/deduse prin alte reguli), atunci un algoritm poate implementa recurența de mai sus folosind 2 cicluri de tip: for (for i = 1 : n, for k = 1 : KMAX ...). |
- | * **complexitatea temporala** : $ T =O(n * KMAX) = O(n) $ | + | * **complexitatea temporală** : $T = O(n * KMAX) = O(n)$ |
- | * reamintim ca acea valoarea KMAX este o constanta foarte mica in compartie cu n (ex. KMAX < 100) | + | * reamintim că acea valoare KMAX este o constantă foarte mică în comparație cu n (ex. KMAX < 100) |
- | * **complexitatea spatiala** : $ S = O(n) $ | + | * **complexitatea spațială** : $S = O(n)$ |
- | * am presupus ca avem nevoie sa retinem doar tabloul dp | + | * am presupus că avem nevoie să reținem doar tabloul dp |
</spoiler> | </spoiler> | ||
- | === Exponentiere pe matrice === | + | === Exponențiere pe matrice === |
- | Facem urmatoarele notatii: | + | Facem următoarele notații: |
* $S_i$ = starea la pasul i | * $S_i$ = starea la pasul i | ||
* $S_i = (dp[i - k + 1], dp[i - k + 2], ..., dp[i - 1], dp[i])$ | * $S_i = (dp[i - k + 1], dp[i - k + 2], ..., dp[i - 1], dp[i])$ | ||
- | * $S_k$ = starea initiala (in care cunoaste cele k cazuri de baza) | + | * $S_k$ = starea inițială (în care cunoaște cele k cazuri de bază) |
* $S_k = (dp[1], dp[2], ..., dp[k-1], dp[k])$ | * $S_k = (dp[1], dp[2], ..., dp[k-1], dp[k])$ | ||
- | * $C$ = matrice ce coeficienti constanti | + | * $C$ = matrice de coeficienți constanți |
* are dimensiune $KMAX * KMAX$ | * are dimensiune $KMAX * KMAX$ | ||
- | * putem pune constante in clar | + | * putem pune constante în clar |
- | * putem pune constantele $c_k$ care tin de problema curenta | + | * putem pune constantele $c_k$ care țin de problema curentă |
Line 457: | Line 445: | ||
== Algoritm naiv == | == Algoritm naiv == | ||
Putem formula problema astfel: | Putem formula problema astfel: | ||
- | * $S_k$ = este starea initiala | + | * $S_k$ = este starea inițială |
- | * pentru a obtine starea urmatoare, aplicam algoritmul urmator | + | * pentru a obține starea următoare, aplicăm algoritmul următor |
* $S_i = S_{i-1}C$ | * $S_i = S_{i-1}C$ | ||
| | ||
== Determinare C == | == Determinare C == | ||
- | Pentru a determina elementele matricei C, trebuie sa ne uitam la inmultirea matriceala de mai sus si sa alegem elementele lui C astfel incat prin inmultirealui $S_{i-1}$ cu $C$ sa obtinem elementele din $S_i$. | + | Pentru a determina elementele matricei C, trebuie să ne uităm la înmulțirea matriceală de mai sus și să alegem elementele lui C astfel încât prin înmulțirea lui $S_{i-1}$ cu $C$ să obținem elementele din $S_i$. |
\begin{gather} | \begin{gather} | ||
Line 481: | Line 469: | ||
- | <spoiler Explicatie determinare C> | + | <spoiler Explicație determinare C> |
- | * ultima coloana contine toti coeficientii $c_k$ intrucat $dp[i] = \sum_{k=1}^{KMAX} c_k * dp[i - k]$ | + | * ultima coloană conține toți coeficienții $c_k$ întrucât $dp[i] = \sum_{k=1}^{KMAX} c_k * dp[i - k]$ |
- | * celelalte coloane contin doar cate o valoare nenula | + | * celelalte coloane conțin doar câte o valoare nenulă |
- | * pe coloana j vom avea valoarea 1 pe linia $j+1$ ($j = 1 : KMAX - 1$) | + | * pe coloana j vom avea valoarea 1 pe linia $j + 1$ ($j = 1 : KMAX - 1$) |
- | * cum obtinem, de exemplu, $dp[i - 1]$? | + | * cum obținem, de exemplu, $dp[i - 1]$? |
- | * pai avem $dp[i-1]$ chiar si in starea $S_{i-1}$, deci trebuie sa il copiam in starea $S_i$ | + | * păi, avem $dp[i-1]$ chiar și în starea $S_{i-1}$, deci trebuie să îl copiem în starea $S_i$ |
- | * copierea se realizeaza prin inmultirea cu 1 | + | * copierea se realizează prin inmulțirea cu 1 |
- | * daca $dp[i-1]$ era pe ultima pozitiei (pozitia k) in starea $S_{i-1}$, in noua stare $S_i$ este pe penultima pozitie (pozitia $k-1$) | + | * dacă $dp[i-1]$ era pe ultima poziție (poziția k) în starea $S_{i-1}$, în noua stare $S_i$ este pe penultima poziție (poziția $k-1$) |
- | * deci s-a deplasat la stanga cu o pozitie! | + | * deci s-a deplasat la stânga cu o poziție! |
- | * in noua stare, noua pozitie este deplasata cu o unitate la stanga fata de starea precedenta | + | * în noua stare, noua poziție este deplasată cu o unitate la stânga față de starea precedentă |
- | * de aceea pe coloana $j$, vrem sa avem elementul 1 pe linia $j + 1$ ($j = 1 : KMAX - 1$) | + | * de aceea, pe coloana $j$, vrem să avem elementul 1 pe linia $j + 1$ ($j = 1 : KMAX - 1$) |
- | * cand inmultim $S_{i-1}$ cu coloana $C_j$ **dorim sa** | + | * când înmulțim $S_{i-1}$ cu coloana $C_j$ **dorim să** |
- | * ce copiam? | + | * ce copiem? |
- | * valoarea $dp[i - KMAX + j]$ din $S_{i-1}$ in $S_{i}$ | + | * valoarea $dp[i - KMAX + j]$ din $S_{i-1}$ în $S_{i}$ |
- | * adica sa copiam a j-a valoare de pe linie | + | * adică să copiem a j-a valoare de pe linie |
- | * unde copiam? | + | * unde copiem? |
- | * de pe pozitia $j + 1$ pe pozitia $j$ | + | * de pe poziția $j + 1$ pe poziția $j$ |
</spoiler> | </spoiler> | ||
- | == Exponentiere logaritmica pe matrice == | + | == Exponențiere logaritmică pe matrice == |
- | Algoritmul naiv de mai sus are dezavantajul ca are tot o complexitate temporala $O(n)$. | + | Algoritmul naiv de mai sus are dezavantajul că are tot o complexitate temporală $O(n)$. |
- | Sa executam cativa pasi de inductie pentru a vedea cum este determinat $S_i$. | + | Să executăm câțiva pași de inducție pentru a vedea cum este determinat $S_i$. |
$$S_i = S_{i-1}C$$ | $$S_i = S_{i-1}C$$ | ||
$$S_i = S_{i-2}C^2$$ | $$S_i = S_{i-2}C^2$$ | ||
Line 511: | Line 499: | ||
- | In laboratorul 2 (Divide et Impera) am invatat ca putem calcula $ x ^ n $ in timp logaritmic. Deoarece si inmultirea matricilor este asociativa, putem calcula $C ^ n$ in timp logaritmic. | + | În laboratorul 2 (Divide et Impera) am învățat că putem calcula $x ^ n$ în timp logaritmic. Deoarece și înmulțirea matricilor este asociativă, putem calcula $C ^ n$ in timp logaritmic. |
- | Obtinem astfel o solutie cu urmatoarele complexitati: | + | Obținem astfel o soluție cu următoarele complexități: |
- | * ** complexitate temporala **: $T = O(KMAX^3 * log(n))$ | + | * ** complexitate temporală **: $T = O(KMAX^3 * log(n))$ |
- | * explicatie | + | * explicație |
- | * facem doar $O(log n)$ pasi, dar un pas implica inmultire de matrice | + | * facem doar $O(log n)$ pași, dar un pas implică înmulțire de matrice |
- | * o inmultire de matrice patratica de dimensiune KMAX are $KMAX^3$ operatii | + | * o înmulțire de matrice patrătică de dimensiune KMAX are $KMAX^3$ operații |
- | * aceasta metoda este eficienta cand $KMAX << n$ (KMAX este mult mai mic decat n) | + | * această metodă este eficientă când $KMAX << n$ (KMAX este mult mai mic decât n) |
- | * ** complexitatea spatiala **: $S = O(KMAX^3)$ | + | * ** complexitatea spațială **: $S = O(KMAX^2)$ |
- | * explicatie | + | **Observație!** În ultimele calcule nu am șters constanta KMAX, întrucât apare la puterea a 2-a! $KMAX = 1000$ implică $KMAX^2 = 10^6$, valoare care nu mai poate fi ignorată în practică ($KMAX^2$ poate fi comparabil cu n). |
- | * este nevoie sa stocam cateva matrici | + | |
- | Observatie! In ultimele calcule nu am sters contanta KMAX, intrucat apare la puterea a 3-a! $KMAX = 100$ implica $KMAX^3 = 10^6$, valoare care nu mai poate fi ignorata in practica ($KMAX^3$ poate fi comparabil cu n). | + | |
=== Gardurile lui Gigel (optimizare) === | === Gardurile lui Gigel (optimizare) === | ||
- | Dupa cum am vazut mai sus, in problema cu garduri data de Gigel solutia este o recurenta liniara: | + | După cum am văzut mai sus, în problema cu garduri dată de Gigel, soluția este o recurență liniară: |
* $dp[1] = dp[2] = dp[3] = 1$; $d[4]=2$; | * $dp[1] = dp[2] = dp[3] = 1$; $d[4]=2$; | ||
* $dp[i] = dp[i - 1] + dp[i - 4]$, pentru $i > 4$ | * $dp[i] = dp[i - 1] + dp[i - 4]$, pentru $i > 4$ | ||
- | == Exponentiere rapida == | + | == Exponențiere rapidă == |
* $ k = 4 $ | * $ k = 4 $ | ||
- | * $S_4 = (dp[1], dp[2], dp[3], dp[4]) = (1, 1, 1, 4)$ | + | * $S_4 = (dp[1], dp[2], dp[3], dp[4]) = (1, 1, 1, 2)$ |
* $S_i = (dp[i-3], dp[i-2], dp[i-1], dp[i])$ | * $S_i = (dp[i-3], dp[i-2], dp[i-1], dp[i])$ | ||
- | * Raspunsul se afla efectuand operatia $S_n = S_4 * C^{n - 4}$, unde C are urmatorul continut: | + | * Răspunsul se află efectuând operația $S_n = S_4 * C^{n - 4}$, unde C are următorul conținut: |
\begin{gather} | \begin{gather} | ||
C = \begin{bmatrix} | C = \begin{bmatrix} | ||
Line 543: | Line 529: | ||
\end{gather} | \end{gather} | ||
- | <spoiler Implementare in C++> | + | <spoiler Implementare în C++> |
- | Mai jos se afla o implementare simplista in C++ care cuprinde toate etapele pe care trebuie sa le realizati in cod, dupa ce stiti cum arata recurenta sub forma matriceala. | + | Mai jos se află o implementare simplistă în C++ care cuprinde toate etapele pe care trebuie să le realizați în cod, după ce știți cum arată recurența sub forma matriceală. |
<code cpp> | <code cpp> | ||
Line 558: | Line 544: | ||
for (int i = 0; i < KMAX; ++i) { | for (int i = 0; i < KMAX; ++i) { | ||
for (int j = 0; j < KMAX; ++j) { | for (int j = 0; j < KMAX; ++j) { | ||
- | unsigned long long sum = 0; // presupun ca suma incape pe 64 de biti | + | unsigned long long sum = 0; // presupun că suma încape pe 64 de biți |
for (int k = 0; k < KMAX; ++k) { | for (int k = 0; k < KMAX; ++k) { | ||
Line 585: | Line 571: | ||
if (p % 2 == 0) { | if (p % 2 == 0) { | ||
multiply_matrix(C, C, C); // C = C*C | multiply_matrix(C, C, C); // C = C*C | ||
- | p /= 2; // ramane de calculat C^(p/2) | + | p /= 2; // rămâne de calculat C^(p/2) |
} else { | } else { | ||
// reduc la cazul anterior: | // reduc la cazul anterior: | ||
multiply_matrix(tmp, C, tmp); // tmp = tmp*C | multiply_matrix(tmp, C, tmp); // tmp = tmp*C | ||
- | --p; // ramane de calculat C^(p-1) | + | --p; // rămâne de calculat C^(p-1) |
} | } | ||
} | } | ||
- | // avem o parte din rezultat in C si o parte in tmp | + | // avem o parte din rezultat în C și o parte în tmp |
multiply_matrix(C, tmp, R); // rezultat = tmp * C | multiply_matrix(C, tmp, R); // rezultat = tmp * C | ||
} | } | ||
int garduri_rapide(int n) { | int garduri_rapide(int n) { | ||
- | // cazurile de baza | + | // cazurile de bază |
if (n <= 3) return 1; | if (n <= 3) return 1; | ||
if (n == 4) return 2; | if (n == 4) return 2; | ||
Line 607: | Line 593: | ||
{0, 1, 0, 0}, | {0, 1, 0, 0}, | ||
{0, 0, 1, 1}}; | {0, 0, 1, 1}}; | ||
- | // vreau sa aplic formula S_n = S_4 * C^(n-4) | + | // vreau să aplic formula S_n = S_4 * C^(n-4) |
// C = C^(n-4) | // C = C^(n-4) | ||
power_matrix(C, n - 4, C); | power_matrix(C, n - 4, C); | ||
- | // sol = S_4 * C = dp[n] (se afla pe ultima pozitie din S_n, | + | // sol = S_4 * C = dp[n] (se află pe ultima poziție din S_n, |
- | // deci voi folosi ultima coloana din C) | + | // deci voi folosi ultima coloană din C) |
int sol = 1 * C[0][3] + 1 * C[1][3] + 1 * C[2][3] + 2 * C[3][3]; | int sol = 1 * C[0][3] + 1 * C[1][3] + 1 * C[2][3] + 2 * C[3][3]; | ||
return sol % MOD; | return sol % MOD; | ||
Line 621: | Line 607: | ||
<note> | <note> | ||
- | Remarcati faptul ca in functia de inmultire se foloseste o matrice temporara $tmp$. Motivul este ca vrem sa apelam functia $multiply(C, C, C)$, unde C joaca atat rol de intrare cat si de iesire. Daca am pune rezultatele direct in C, atunci am strica inputul inainte sa obtinem rezultatul. | + | Remarcați faptul că în funcția de înmulțire se folosește o matrice temporară $tmp$. Motivul este că vrem să apelăm funcția $multiply(C, C, C)$, unde C joacă atât rol de intrare cât și de ieșire. Dacă am pune rezultatele direct in C, atunci am strica inputul înainte să obținem rezultatul. |
- | Putem spune ca acea functie este **matrix_multiply_safe**, in sensul ca pentru orice A,B,C care respecta dimensiunile impuse, functia va calcula corect produsul. | + | Putem spune că acea funcție este **matrix_multiply_safe**, în sensul că pentru orice A,B,C care respectă dimensiunile impuse, funcția va calcula corect produsul. |
</note> | </note> | ||
</spoiler> | </spoiler> | ||
- | <spoiler Comparatie solutii (studiu de caz pentru curiosi)> | + | <spoiler Comparație solutii (studiu de caz pentru curioși)> |
- | In arhiva ** demo-lab04.zip ** gasiti o sursa completa in care se realizeaza: | + | Pe git găsiți o sursă completă în care se realizează: |
- | * o verificare a faptului ca cele 2 implementari (** gardurile_lui_Gigel** si **garduri_rapide**) produc aceleasi rezultate | + | * o verificare a faptului că cele 2 implementări (** gardurile_lui_Gigel** și **garduri_rapide**) produc aceleași rezultate |
- | * un benchmark in care cele 2 implementari sunt comparate | + | * un benchmark în care cele 2 implementări sunt comparate |
- | * pe sistem uzual (laptop) s-au obtinut urmatoarele rezulate: | + | * pe un sistem uzual (laptop) s-au obținut următoarele rezulate: |
<code bash> | <code bash> | ||
- | test case: varianta simpla | + | test case: varianta simplă |
n = 100000000 sol = 119; time = 0.984545 s | n = 100000000 sol = 119; time = 0.984545 s | ||
- | test case: varianta rapida | + | test case: varianta rapidă |
n = 100000000 sol = 119; time = 0.000021 s | n = 100000000 sol = 119; time = 0.000021 s | ||
- | test case: varianta simpla | + | test case: varianta simplă |
n = 1000000000 sol = 812; time = 9.662377 s | n = 1000000000 sol = 812; time = 9.662377 s | ||
- | test case: varianta rapida | + | test case: varianta rapidă |
n = 1000000000 sol = 812; time = 0.000022 s | n = 1000000000 sol = 812; time = 0.000022 s | ||
</code> | </code> | ||
- | * se observa clar diferenta intre cele 2 solutii (am confirmat ceea ce spunea si teoria: $O(n) $ vs $O(log(n))$); aceasta tehnica imbunatateste drastic o solutie gasita relativ usor. | + | * se observă clar diferența între cele 2 soluții (am confirmat ceea ce spunea și teoria: $O(n) $ vs $O(log(n))$); această tehnică îmbunătățește drastic o soluție gasită relativ usor. |
</spoiler> | </spoiler> | ||
- | ===== Exercitii ===== | + | ===== Exerciții ===== |
<note> | <note> | ||
Scheletul de laborator se găsește pe pagina [[https://github.com/acs-pa/pa-lab/tree/main/skel/lab04|pa-lab::skel/lab04]]. | Scheletul de laborator se găsește pe pagina [[https://github.com/acs-pa/pa-lab/tree/main/skel/lab04|pa-lab::skel/lab04]]. | ||
</note> | </note> | ||
=== DP or math? === | === DP or math? === | ||
- | Fie un sir de **numere naturale strict pozitive**. Cate **subsiruri** (submultimi nevide) au suma numerelor **para**? | + | Fie un șir de **numere naturale strict pozitive**. Câte **subșiruri** (submulțimi nevide) au suma numerelor **pară**? |
<note> | <note> | ||
- | **subsir** (**subsequence** in engleza) pentru un vector ** v **inseamna un alt vector $u = [v[i_1], v[i_2],..., v[i_k]]]$ unde $i_1 < i_2 < ... < i_k$. | + | **subșir** (**subsequence** în engleză) pentru un vector **v** înseamnă un alt vector $u = [v[i_1], v[i_2],..., v[i_k]]]$ unde $i_1 < i_2 < ... < i_k$. |
</note> | </note> | ||
Task-uri: | Task-uri: | ||
- | * Se cere o **solutie folosind DP**. | + | * Se cere o **soluție folosind DP**. |
- | * Inspectand recurenta gasita la punctul precedent, incercati sa o inlocuiti cu o **formula matematica**. | + | * Inspectând recurența gasită la punctul precedent, încercați să o înlocuiți cu o **formulă matematică**. |
- | * Care este **complexitatea** pentru fiecare solutie (timp + spatiu)? Care este mai buna? De ce? :D | + | * Care este **complexitatea** pentru fiecare soluție (timp + spațiu)? Care este mai bună? De ce? :D |
- | Deoarece rezultatul poate fi prea mare, se cere **restul impartirii** lui la $1000000007$ ($10^9 + 7$). | + | Deoarece rezultatul poate fi prea mare, se cere **restul împărțirii** lui la $1000000007$ ($10^9 + 7$). |
- | Pentru punctaj maxim pentru aceasta problema, este necesar sa rezolvati toate subpunctele (ex. nu folositi direct formula, gasiti mai intai recurenta DP). Trebuie sa implementati **cel putin** solutia cu DP. | + | Pentru punctaj maxim pentru această problemă, este necesar să rezolvați toate subpunctele (ex. nu folosiți direct formula, găsiți mai întâi recurența DP). Trebuie să implementați **cel puțin** soluția cu DP. |
Line 676: | Line 662: | ||
|v|2|6|4| | |v|2|6|4| | ||
- | Raspuns: $7$ | + | Răspuns: $7$ |
- | Explicatie: Toate subsirurile posibile sunt | + | Explicație: Toate subșirurile posibile sunt |
* $[2]$ | * $[2]$ | ||
* $[2, 6]$ | * $[2, 6]$ | ||
Line 686: | Line 672: | ||
* $[6, 4]$ | * $[6, 4]$ | ||
* $[4]$ | * $[4]$ | ||
- | Toate subsirurile de mai sus au suma para. | + | Toate subșirurile de mai sus au suma pară. |
</spoiler> | </spoiler> | ||
Line 695: | Line 681: | ||
|v|2|1|3| | |v|2|1|3| | ||
- | Raspuns: $3$ | + | Răspuns: $3$ |
- | Explicatie: Toate subsirurile posibile sunt | + | Explicație: Toate subșirurile posibile sunt |
* $[2]$ | * $[2]$ | ||
* $[2, 1]$ | * $[2, 1]$ | ||
Line 706: | Line 692: | ||
* $[3]$ | * $[3]$ | ||
- | Subsirurile cu suma para sunt: $[2]$, $[2, 1, 3]$, $[1, 3]$. | + | Subșirurile cu sumă pară sunt: $[2]$, $[2, 1, 3]$, $[1, 3]$. |
</spoiler> | </spoiler> | ||
Line 716: | Line 702: | ||
|v|3|2|1| | |v|3|2|1| | ||
- | Raspuns: $3$ | + | Răspuns: $3$ |
- | Explicatie: Toate subsirurile posibile sunt | + | Explicație: Toate subșirurile posibile sunt |
* $[3]$ | * $[3]$ | ||
* $[3, 2]$ | * $[3, 2]$ | ||
Line 727: | Line 713: | ||
* $[1]$ | * $[1]$ | ||
- | Subsirurile cu suma para sunt: $[3, 2, 1]$, $[3, 1]$, $[2]$. | + | Subșirurile cu sumă pară sunt: $[3, 2, 1]$, $[3, 1]$, $[2]$. |
</spoiler> | </spoiler> | ||
Line 734: | Line 720: | ||
Morala: există probleme pentru care găsim o soluție cu DP, dar pentru care pot exista și alte soluții mai bune (am ignorat citirea/afișarea). | Morala: există probleme pentru care găsim o soluție cu DP, dar pentru care pot exista și alte soluții mai bune (am ignorat citirea/afișarea). | ||
- | In problemele de numarat, exista o **sansa** buna sa putem gasi (si) o formula matematica, care poate fi implementata intr-un mod mai eficient decat o recurenta DP. | + | În problemele de numărat, există o **șansă** bună să putem găsi (și) o formulă matematică, ce poate fi implementată într-un mod mai eficient decât o recurență DP. |
</note> | </note> | ||
<spoiler Hint> | <spoiler Hint> | ||
- | Cate subsiruri au suma **impara**? | + | Câte subșiruri au suma **impară**? |
</spoiler> | </spoiler> | ||
<hidden> | <hidden> | ||
- | <spoiler Solutie> | + | <spoiler Soluție> |
- | Problema este preluata de [[https://infoarena.ro/problema/azerah|aici]]. Solutia se gaseste [[https://www.infoarena.ro/onis-2015/solutii-runda-1#azerah|aici]]. | + | Problema este preluată de [[https://infoarena.ro/problema/azerah|aici]]. Soluția se găsește [[https://www.infoarena.ro/onis-2015/solutii-runda-1#azerah|aici]]. |
</spoiler> | </spoiler> | ||
</hidden> | </hidden> | ||
- | === Expresie booleana === | + | === Expresie booleană === |
- | Se da o expresie booleana corecta cu n termeni. Fiecare din termeni poate fi unul din stringurile **true**, **false**, **and**, **or**, **xor**. | + | Se dă o expresie booleană corectă cu n termeni. Fiecare din termeni poate fi unul din stringurile **true**, **false**, **and**, **or**, **xor**. |
- | Numarati modurile in care se pot aseza paranteze astfel incat rezultatul sa fie **true**. Se respecta regulile de la logica (tabelele de adevar pentru operatiile **and**, **or**, **xor**). | + | Numărați modurile în care se pot așeza paranteze astfel încât rezultatul să fie **true**. Se respectă regulile de la logică (tabelele de adevăr pentru operațiile **and**, **or**, **xor**). |
- | Deoarece rezultatul poate fi prea mare, se cere **restul impartirii** lui la $1000000007$ ($10^9 + 7$). | + | Deoarece rezultatul poate fi prea mare, se cere **restul împărțirii** lui la $1000000007$ ($10^9 + 7$). |
<note> | <note> | ||
- | In schelet vom codifica cu valori de tip char cele 5 stringuri: | + | În schelet vom codifica cu valori de tip char cele 5 stringuri: |
* **false**: 'F' | * **false**: 'F' | ||
* **true**: 'T' | * **true**: 'T' | ||
Line 762: | Line 748: | ||
* **xor**: '^' | * **xor**: '^' | ||
- | Functia pe care va trebui sa o implementati voi va folosi variabilele **n** (numarul de termeni) si ** expr** (vectorul cu termenii expresiei). | + | Funcția pe care va trebui să o implementați voi va folosi variabilele **n** (numărul de termeni) și **expr** (vectorul cu termenii expresiei). |
</note> | </note> | ||
<spoiler Exemplu 1> | <spoiler Exemplu 1> | ||
- | $n = 5$ si $expr = ['T', '&', 'F', '^', 'T']$ (expr = [** true and false xor true**]) | + | $n = 5$ și $expr = ['T', '&', 'F', '^', 'T']$ (expr = [** true and false xor true**]) |
- | Raspuns: $2$ | + | Răspuns: $2$ |
- | Explicatie: Exista 2 moduri corecte de a paranteza expresia astfel incat sa obtinem rezultatul **true** (1). | + | Explicație: Există 2 moduri corecte de a paranteza expresia astfel încât să obținem rezultatul **true** (1). |
* $ T&(F^T) $ | * $ T&(F^T) $ | ||
* $ (T&F)^T $ | * $ (T&F)^T $ | ||
Line 777: | Line 763: | ||
<spoiler Hint> | <spoiler Hint> | ||
- | Complexitate temporală dorita este $O(n ^ 3)$. | + | Complexitate temporală dorită este $O(n ^ 3)$. |
| | ||
- | Optional, se pot defini functii ajutatoare precum **is_operand**, **is_operator**, **evaluate**. | + | Opțional, se pot defini funcții ajutătoare precum **is_operand**, **is_operator**, **evaluate**. |
</spoiler> | </spoiler> | ||
<note tip> | <note tip> | ||
- | Pentru rezolvarea celor doua probleme ganditi-va la ce scrie in sectiunea [[http://ocw.cs.pub.ro/courses/pa/laboratoare/laborator-04?&#sfaturireguli| Sfaturi / Reguli]]. Pentru fiecare dintre cele doua probleme facem o **partitionare dupa un anumit criteriu**. | + | Pentru rezolvarea celor două probleme gândiți-vă la ce scrise în secțiunea [[http://ocw.cs.pub.ro/courses/pa/laboratoare/laborator-04?&#sfaturireguli| Sfaturi / Reguli]]. Pentru fiecare dintre cele două probleme facem o **partiționare după un anumit criteriu**. |
- | Pentru problema** DP or math?** partitionam toate subsirurile dupa critieriul **paritatii sumei subsirului** (cate sunt pare/impare).\\ | + | Pentru problema **DP or math?** partiționăm toate subșirurile după critieriul **parității sumei subșirului** (câte sunt pare/impare).\\ |
- | Pentru problema **expresie booleana** partitionam **toate parantezarile posibile dupa rezultatul lor** (cate dau true/false). | + | Pentru problema **expresie booleană** partiționăm **toate parantezările posibile după rezultatul lor** (câte dau true/false). |
</note> | </note> | ||
=== Bonus === | === Bonus === | ||
- | Asistentul va alege una dintre problemele din sectiunea Extra. | + | Asistentul va alege una dintre problemele din secțiunea Extra. |
<spoiler Hint> | <spoiler Hint> | ||
- | Recomandam sa ** NU** fie una din cele 3 probleme de la Test PA 2017. Recomandam sa le incercati dupa ce recapitulati acasa DP1 si DP2, pentru a verifica daca cunostintele acumulate sunt la nivelul asteptat. | + | Recomandăm să **NU** fie una dintre cele 3 probleme de la Test PA 2017. Recomandăm să le incercați după ce recapitulați acasă DP1 și DP2, pentru a verifica dacă cunoștințele acumulate sunt la nivelul așteptat. |
</spoiler> | </spoiler> | ||
=== Extra === | === Extra === | ||
- | <spoiler Extraterestrii> | + | <spoiler Extratereștrii> |
- | Rezolvati problema [[https://www.hackerrank.com/contests/test-practic-pa-2017-v1-plumbus/challenges/test-1-extraterestrii | + | Rezolvați problema [[https://www.hackerrank.com/contests/test-practic-pa-2017-v1-plumbus/challenges/test-1-extraterestrii |
- | | extraterestrii]] de la Test PA 2017. | + | | extratereștrii]] de la Test PA 2017. |
</spoiler> | </spoiler> | ||
- | <spoiler Secvente> | + | <spoiler Secvențe> |
- | Rezolvati problema [[https://www.hackerrank.com/contests/test-practic-pa-2017-v1-plumbus/challenges/test-1-secvente | + | Rezolvați problema [[https://www.hackerrank.com/contests/test-practic-pa-2017-v1-plumbus/challenges/test-1-secvente |
- | | Secvente]] de la Test PA 2017. | + | | Secvențe]] de la Test PA 2017. |
</spoiler> | </spoiler> | ||
Line 811: | Line 797: | ||
<spoiler PA Country> | <spoiler PA Country> | ||
- | Rezolvati problema [[https://www.hackerrank.com/contests/test-practic-pa-2017-v2-meeseeks/challenges/test-2-pa-country-medie | + | Rezolvați problema [[https://www.hackerrank.com/contests/test-practic-pa-2017-v2-meeseeks/challenges/test-2-pa-country-medie |
| PA Country]] de la Test PA 2017. | | PA Country]] de la Test PA 2017. | ||
</spoiler> | </spoiler> | ||
Line 819: | Line 805: | ||
<spoiler iepuri> | <spoiler iepuri> | ||
- | Rezolvati pe infoarena problema [[http://infoarena.ro/problema/iepuri| iepuri]]. | + | Rezolvați pe infoarena problema [[http://infoarena.ro/problema/iepuri| iepuri]]. |
- | Hint: Exponentiere logaritmica pe matrice | + | Hint: Exponențiere logaritmică pe matrice |
- | Solutie: | + | Soluție: |
* $dp[0] = X; dp[1] = Y; dp[0] = Z; $ | * $dp[0] = X; dp[1] = Y; dp[0] = Z; $ | ||
* $dp[i] = (A * dp[i-1] + B * dp[i-2] + C * dp[i-3]) \ \% \ 666013$ | * $dp[i] = (A * dp[i-1] + B * dp[i-2] + C * dp[i-3]) \ \% \ 666013$ | ||
- | Pentru punctaj maxim, pentru fiecare test se foloseste ecuatia matriceala atasata. | + | Pentru punctaj maxim, pentru fiecare test se folosește ecuația matriceală atașată. |
Complexitate: $O(T * log(n))$. | Complexitate: $O(T * log(n))$. | ||
Line 834: | Line 820: | ||
<spoiler Minimum Path Sum> | <spoiler Minimum Path Sum> | ||
- | Rezolvati pe leetcode problema [[https://leetcode.com/problems/minimum-path-sum/description/#| Minimum Path Sum]]. | + | Rezolvați pe leetcode problema [[https://leetcode.com/problems/minimum-path-sum/description/#| Minimum Path Sum]]. |
</spoiler> | </spoiler> | ||
- | <spoiler Lacusta> | + | <spoiler Lăcusta> |
- | Rezolvati pe infoarena problema [[http://infoarena.ro/problema/Lacusta| Lacusta]]. | + | Rezolvați pe infoarena problema [[http://infoarena.ro/problema/Lacusta| Lăcusta]]. |
</spoiler> | </spoiler> | ||
<spoiler Suma4> | <spoiler Suma4> | ||
- | Rezolvati pe infoarena problema [[http://infoarena.ro/problema/Suma4|Suma4]]. | + | Rezolvați pe infoarena problema [[http://infoarena.ro/problema/Suma4|Suma4]]. |
</spoiler> | </spoiler> | ||
- | <spoiler Subsir> | + | <spoiler Subșir> |
- | Rezolvati pe infoarena problema [[https://www.infoarena.ro/problema/subsir|subsir]]. | + | Rezolvați pe infoarena problema [[https://www.infoarena.ro/problema/subsir|subșir]]. |
</spoiler> | </spoiler> | ||
- | <spoiler 2sah> | + | <spoiler 2șah> |
- | Rezolvati pe infoarena problema [[https://infoarena.ro/problema/2sah | 2sah]]. | + | Rezolvați pe infoarena problema [[https://infoarena.ro/problema/2sah | 2șah]]. |
- | Hint: Exponentiere logaritmica pe matrice | + | Hint: Exponențiere logaritmică pe matrice |
- | O descrie detaliata se afla in [[http://olimpiada.info/oji2015/index.php?cid=arhiva | arhiva OJI 2015]]. | + | O descriere detaliată se află în [[http://olimpiada.info/oji2015/index.php?cid=arhiva | arhiva OJI 2015]]. |
</spoiler> | </spoiler> | ||
Line 865: | Line 851: | ||
===== Referințe ===== | ===== Referințe ===== | ||
- | [0] Capitolul **Dynamic Programming** din **Introductions to Algorithms** de către T. H. Cormen, C. E. Leiserson, R. L. Rivest, C. Stein | + | [0] Chapter **Dynamic Programming**, “Introduction to Algorithms”, Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest and Clifford Stein |
+ | |||
[1] [[http://infoarena.ro/problema/podm]] | [1] [[http://infoarena.ro/problema/podm]] | ||