This shows you the differences between two versions of the page.
|
pa:laboratoare:laborator-02 [2026/03/13 06:58] gabriel.gutu Updated link to previous lab |
pa:laboratoare:laborator-02 [2026/03/15 19:10] (current) aureliu.antonie [Gardurile lui Gigel] |
||
|---|---|---|---|
| Line 61: | Line 61: | ||
| == Enunț == | == Enunț == | ||
| - | Fie un produs matriceal $M = M_1 M_2 ... M_n$. Putem pune paranteze în mai multe moduri și vom obține același rezultat (înmulțire asociativă), dar este posibil să obținem un număr diferit de **înmulțiri scalare**. | + | Fie un produs matriceal $M = M_1 M_2 ... M_n$. Putem pune paranteze în mai multe moduri și vom obține același rezultat (înmulțirea este asociativă), dar este posibil să obținem un număr diferit de **înmulțiri scalare**. |
| Matricea $M_i$ are (prin convenție), dimensiunile $d_{i-1} d_{i}$. | Matricea $M_i$ are (prin convenție), dimensiunile $d_{i-1} d_{i}$. | ||
| Line 359: | Line 359: | ||
| * $dp[i] = (dp[i-1] + dp[i-4]) \ \% \ MOD$ | * $dp[i] = (dp[i-1] + dp[i-4]) \ \% \ MOD$ | ||
| * | * | ||
| - | <note> Așa cum am zis în secțiunea de [[http://ocw.cs.pub.ro/courses/pa/laboratoare/laborator-04?&#sfaturireguli|sfaturi și reguli]] vrem să facem o **parționare** după un anumit **criteriu**: în cazul problemei de față, criteriul de parționare este dacă gardul se termină cu o scândură verticală sau orizontală. | + | <note> Așa cum am zis în secțiunea de [[http://ocw.cs.pub.ro/courses/pa/laboratoare/laborator-02?&#sfaturireguli|sfaturi și reguli]] vrem să facem o **parționare** după un anumit **criteriu**: în cazul problemei de față, criteriul de parționare este dacă gardul se termină cu o scândură verticală sau orizontală. |
| - | De asemenea, tot în secțiunea [[http://ocw.cs.pub.ro/courses/pa/laboratoare/laborator-04?&#sfaturireguli|sfaturi și reguli]] am precizat că nu vrem **să număram un obiect** (un mod de a construi gardul) **de două ori**. Recurența noastră (dp[i] = dp[i-1] + dp[i-4]) nu ia un obiect de două ori pentru că orice soluție care vine din dp[i-4] e diferită de alta care vine din dp[i-1] pentru că diferă în cel puțin ultima scândură așezată) </note> | + | De asemenea, tot în secțiunea [[http://ocw.cs.pub.ro/courses/pa/laboratoare/laborator-02?&#sfaturireguli|sfaturi și reguli]] am precizat că nu vrem **să număram un obiect** (un mod de a construi gardul) **de două ori**. Recurența noastră (dp[i] = dp[i-1] + dp[i-4]) nu ia un obiect de două ori pentru că orice soluție care vine din dp[i-4] e diferită de alta care vine din dp[i-1] pentru că diferă în cel puțin ultima scândură așezată) </note> |
| == Implementare recurență == | == Implementare recurență == | ||
| Line 409: | Line 409: | ||
| De multe ori, este nevoie să folosim câteva tehnici pentru a obține performanța maximă cu recurența găsită. | De multe ori, este nevoie să folosim câteva tehnici pentru a obține performanța maximă cu recurența găsită. | ||
| - | Î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. | + | În prima parte a [[pa:laboratoare:laborator-01|laboratorului 01]] 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. |
| ==== Exponențiere pe matrice pentru recurențe liniare ==== | ==== Exponențiere pe matrice pentru recurențe liniare ==== | ||
| Line 498: | Line 498: | ||
| $$S_i = S_{k}C^{i -k}$$ | $$S_i = S_{k}C^{i -k}$$ | ||
| + | Pentru a aduce un plus de viteză, vom folosi un truc numit [[https://en.wikipedia.org/wiki/Exponentiation_by_squaring|exponențiere rapidă]]. Pe scurt, vom ridica o valoare(notată $a$) la puterea n, împărțind n folosind reprezentarea sa binară. | ||
| - | Î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. | + | De exemplu, dacă am vrea să calculăm: |
| + | $$ 5^{13} = 5^{1101_2}= 5^8 * 5^4 * 5^1 $$ | ||
| + | |||
| + | Exponentul are exact $\lfloor log_2(n) \rfloor + 1$ cifre în baza 2, deci vom avea nevoie de $O(log(n))$ înmulțiri, fiind condiționați de faptul că trebuie să cunoaștem $a^1, a^2, a^4, etc.$ Din fericire, putem calcula rapid acești factori prin ridicarea la pătrat a elementului precedent. | ||
| + | |||
| + | În final, putem implementa un algoritm iterativ pentru calcularea rezultatului $a^n$ iterând prin cifrele din baza 2 ale lui n și înmulțind cu ${a^2}^{k}$ atunci când cifra curentă este 1(pentru viteză, vom folosi operații pe biți în loc de o iterare propriu zisă prin cifre). | ||
| + | |||
| + | Mai jos putem vedea o implementare in C++: | ||
| + | <code cpp> | ||
| + | long long pow(long long a, long long n) { | ||
| + | long long res = 1; | ||
| + | while (n > 0) { | ||
| + | // daca cel mai nesemnificativ bit(LSB) din n este 1 | ||
| + | if (n & 1) { | ||
| + | res = res * a; | ||
| + | } | ||
| + | |||
| + | // calculam urmatorul factor | ||
| + | a = a * a; | ||
| + | |||
| + | // shiftam la dreapta n cu o pozitie(urmatorul bit va deveni LSB) | ||
| + | n >>= 1; | ||
| + | } | ||
| + | return res; | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | Acest truc poate fi folosit pentru orice tip de date pentru care înmulțirea este asociativă(inclusiv matrice). | ||
| Obținem astfel o soluție cu următoarele complexități: | Obținem astfel o soluție cu următoarele complexități: | ||