5.

Matrice rare Matricele rare îşi găsesc aplicabilitatea în modelarea unor procese de natură industrială, economică, tehnică, socială etc. Materialul de faţă îşi propune să trateze modalităţile de reprezentare în structuri de date a matricelor rare, precum şi principalele operaţii matriceale implementate într-un limbaj orientat pe obiecte. În final este prezentată o aplicaţie concretă – estimarea parametrilor unei regresii statistice. 1. Introducere În rezolvarea multor probleme de natură economică, tehnică, socială, a diverselor probleme de optimizare, precum şi în modelarea unor procese industriale şi tehnologice este necesar să se determine modelul matematic care descrie funcţionarea procesului respectiv. Descrierea acestor sisteme fizice conduce la obţinerea unor modele matematice care fie în mod direct, prin modelare, fie prin metoda de rezolvare implică sisteme de ecuaţii algebrice liniare sau probleme de programare liniară a căror matrice a coeficienţilor este rară ( sparse ), în sensul că ponderea elementelor nenule în totalul elementelor matricei este mică. Din punct de vedere practic trebuie remarcat faptul că analiza sistemelor mai sus amintite conduce la obţinerea unor modele matematice de mari dimensiuni care implică sisteme de ecuaţii algebrice liniare de mii de ecuaţii, pentru a căror rezolvare sunt necesare resurse mari de memorie şi timp de calcul. În multe cazuri practice, cum ar fi sistemele în timp real, timpul de calcul este o resursă critică, căreia nu-I este permis să depăşească o valoare limită. Modelele matematice ale proceselor reale implică un număr foarte mare de variabile şi restricţii care prezintă fenomenul de raritate ( sparsity ), adică de slabă interconectare a elementelor sale. Luarea în consideraţie a fenomenului de raritate furnizează un nou mod de abordare foarte eficient, ce implică în dezvoltarea aplicaţiilor informatice folosirea unor structuri de date speciale, care să conducă la reducerea resurselor de memorie şi a timpului de calcul. elemente nenule τ , adică τ << n . Cantitativ, matricele rare sunt caracterizate de ponderea numărului de elemente nenule în totalul de elemente, pondere ce defineşte gradul de umplere al matricei. În aplicaţiile curente se întâlnesc matrice rare cu grade de umplere între 0,15% şi 3%.
2

În general, o matrice (n, n) - dimensională este rară dacă conţine un număr mic de

2. Memorarea matricelor rare. Se consideră matricea:

1 0 0  0 0 −2 A=  0 0 0  0 −1 0 
de 20.

0 0 0 0

0  4 0  0 

Matricea A este un exemplu de matrice rară, ea conţinând 16 elemente nule din totalul Se defineşte gradul de umplere (densitatea) unei matrice prin raportul dintre numărul elementelor nenule şi numărul total al elementelor sale:

G=
unde:

p × 100 (%) n×m (1)

p – numărul de elemente nenule; n – numărul de linii; m – numărul de coloane. Se acceptă, în general, că o matrice este rară dacă densitatea sa este de cel mult 3%. Densitatea

% matricei A este G( A) = 20 , ea fiind prezentată aici în scopul ilustrării conceptului de matrice rară. Structura de date “clasică” folosită pentru manipularea matricelor obişnuite – tablou de dimensiune ( n, m ) alocat la compilare – se dovedeşte a fi ineficientă în cazul în care ,matricea este rară. Un prim avantaj este legat de folosirea neeconomică a spaţiului de memorie prin alocarea de zone mari pentru memorarea elementelor nule, care nu sunt purtătoare de informaţie. Ocuparea unor zone de memorie cu elemente nule nu se justifică deoarece acestea

nu contribuie la formarea rezultatului operaţiilor cu matrice (adunare, înmulţire, etc) ducând totodată şi la mărirea duratei de realizare a acestor operaţii prin ocuparea procesorului cu adunări şi înmulţiri scalare cu zero. Acest inconvenient se manifestă cu atât mai pregnant cu cât dimensiunea matricei este mai mare. Prin urmare, pentru probleme de dimensiuni mari, s-a căutat găsirea unor modalităţi de reprezentare compactă a matricelor rare, în care să se renunţe la memorarea elementelor nule. În acest caz este necesar ca tehnicile de memorare să încorporeze îe lângă elementele nenule şi mijloacele de identificare a poziţiilor acestor elemente în matrice. Sunt prezentate în continuare câteva posibilităţi de memorare compactă a MR. Se face de asemeni o analiză a oportunităţii folosirii fiecărei tehnici în parte, în funcţie de densitatea matricei. Memorarea prin identificare binară se bazează pe natura binară a sistemului de calcul, constând în memorarea numai a elementelor nenule ale matricei într-o zonă primară ZP având tipul de bază corespunzător tipului elementelor matricei şi dimensiunea egală cu numărul elementelor nenule. Structura matricei este indicată printr-o secvenţă binară memorată într-o zonă secundară ZS. Matricea A prezentată anterior se memorează astfel: Zona primară Locaţie 1 2 3 4 Valoare 1 -2 4 -1 Zona secundară Locaţie 1 Valoare 1 0 Locaţie 11 Valoare 0 0 5 0 0 0 15 0 0 0 6 0 16 0 10 0 1 1 0 20 1

Matricea A a fost memorată în ordinea liniilor, dar se poate imagina o altă posibilitate de memorare, în ordinea coloanelor. Zona secundară prezentată mai sus poate fi memorată chiar la nivel de bit. Dacă matricea B ( m, n ) – dimensională are densitatea G şi dacă tipul de bază al matricei (tipul fiecăruia dintre elemente nenule ale matricei) este reprezentat printr-un cuvânt de b octeţi, atunci zona primară va necesita mnG cuvinte de b octeţi iar zona secundară mn/8b cuvinte. Numărul total de cuvinte necesare memorării matricei B prin intermediul celor două zone este mnG + mn/8b. Întrucât pentru memorarea matricei în forma clasică sunt necesare mn cuvinte, raportul dintre cerinţele de memorie ale structurii de mai sus şi a celei standard este: (2) În relaţia (2) s-a considerat că memorarea zonei secundare se face la nivel de bit. Considerând că elementele matricei A sunt reale şi se reprezintă pe 4 octeţi, rezultă:

c1 = G + 1 / 8b

c1A = 0,2 + 1/ 32= 0,23

adică memorarea matricei A conform acestei structuri ocupă de circa 4 ori mai puţină memorie decât cea standard. Folosind relaţia (2) în care se egalează 1 se determină limita superioară a densităţii unei matrice pentru care această structură necesită mai puţină memorie decât cea standard:

c =1

Glim = 1 − 1 / 8b

(3)

Pentru matricea A:

Glim = 1− 1/ 32= 0,96= 96 %

Această structură de memorare diferă de altele prin aceea că în zona secundară este alocată memorie şi pentru elementele nule ale matricei (un singur bit). Structura este mai puţin eficientă pentru matricele de mari dimensiuni foarte rare. Principala dificultate constă în complexitatea programelor de implementare a operaţiilor matriciale. O altă modalitate de memorare prin identificare binară se obţine prin modificarea informaţiilor din zona secundară. Această zonă va conţine pe jumătăţi de cuvânt indicii de coloană ai elementelor nenule din matrice, precum şi anumite informaţii de control pentru identificarea rapidă a poziţiei elementelor nenule în matrice. Structura ZS pe cuvinte este următoarea: Numărul cuvântului Jumătatea stângă Jumătatea dreaptă

este posibil ca matricea să fie memorată în ordine aleatoare.666 = 66. Pentru o matrice rară (100. Structura se dovedeşte a fi deosebit de eficientă.6%) de 10.6% 3 2m 2  (5) În relaţia (5) se ajunge la acelaşi rezultat în cazul unei matrice nepătratice pentru care se trece la limită pentru n → ∞ şi m→ ∞ . ) Numărul total de cuvinte necesare zonei secundare va fi (5 + m+ mnG / 2 rotunjit la cel mai mare întreg.000 cuvinte necesare pentru memorarea standard. ZS are următoarea structură: Locaţie 1 2 3 4 Valoare 4 5 4 1 2 0 1 În reprezentarea de mai sus s-a considerat că elementele nenule sunt reprezentate pe 4 octeţi astfel că o jumătate de cuvânt în zona secundară se reprezintă pe 2 octeţi. cu ceva mai puţin (0. treilea element memorat … … Indicele de coloană al ultimului element memorat 5 1 3 5 6 2 Pentru matricea A. Raportul dintre cerinţele de memorie ale acestei structuri de identificare binară şi a celei standard este: (4) Pentru o matrice pătrată (m = n). Deoarece fiecare element nenul al matricei este identificat individual. Numărul total de cuvinte necesar memorării unei matrice prin ) intermediul celor două zone ZP şi ZS este (5 + m+ 3mnG / 2. structura de mai sus necesită un total de 6600 + ( 5 + 100 + 6600 )/2 = 9952 cuvinte. Se face observaţia că în cazul matricelor pătrate în primul cuvânt din ZS se va memora dimensiunea matricei.1 2 3 4 … k k+1 k+2 … j Numărul de linii Numărul de coloane Numărul de elemente nenule Numărul de elemente nenule în Numărul de elemente nenule în linia1 linia 2 Numărul de elemente nenule în etc linia 3 … … Numărul de elemente nenule în ultima linie Indicele de coloană al primului Indicele de coloană al celui de-al element memorat doilea element memorat Indicele de coloană al celui de-al etc. Întrucât densitatea elementelor nenule ale unei matrice rare este de obicei între 1% şi 3%. Memorarea compactă aleatoare constă în utilizarea unei zone primare ZP. egalând c2 = 1 în (4) şi trecând la limită pentru m→ ∞ rezultă valoarea maximă a densităţii unei matrice rare pentru care structura prezentată este eficientă: c2 = 3G 5 + m + 2 2mn Glimm →∞ 2 5+ m 1 −  = 0. Matricea A se memorează astfel: Locaţia Valoare Indice linie Indice coloană 1 1 1 1 2 -2 2 3 3 4 2 5 4 -1 4 2 . conţinând numai elementele nenule ale matricei şi a două zone secundare conţinând indicii de linie şi de coloană corespunzătoare elementelor nenule. Prin structura de memorare prezentată mai sus se pot memora matrice a căror dimensiune maximă este de 9999 de linii sau coloane cu numărul de elemente nenule memorate de 10 8 – 1.100) – dimensională cu o medie de 66 elemente nenule pe linie.

este suficient ) pentru identificarea elementului în matrice. Raportul dintre cerinţele de memorie ale acestei structuri şi a celei standard este (7) Valoarea limită a densităţii matricei pentru care această structură este eficientă este: G = 50%. Şi în acest caz se pot imagina mai multe structuri de memorare. Astfel. Numărul total de cuvinte necesare memorării unei matrice ( m. matricea A se memorează astfel: Locaţia Valoare Indice agregat 1 1 1 2 -2 12 3 4 22 4 -1 9 Pentru a regăsi indicele de linie şi de coloană al oricărui element memorat în locaţia k se utilizează următoarea tehnică de calcul: . fiind în schimb mai puţin rapidă în ce priveşte manevrarea datelor. Sfârşitul matricei este marcat prin prezenţa în ZP a unui element fals cu valoarea zero. precum şi elemente false care indică începutul fiecărei linii şi sfârşitul memorării întregii matrice. precum şi a elementelor nenule situate pe această diagonală. unde n este numărul de coloane a matricei. Memorarea compactă sistematică presupune că elementele nenule ale unei matrice rare sunt memorate într-o anumită ordine (pe linii sau pe coloane). Se prezintă în continuare o altă posibilitate de memorare în care se va utiliza o singură zonă secundară de dimensiune egală cu numărul de elemente nenuşle ale matricei. care conţine indicii de coloană ale elementelor nenule din matricea considerată. În cazul matricelor simetrice această structură de memorare se poate simplifica prin memorarea numai a elementelor nenule de deasupra diagonalei principale. Cea prezentată în continuare este caracterizată prin faptul că utilizează o singură zonă secundară ZS.coloana j este obţinută ca: j este mai mic întreg ≥ Indice agregat (k)/n. ne putwem dispersa de indicii de linie. Raportul dintre cerinţele de memorie ale acestei structuri şi a celei standard este: c3 = 3G (6) Egalând relaţia (6) cu unitatea se determină valoarea limită a densităţii matricei pentru rare această structură este eficientă: lim . Pentru matricea A.linia i este determinată astfel: i = Indice agregat (k) – ( j – 1 ) n. Pentru o memorare în ordinea liniilor. Numărul total de cuvinte necesar memorării matricei este 2mnG. În acest caz nu este necesar să se memoreze în zonele secundare indicii de linie. Dacă elementul ij este memorat în locaţia k a zonei primare atunci în zona secundară se va memora numărul G = 333% . respectiv de coloană. conţinând simultan informaţii asupra indicilor de linie şi de coloană. . fiecărui element din zona primară i se ataşează în zona secundară un număr întreg din care se pot determina indicii de linie şi de coloană. un element zero în ZS marchează prezenţa unui element fals şi acesta specifică în ZP numărul liniei elementelor de la dreapta locaţiei. însă se cere specificarea începutului fiecărei linii.Avantajele memorării compacte aleatoare constau în faptul că noi elemente nenule ale matricei pot fi adăugate la sfârşitul zonelor de memorare fără a perturba celelalte elemente. precum şi o manevrabilitate rapidă a datelor. a ≠0 i + ( j − 1 n. memorarea în acestă formă este următoarea: Locaţia 1 2 3 4 5 6 7 8 ZP 1 1 2 -2 4 4 -1 0 ZS 0 1 0 3 5 0 2 0 c4 = 2G . De exemplu.n ) – dimensionale este în acest caz 3mn. Utilizând acest artificiu. Acest număr. În structura de mai sus pentru identificarea elementelor nenule ale matricei rare au fost folosite două zone secundare corespunzătoare indicelui de linie şi de coloană. Avantajul acestei structuri de memorare constă în faptul că necesită mai puţină memorie decât cea precedentă. singur.

Este prezentată în continuare o parte a clasei MR. În acest caz memorarea standard. De aceea structurile de memorare bazate pe această tehnică sunt folosite pentru memorarea şi manipularea matricelor rare de mari dimensiuni. Structura propusă utilizează o zonă principală ZP pentru memorarea elementelor nenule şi trei zone secundare. Matricea A se memorează după cum urmează: Locaţia ZP ZSL ZSC ZSU 1 1 1 1 &2 2 -2 2 3 &3 3 4 2 5 &4 4 -1 4 2 NULL unde prin “&2” se înţelege adresa celei de-a doua locaţii. cerinţe care de cele mai multe ori sunt contradictorii. ZSC – pentru indicii de coloană şi ZSU pentru memorarea adresei următorului element al matricei. 3. Raportul dintre cerinţele de memorare ale acestei structuri şi a celei standard este: c6 = 4G (9) Prin urmare această structură de memorare este eficientă pentru memorarea matricelor cu o densitate a elementelor nenule de maximum 25%. câteva dintre funcţiile şi operatorii implementaţi şi secţiunea privată. Principiul fundamental de programare cu matrice rare constă în memorarea şi manipularea numai a elementelor nenule. Raportul de memorare este: Pentru această structură de memorare numărul maxim de cuvinte necesar pentru a c5 = 2G + 2( m + 1) / ( mm ) (8) Se constată că structura este eficientă pentru memorarea matricelor rare cu o densitate a elementelor nenule de maximum 50%. ceea ce creează necesitatea considerării unor tehnici speciale de memorare. conţinând constructorii. devenind ineficientă. În timpul operaţiilor de inversare a matricelor rare noi elemente nenule sunt continuu generate iar altele sunt anulate şi deci structurile de memorare trebuie să fie capabile să execute aceste modificări într-un mod eficient. O cerinţă esenţială în programarea matricelor rare constă în memorarea şi executarea operaţiilor numerice numai cu elementele nenule ale matricei. n ) – dimensională este 2(mnr+ m+ 1 . programare şi analiză numerică. este recunoscută necesitatea unei anumite de structuri de memorare a datelor şi o anumită tehnică de manipulare a acestora în cadrul unui algoritm în care sunt implicate matricele rare. de a salva memorie şi timp de calcul. de evitare a buclelor complete. Un program de calcul cu matrice rare este cu atât mai eficient cu cât timpul de calcul şi cerinţele de memorie necesare sunt mai reduse faţă de acelea ale unui program readiţional. este abandonată şi înlocuită cu metode de memorare adecvate. În scopul ilustrării principalelor operaţii efectuate asupra matricelor rare s-a făcut implementarea acestora în limbajul VisualC++. Software orientat spre lucrul cu matrice rare Metodele de calcul cu matrice rară pentru a fi eficiente trebuie să beneficieze de proporţia mare de elemente nule din aceste matrice. În general. . câteva dintre acestea fiind prezentate în paragraful anterior. Memorarea cu ajutorul listelor reprezintă o extensie a memorării compacte aleatoare. datorită flexibilităţii în manevrarea datelor. De aceea tehnica de programare trebuie să realizeze o proporţie convenabilă între timpul de calcul şi memoria utilizată. de sortare şi ordonare în structuri speciale în vederea menţinerii structurii de matrice rară şi a stabilităţii numerice. astfel: ZSL pentru memorarea indicilor de linie ai elementelor nenule. destructorul.) memora o matrice rară ( m. Pentru reprezentarea matricelor s-a ales memorarea compactă aleatoare.

* tip e. ( MR ). }. + & & MR operator (MR x). având impusă în schimb valoarea gradului de umplere. ~ ( virtual MR ). int n. float). l şi c sunt pointeri la masive de întregi reprezentând linia. transpunerea. S-a realizat de asemeni o funcţie (Generate(float)) care să construiască o matrice rară cu elemente aleatoare. În continuare se face o prezentare detaliată a operatorilor care implementează principalele operaţii matriceale: adunarea. & * MR operator (float). ( void Delete ). * int l. dim serveşte pentru memorarea numărului de elemente nenule. ( voidInsert ). vizualizarea lor. iar e este pointer la un masiv având tipul de bază tip (tipul de bază al elementelor matricei). transpunerea. int. : private int label. void Generate(int. În cadrul secţiunii private.classMr { public . * void FileWrite (const char ). MR (int. ( void Write ). scăderea. înmulţirea şi inversarea. ( void Change ). void Zero (float). .. Pe parcursul dezvoltării clasei MR s-a dovedit necesară implementarea unei funcţii ( void Zero (float) ) care să elimine dintr-o matrice acele elemente care sunt foarte mici (cu câteva ordine de mărime mai mici decât restul elementelor). & * & MR operator (MR x). ( long Find ). void Read(const char *). * int c. () float Tras * tip Full(int &). scrierea şi citirea din fişiere. label reprezintă o etichetă ce serveşte pentru identificarea matricei. int). & & MR operator (MR x). long dim. respectiv coloana elementelor nenule. Softul realizat vizează principalele operaţii necesare manipulării matricelor rare: construirea acestora (prin introducerea datelor de la tastatură). datorate numărului finit de octeţi în care se face reprezentarea diverselor tipuri de date în calculator. &I MR (int). Aceste elemente cu valoare nesemnificativă sunt rezultatul erorilor sistematice introduse prin rotunjiri. & !( MR operator ).. principalele operaţii matriceale: adunarea. n este dimensiunea matricei. void One (float). înmulţirea şi inversarea. scăderea.

p->FileWrite(“p.n) return * this.1[i]. for (i = 0.> label.e[i]. i < x. Adunarea. x. k++. scăderea şi transpunerea Adunarea matricelor rare presupune parcurgerea câtorva paşi: • determinarea numărului de elemente nenule ale matricei sumă. Funcţia Found ( ) este definită în afara clasei MR şi serveşte pentru găsirea elementelor comune celor două matrice.thois->1. pentru a conferi o mai mare putere de sugestie operaţiilor matriceale implementate.> n! = x. AfxMessage Box ("\nAdunar e : %d + %d" . i + + ) if (Found ( x. this − > c)) D . if(D) p->e[D-1]+=x. this->FileWrite(“this.c[i]. p->e[k]=x. this. i<x.txt”).p->e. this. x. p->c[k]=x.-. i ++) { p-> 1[i]=this->1[i]. MR & MR :: operator + (MR & x ) { int i.this->dim.label). MR * p. însumarea lor şi adăugarea la matricea sumă.4. Prin “elemente comune” au fost desemnate acele elemente caracterizate prin indicii de linie şi de coloană care sunt nenule în ambele matrice. x. • determinarea elementelor din cea de-a doua matrice care nu au corespondent în prima şi adăugarea lor la matricea sumă. tis ->label+x. k. D = x.dim.p->c. i++) { D=Found(x.FileWrite(“x. } } Sortare(p->1. Este prezentat în continuare operatorul care implementează operaţia de adunare.1[i].dim. for(i=0.1[i].txt”).c[i]. D. p = new MR (this ->n. else { p->1[k]=x.> dim.txt”). this->c). p-> e[i]=this->e[i].label). i <this-> dim. D. p-> c[i]=this->c[i]. x. D + = this.e[i].c[i].dim.> 1. structurată conform paşilor prezentaţi mai sus. } k=this->dim. return *p. for (i = 0. • determinarea acelor elemente din prima matrice care nu au corespondent în cea de-a doua şi adăugarea lor la matricea sumă. if (this.p->dim). { . • determinarea elementelor comune celor două matrice. • alocarea memoriei corespunzătoare acestui număr. Pentru implementare s-a folosit suprascrierea operatorilor.

// Sunt cel mult D. (m.dim. se utilizează o modificare a procedurii standard.1[j] { T=Found(this->1[i]. D this->label * x. j<x. Întrucât elementul aik al matricei A poate conntribui la formarea tuturor elementelor liniei i a matricei C. Procedura standard de înmulţire se modifică astfel încât în bucla internă să se execute toate operaţiile corespunzătoare unui element al matricei A. i++) for(j=0. În cazul de faţă se inversează pointeriii la masivele de întregi reprezentând liniile. j++) if(this->c[i]==x. D. } else . MR& operator * (MR &x) int i. prin coloanele matricei B. * q. n) – dimensională. } 5.c[j]. (l. label). i<this->dim. long T. p=new MR(this->n. până la determinarea completă a liniei i a matricei C. respectiv coloanele elementelor nenule: MR&MR::operator ++() { int*p. p->e[R]=this->e[i] *x.label). // Pas 1 : Câte elemente va avea matricea finală? D=0. printf (“\nÎnmulţire : &d * *d”. . tip S. R++. R. j. k. for(i=0. singura diferenţă fiind accea că în cazul elementelor comune se face scaderea lor. p -> e[i] trebuie să fie (şi sunt) nule după iniţializare R=0. i<this->dim. if (this ->n!=x.dim. 1) – dimensională.c[j]. // Se ia fiecare element din A şi se înmulţeşte cu tot ce // se poate din B // Elem.n) – dimensională. this->label. if(T) if(p->e[T-1]) // deja iniţializat p->[T-1]+=this->e[i]*x. (m. considerând influenţa liniilor matricei A asupra matricei produs C. Implementarea şi inversarea matricelor rare Pentru înmulţirea matricei rare A. p->c).this->c=p.j<x.n) return *this. x. constând în inversarea indicilor de linie şi coloană între ei.”). Se prezintă în continuare acest operator.1[j]) D++.e[j]’ else // se face iniţializarea { p->1 [R]=this->1[i]. Transpunerea matricelor rare este similară celei efectuate pe structră tablou. în locul adunării.this->1=this->c. Mr *p. . x. return *this.Implementarea operatorului de scăderea este absolut similară celui de adunare. cu matricea rară B. p=this->1. AfxessageBox(“\nTraspunere. se determină mai întâi această contribuţie după care se continuă cu următorul element al liniei i a matricei A. for (i =0.e[j]. p->1. j++) if(this->c[i] ==x. p->c[R]=x.D. i++) for (j=0. Acest principiu este utilizat în implementarea operatorului de înmulţire a matricelor rare din cadrul clasei MR prezentate în paragraful anterior. // pas 2 : Calcul efectiv.

q->c[D]=p->c[i]. for(i=0. n-1 An-1 = A*Bn-2 p n −1 = pn = 1 tr(A n −1 ) B n −1 = I − p n −1 * A n −1 n −1 Bn = I − p n * A n n: An = A* Bn-1 1 tr(A) n Prin tr (A) se înţelege urma matricei A (suma elementelor diagonale) iar I reprezintă matricea unitate de aceeaşi dimensiune cu matricea A.dim. for(i=0. q->e[D]=p->e[i]. q->dim. Sortare (q->1. i<p->dim. q->e.dim.n. label). } } else if(this->c[i] <x.c[j]. } delete p. q->c. Acesta constă în parcurgerea unui număr de paşi egal cu dimensiunile matricei: 1 p1 = tr(A 1 ) B1 = I − p1 * A 1 1 1: A1 = A 1 p 2 = tr ( A2 ) 2 2: A2 = A*B1 B2 = I – p2 * A2 . p->c[R]x. *B. { Prin analiza acestui operator se constată că matricea rezultată păstrează structura de matrice rară (zerourile apărute au fost eliminate). Bn este o matrice idenitic nulă. // Se scot zerourile şi se sortează D=p->dim. Krîlov a demonstrat că după parcurgerea celor n paşi. float P. MR & MR::operator ! () { MR A(n. de aici rezultă inversa matricei A: A-1 = pn * Bn-1 (10) Se prezintă în continuare operatorul de inversare a matricelor rare care implementează algoritmul prezent.. E=this->I(n).1).. this->label-x.E(n. .label).label).-. Pentru implementarea operatorului de inversare s-a folosit algoritmul lui Krîlov. i<p->dim.e[j]. AfxMessageBox(“Inversare”).D. i++) if (!p->e[i]) D. T(n.. p->e[R]=this->e[i] *x. i++) if(p->e[i]) { D. B=new MR(n.. q=new MR(this->n.{ p->1[R]=this->1[i]. q->1[D]=p->1[i].dim. dim.1[j])) // x e totusi sortat j=x. label). return *q. R++.

(*). for(int k=2.txt”). } Avantajele acestui algoritm constau în simplitatea implementării şi precizia rezultatelor. A. factori care din diverse motive nu pot fi luaţi în considerare.txt”).txt”). B=A-EP.Tras().Tras().T<=*this. al numărului de utilaje de mare performanţă deţinute (x1) şi al veniturilor suplimentare acordate salariaţilor (x2).txt”).txt”). rezultate în urma aplicării modelului liniar. specificăm modelul astfel: yi = a0 + a1x1i + a2x2i + ui (11) unde ui reprezintă o variabilă “reziduală” ce caracterizează influenţa altori factori asupra variabilei efect y.k++) { A=T*(*B). Tabelul 1 y – productivitatea muncii (creşteri procentuale) 1 2 5 6 7 x1 – utilaje de mare randament (bucăţi) 2 4 4 4 5 x2 – venituri suplimentare (mil.FileWrite(“B. P=((float) 1/ float)K)*A. datorată folosirii unui număr redus de operaţii de împărţire. (*B).txt”). Estimarea parametrilor unei regresii statistice folosind clasa MR Se consideră datele din tabelul 1 ce caracterizează cinci întreprinderi din punctul de vedere al productivităţii (y). (*B). B=(B*)((float) 1/(float)P.FileWrite(“B. se analizează pe cale grafică dependenţa variabilei – efect (y) în raport cu fiecare dintre variabilele cauzale. k<n. T. 6.FileWrite(“A. y • • • • • y • • • x x1 Figura 1 – Dependenţa y=f(x1).txt”). A. return *B.FileWrite(“A.FileWrite (“A. rezultând: .FileWrite(A”. lei) 1 1 3 5 5 Pentru a specifica forma funcţiei. y i = a 0 + a1x 1i + a 2 x 2i (12) Relaţia (12) se scrie pentru fiecare set de valori prezentate în tabelul 1. B=T-E*P. Dacă simbolizăm “ specificăm modelul astfel: y ” valorile “ajustate”. A. FileWrite(“B. } A=T*(*B). P=((float)1/(float)n*Atras(). y=f(x2) • x x2 • Întrucât norul de puncte din fiecare reprezentare grafică sugerează o linie dreaptă. Se doreşte determinarea unei funcţii care să caracterizeze dependenţa dintre productivitate şi celelalte două variabile considerate independente (număr de utilaje şi venituri suplimentare). P=T.

U U. să decidă asupra structurilor de date ce vor fi folosite pentru memorare şi efectuarea calculelor. datorat caracteristicilor reale ale sistemelor. În cele mai multe dintre cazuri baza de calcul a acestor factori o constituie statistică matematică şi teoria probabilităţilor. şi. a1 şi a 2 . permiţând în acest fel o mai bună activitate de control şi comandă a acestuia. pentru aflarea matricei A sunt necesare două operaţii de transpunere. ′ U ′U = ( Y − XA ) ( Y − XA ) A = ( X ′X ) X ′Y −1 (17) O determinare cât mai precisă a matricei parametrilor unei regresii presupune existenţa unui număr foarte mare de date (număr mare de linii în matricea X). În acest scop se utilizează ∑ u 2t t . considerată a fi imposibilă. [3] Această metodă presupune minimizarea expresiei adică. . Concluzii Asistăm în prezent la un fenomen care tinde să atingă tot mai multe domenii de activitate: necesitatea de a cunoaşte cît mai precis anumiţi factori sau parametri ce caracterizează domeniul respectiv. Având deja definiţii operatorii de transpunere. implementarea relaţiei (17) presupune scrierea unei singure linii de cod: A = (! ( ( + + X ) * X ) ) * ( + + X ) * Y (18) Aşadar. astfel că este pe deplin justificată folosirea unor structuri de memorare specifice matricelor rare. Softul orientat spre lucrul cu matrice rare exploatează caracterul de raritate al structurilor manipulate. la care se adaugă necesitatea unei rezolvări rapide. Matricele X şi Y asupra cărora se operează au în multe dintre cazurile practice o densitate foarte mică. odată cu apariţia sistemelor în timp real. înmulţire şi inversare. însă timpul necesar calculelor. ceea ce conduce la necesitatea rezolvării unor probleme liniare de foarte mari dimensiuni. Dar (16) unde prin U' s-a notat transpusa matricei U. fapt ce justifică implementarea relaţiei (17) pe o structură de matrice rare. care până nu de mult erau fie consideraţi aleatori. Caracterul de raritate al structurilor implicate în rezolvarea problemelor. se dovedeşte a fi tot mai mult o resursă critică. În multe cazuri practice valorile acestor date sunt nule. în cadrul unui sistem în care timpul reprezintă o resursă critică. se poate imagina un soft care să facă o evaluare anterioară calculelor asupra densităţii matricei. astfel încât timpul afectat calculelor să fie minim. fie nu se punea problema determinării lor. Referitor la lucrul cu matrice în general. justifică pe deplin introducerea în aplicaţiile informatice asociate a unor structuri de date adaptate acestor particularităţi. în funcţie de aceasta. iar x 21   a0   u1       x 2 2   a1   u2  ⋅ +            x 2 n   a n   un       (13) (14) (15) Y = XA + U Y = XA Determinarea dependenţei dintre variabila efect şi variabilele cauză însemnă determinarea valorilor numerice ale parametrilor metoda celor mai mici pătrate. În [3] se demonstrează că prin minimizarea relaţiei (16) se ajunge la expresia: a 0 . Cunoaşterea acestor factori oferă o descriere mai detaliată a sistemului în care se lucrează. oferind un dublu avantaj: memorie şi timp. 7. y1   1 x11     y 2   1 x12    =       y   1 x1 n  n  Aşadar. în concordanţă cu dinamica crescândă a sistemelor actuale. În ultimii ani memoria nu mai constituie o problemă. trei înmulţiri şi o inversare. matriceal.

int *vc1. Procedura “ adunare” are urmatorii pasi: P1-Alocarea celor trei vectori corespunzatori rezultatului.int *&v2. P8-Compresia celei de-a doua matrice prin procedura “ matrar“ .int *&vlr) care primeste ca parametri trei pointeri catre cei trei vectori corespunzatori primei matrice si dimensiunea lor.int *vl2. Pe fiecare pozitie din cei trei vectori se vor gasi rangul liniei.int *&v1.int *&vcr. Programul contine urmatoarele proceduri : • O procedura de alocare si initiere de la tastatura a unei matrice: void incarcare(float **&a. dimensiunile matricei. iar daca este se trece la pasul urmator.int *vc2.float *&v3) care primeste ca parametri un pointer la matrice. P2.int n2. in primul vector.int n) care primeste ca parametri un pointer la matrice si dimensiunile matricei si returneaza numarul de elemente nenule din matrice daca aceasta este rara sau 0 daca nu. P7-Verificarea celei de-a doua matrice daca este rara prin procedura “ ifrar” si daca nu este se afiseaza ca nu este rara si se incheie rularea programului.int m. rangul coloanei. numarul de elemente nenule din matrice si cate un pointer la fiecare din cei trei vectori care rezulta. • O procedura care calculeaza diferenta dintre doua matrice rare comprimate in prealabil: int scadere(float *vel1.int m. . Programul urmeaza urmatorul algoritm: P1-Introducerea de la tastatura a dimensiunilor matricelor( matricele trebuie sa aiba aceleasi dimensiuni pentru a le putea aduna sau scadea) . • O procedura care aduna doua matrice rare ce au fost in prealabil comprimate : int adunare(float *vel1.int k. P3.int *vl1.float *vel2. P4-Compresia primei matrice prin procedura “ matrar“ .Parcurgerea vectorilor primei matrice. P5-Dezalocarea primei matrice prin procedura “ distruge“. primiti prin referinta . P10-Suma celor doua matrice prin procedura “ adunare”. trei pointeri catre cei trei vectori corespunzatori celei de-a doua matrice si dimensiunea lor si trei pointeri catre trei vectori corespunzatori matricei rezultate si returneaza dimensiunea vectorilor rezultati. P2-Alocarea si initializarea primei matrice prin procedura “incarcare”.int *vl1. P6-Alocarea si initializarea celei de-a doua matrice prin procedura “incarcare”.int m.int n1.int *vc1. in al doilea vector si elementul nenul din matrice in cel de-al treilea vector. • O procedura care dezaloca o matrice alocata dinamic: void distruge(float **&a.int *&vlr) avand aceiasi parametri de intrare si returnand tot dimensiunea vectorilor rezultati.int n) care primeste ca parametri un pointer catre o matrice de numere reale prin referinta pentru a permite modificarea sa si dimensiunile matricei. O matrice rara este o matrice ce contine mai mult de doua treimi valori nule.int n.int *vc2. • O procedura care transforma o matrice rara in cei trei vectori despre care am vorbit mai sus: void matrara(float **a.int m.int *&vcr. iar daca este se trece la pasul usmator. P3-Verificarea primei matrice daca este rara prin procedura “ ifrar” si daca nu este se afiseaza ca nu este rara si se incheie rularea programului. P11-Diferenta celor doua matrice prin procedura “ scadere”. iar pentru fiecare iteratie se parcurg pasii P3-P5. P9-Dezalocarea celei de-a doua matrice prin procedura “ distruge“.float *&velr.int *vl2.int n) care primeste ca parametri un pointer la matrice transmis prin referinta si dimensiunile ei.int n2.Parcurgerea vectorilor celei de-a doua matrice pana la egalarea rangului liniei si coloanei elementului curent din prima matrice si copierea lor in vectorii rezultanti. O astfel de matrice se poate comprima foarte simplu prin inlocuirea sa in memorie cu trei vectori cu dimensiuni egale cu numarul de elemente nenule din matrice .float *&velr. • O procedura care verifica daca o matrice este rara: int ifrar(float **a. Al treilea vector va fi ordonat dupa primul vector si dupa cel de-al doilea vector.int n1.float *vel2.Suma si diferenţa matricelor rare Tendinta de a micsora spatiul ocupat de anumite date in memoria unui calculator a dus la proiectarea si dezvoltarea a multor algoritmi de compresie .

Procedura “ scadere” urmeaza pasii: P1-Alocarea si initializarea unui vector auxiliar cu valorile vectorului de elemente corespunzator celei de-a doua matrice. P3-Returnarea lungimii vectorilor rezultati.h> //procedura de compresie a unei matrice rare(normalizarea sa) void matrara(float a[10][10]. } } //procedura de verificare a unei matrice daca este rara int ifrar(float a[10][10].i++) for(int j=0.float v3[34]) { int k=0. P5-daca nu exista in cea de-a doua matrice element nenul pe linia si coloana corespunzatoare elementului curent din prima matrice se copiaza acest element si pozitia sa in matrice in vectorii rezultanti.int m.i<n. for(int i=0. //returneaza numarul de elemente nenule else return 0. P2-Efectuarea diferentei prin adunarea primei matrice cu matricea a doua inmultita cu -1. se verifica daca in cea de-a doua matrice sunt elemente de rang al liniei sau coloanei mai mare decat al ultimului element din vectorii primei matrice si daca exista astfel de elemente se vor memora aceste elemente in continuare in vectorii matricei rezultate. P6-Dupa ce se termina de parcurs vectorii primei matrice.P4-Daca rangul liniei si coloanei sunt aceleasi la elementele curente din cele doua matrice se aduna elementele si se introduce rezultatul in vectorii rezultanti si se sare la P6. //vectorilor v3[k]=a[i][j]. //nule if(k>m*n*2/3) return m*n-k.int n) { int k=0. } .int n.j<m.j++) { cout<<"Introduceti elementul:"<<i+1<<".j++) if(a[i][j]) { v1[k]=i.int n) { for(int i=0. } } //procedura dealocare si incarcare de la tastatura a unei matrice void incarcare(float a[10][10].int m.i++) for(int j=0.i++) //contorizarea for(int j=0."<<j+1<<" : ". //initiere v2[k]=j.int m.int v2[34].int v1[34].i<n. #include<iostream.j<m. //introducerea datelor de la tastatura cin>>a[i][j].j++) //elementelor if(!a[i][j]) k++.j<m.i<n. //cu elementele nenule ale matricei k++. altfel se trece prin pasul P5. for(int i=0. luate cu semn schimbat. P7-Se returneaza lungimea vectorilor rezultati.

} else { velr[l]=vel1[i]. if(a) { velr[l]=a. } k++.//procedura de adunare a doua matrici rare memorate in vectori int adunare(float *vel1. //primei matrice vcr[l]=vc2[j]. vcr[l]=vc2[j].i++) // parcurgerea vectorilor primei matrice { for(int j=k.(vl1[i]>vl2[j])||((vl1[i]==vl2[j])&&(vc1[i]>vc2[j])).k=0.int n1.i<n1. float a. //liniei si coloanei elementului vlr[l]=vl2[j]. } if((vl1[i]==vl2[j])&&(vc1[i]==vc2[j])) //daca rangul liniei si coloanei sunt { // aceleasi se aduna elementele a=vel1[i]+vel2[j]. corespunzatoare elementului din vcr[l]=vc1[i].int *vl1. } return l.j++. l++.int *vc1.k++) //parcurgerea vectorilor { // celei de-a doua matrice //pana la egalarea rangului velr[l]=vel2[j]. } } if(k!=n2) de-a doua matrice sunt elemente de for(i=k.int vlr[67]) { int l=0. for(int i=0. //returneaza lungimea vectorilor rezultati } //procedura de scadere a doua matrice rare memorate fiecare in cate trei vectori //daca in cea //rang al liniei sau // vectorii primei //pe linia si coloana //daca nu exista in cea de-a doua //se vor memora aceste elemente //vectorii matricei .int n2.l+ +.float velr[67].float *vel2. in continuare in vlr[l]=vl2[i].int *vl2.int vcr[67]. matrice element nenul vlr[l]=vl1[i]. matrice l++.i++) coloanei mai mare decat al { //ultimului element din vectorii primei matrice velr[l]=vel2[i]. l++.i<n2. rezultate vcr[l]=vc2[i]. vlr[l]=vl2[j].int *vc2.

vcs[67].float *vel2.vls[67].vels[67].m.//se transforma prima matrice in trei vectori for(int i=0.// se aloca si se initializeaza de la tastatura "<<vc1[i]<<" //se afiseaza cei trei .m.vlr).//se returneaza dimensiunea vectorilor rezultati } //************************************************************** void main () { float x[10][10]. //se // de coloane incarcare(x.int scadere(float *vel1.//se verifica daca prima matrice este rara if(l1) { matrara(x.vel2[34].// se aloca si se initializeaza de la tastatura prima matrice int l1=ifrar(x.vl1[34].vla[67].m.vc1[34].m.vl2.velr.int n2.vel1).//se face scaderea prin //adunare: a-b=a+(-b) delete [n2]vaux.int *vl1.//se declara dimansiunile matricelor //si pointerii la vetorii de ranguri ale liniilor si cocloanelor cout<<"Introduceti numarul de linii(<=10) :".i<l1. introduc de la tastatura cin>>n. //numarul de linii si cout<<"Introduceti numarul de coloane(<=10) :".vel2).i<n2.int *vc1.int vcr[67].vc2.vca[67].float velr[67].n).n).//se verifica daca a doua matrice este rara if(l2) { matrara(y.vl2.vl2[34].int vlr[67]) { float *vaux=new float[n2]. int k=adunare(vel1.vl1.n1.vc2[34].n2.//se dezaloca vectorul auxiliar return k. cin>>m.int n1.vel1[34].//se declara pointerii la cele doua matrice initiale //si la vectorii de elemente int n.vcr.int *vc2. //se foloseste un vector auxiliar in care se introduc //valor ile nenule cu semn schimbat din prima matrice for(int i=0.m.n).vela[67].m.n).i++) vaux[i]=-vel2[i].vl1.n.vc1.//se transforma a doua matrice //in trei vectori //primei matrice incarcare(y. a doua matrice int l2=ifrar(y.vaux.vc1.n.m.vc2.y[10][10].i++) vectori corespunzatori cout<<"\n"<<vl1[i]<<" "<<vel1[i].int *vl2.

vc2. //initiere .h> //procedura de compresie a unei matrice rare(normalizarea sa) void matrara(float **a. } "<<vc2[i]<<" #include<iostream.j<m. } else cout<<"Nu este matrice rara!!!".i<l2.vl1.int *&v1.vla).vel2.// vectori v3=new float[l].vc2. for( i=0.vc1.i++) //se afiseaza vectorii rezultati cout<<"\n"<<vls[i]<<" "<<vcs[i]<<" "<<vels[i]. for( i=0.i++)//se afiseaza cei trei vectori corespunzatori celei cout<<"\n"<<vl2[i]<<" "<<vel2[i].vc1. } else cout<<"Nu este matrice rara!!!".j++) if(a[i][j]) { v1[k]=i.vels.i<la.vela.int *&v2.int n.vca.//se scad //cele doua matrice rezultand tot trei vectori in care va //fi memorata if(!(ls<m*n/3))cout<<"Matricea diferenta nu este rara ".l1.//de-a doua matrice int la=adunare(vel1.//se verifica daca //matricea rezultata este rara cout<<"\n".l1.//alocare v2=new int[l].i++) //se afiseaza vectorii rezultati cout<<"\n"<<vla[i]<<" "<<vca[i]<<" "<<vela[i].float *&v3) { int l=m*n-k.//se aduna //cele doua matrice rezultand tot trei vectori in care va //fi memorata if(!(la<m*n/3))cout<<"Matricea suma nu este rara ".vel2.vl1.vcs.//se verifica daca //matricea rezultata este rara cout<<"\n".l2.l2.for(int i=0.i++) for(int j=0. int ls=scadere(vel1. v1=new int[l].int m.i<ls.vl2.vls).i<n. k=0.// rezultati for(int i=0.vl2.int k.

//returneaza numarul de elemente nenule else return 0. delete [n]a.j++) //elementelor if(!a[i][j]) k++.i++)// parcurgerea vectorilor primei matrice { for(int j=k.i++) delete [m]a[i]. for(int i=0.int *&vcr.i<n. } //procedura de adunare a doua matrici rare memorate in vectori int adunare(float *vel1.//introducerea cin>>a[i][j]. a=new float*[n]. //alocarea for(int i=0.(vl1[i]>vl2[j])||((vl1[i]==vl2[j])&&(vc1[i]>vc2[j])).int n) { for(int i=0."<<j+1<<" : ". } } //procedura dealocare si incarcare de la tastatura a unei matrice void incarcare(float **&a.j<m.float *&velr.j++) { cout<<"Introduceti elementul:"<<i+1<<". vcr=new int[n1+n2].int m.l+ +.v2[k]=j.i<n1.//cu elementele nenule ale matricei k++.i<n.k++)//parcurgerea vectorilor { // celei de-a doua matrice //pana la egalarea rangului .i<n.k=0.int *&vlr) { int l=0.int *vl2.int n) { int k=0.int *vc1.int *vc2.i++) for(int j=0.j<m.int n1. } //procedura de dezalocare a unei matrice void distruge(float **&a.i++) //contorizarea for(int j=0. for(i=0. velr=new float[n1+n2].i<n. for(int i=0.int n2. vlr=new int[n1+n2].int n) { int k=0.j++.int *vl1.int m.int m. float a.i++) //matricei a[i]=new float[m]. //datelor de la tastatura } } //procedura de verificare a unei matrice daca este rara int ifrar(float **a. //vectorilor v3[k]=a[i][j]. //nule if(k>m*n*2/3) return m*n-k.float *vel2.

delete [k](vcr+l). l++.int n1. in continuare in vlr[l]=vl2[i]. /*delete [k](vlr+l). } k=n1+n2-l.int *&vlr) { float *vaux=new float[n2]. //liniei si coloanei elementului vlr[l]=vl2[j]. } k++. //primei matrice vcr[l]=vc2[j]. } } if(k!=n2) de-a doua matrice sunt elemente de for(i=k.i<n2.i++) vaux[i]=-vel2[i].int *vc2. } else { velr[l]=vel1[i].velr[l]=vel2[j]. matrice l++. .i<n2. } if((vl1[i]==vl2[j])&&(vc1[i]==vc2[j])) liniei si coloanei sunt { // aceleasi se aduna elementele a=vel1[i]+vel2[j]. l++. delete [k](velr+l).int *vc1. vlr[l]=vl2[j]. //returneaza lungimea vectorilor rezultati } //daca rangul //daca nu exista in cea de-a doua //pe linia si coloana // vectorii primei //daca in cea //rang al liniei sau //se vor memora aceste elemente //vectorii matricei //procedura de scadere a doua matrice rare memorate fiecare in cate trei vectori int scadere(float *vel1.int *vl2. matrice element nenul vlr[l]=vl1[i]. if(a) { velr[l]=a. //se foloseste un vector auxiliar in care se introduc //valor ile nenule cu semn schimbat din prima matrice for(int i=0.int n2.i++) coloanei mai mare decat al { //ultimului element din vectorii primei matrice velr[l]=vel2[i]. vcr[l]=vc2[j]. corespunzatoare elementului din vcr[l]=vc1[i].int *vl1.float *&velr.float *vel2.*/ return l. rezultate vcr[l]=vc2[i].int *&vcr.

m.vc1. a doua matrice int l2=ifrar(y.vel2).m.n).//se transforma a doua matrice //in trei vectori distruge(y.//se dezaloca a doua matrice for(int i=0.//se verifica daca a doua matrice este rara if(l2) { matrara(y.vl1.*vc1.*vl2.vl2.l1. la tastatura cin>>n.//se declara pointerii la cele doua matrice initiale //si la vectorii de elemente int n.*vc2.vl1.*vels.//se returneaza dimensiunea vectorilor rezultati } //*************************************************************************** ****************** void main () { float **x.int k=adunare(vel1.m.i<l1.velr.m.vel1).vaux.n. //numarul de linii si cout<<"Introduceti numarul de coloane:".n2.m.vlr).vc2.//se transforma prima matrice in trei vectori distruge(x.// se aloca si se initializeaza de la tastatura "<<vc1[i]<<" //se afiseaza cei trei .vc2.l2.i<l2.vl2.n).*vl1.//se declara dimansiunile matricelor //si pointerii la vetorii de ranguri ale liniilor si cocloanelor cout<<"Introduceti numarul de linii:".m.n).vcr.*vela.*vel2.*vls.*vla.m.n).**y.*vel1. cin>>m.// se aloca si se initializeaza de la tastatura prima matrice int l1=ifrar(x.//de-a doua matrice "<<vc2[i]<<" //primei matrice incarcare(y.n).//se face scaderea prin //adunare: a-b=a+(-b) delete [n2]vaux.*vcs.m.n).//se dezaloca vectorul auxiliar return k.i++)//se afiseaza cei trei vectori corespunzatori celei cout<<"\n"<<vl2[i]<<" "<<vel2[i].m.i++) vectori corespunzatori cout<<"\n"<<vl1[i]<<" "<<vel1[i]. //se dezaloca prima matrice for(int i=0.n1.vc1.n.*vca. // de coloane //se introduc de incarcare(x.//se verifica daca prima matrice este rara if(l1) { matrara(x.

i<ls.vela.i++) //se afiseaza vectorii rezultati cout<<"\n"<<vla[i]<<" "<<vca[i]<<" "<<vela[i].//se aduna //cele doua matrice rezultand tot trei vectori in care va //fi memorata if(!(la<m*n/3))cout<<"Matricea suma nu este rara ".vca.vc1.l2.vls).vl1.vl2.vel2.//se verifica daca //matricea rezultata este rara cout<<"\n".//se verifica daca //matricea rezultata este rara cout<<"\n".vla).vels.l2.vc1.vcs. int ls=scadere(vel1. for( i=0.vc2. for( i=0.vc2. } .//se scad //cele doua matrice rezultand tot trei vectori in care va //fi memorata if(!(ls<m*n/3))cout<<"Matricea diferenta nu este rara ".vl2.vl1. } else cout<<"Nu este matrice rara!!!".i++) //se afiseaza vectorii rezultati cout<<"\n"<<vls[i]<<" "<<vcs[i]<<" "<<vels[i].i<la.l1.vel2. } else cout<<"Nu este matrice rara!!!".int la=adunare(vel1.l1.

Sign up to vote on this title
UsefulNot useful