În domeniul inteligenței artificiale se folosesc în mare măsură operații liniare pe matrice. Ca să puneți bazele unui framework de ML (Machine Learning) aveți nevoie să implementați o clasă care să stocheze o matrice de dimensiune variabilă, cu un anumit timp de valori.
Cerință
Să se creeze clasa Mat, implementând conținutul acesteia astfel:
Clasa trebuie să conțină 2 atribute private numite mCols și mRows. Acestea sunt dimensiunile matricei, iar tipul lor este număr întreg fără semn pe 16 biți.
Clasa trebuie să conțină 1 atribut privat numit mData. Acesta este un pointer la numere fracționare pe dublă precizie, iar memoria pentru acest pointer va fi alocată în constructor.
Un constructor ce ia ca argument două numere întregi fără semn ce reprezintă numărul de linii și numărul de coloane a unei matrice și să aloce memorie pentru a stoca aceste valori.
Un al doilea constructor care să ia ca argument un singur număr. În acest caz se consideră matricea un vector, numărul de linii va fi implicit 1 iar argumentul primit reprezintă numărul de coloane. Desigur, alocarea de memorie pentru a putea stoca numere trebuie făcută și în acest caz.
Un al treilea constructor care să nu ia niciun argument. Acesta doar va inițializa atributele întregi cu valoarea 0.
Un al patrulea constructor, care va fi constructorul de copiere. Acesta primește ca argument un obiect de tip const Mat & și rolul lui este de a copia toate valorile atributelor obiectului dat ca parametru în obiectul curent (this).
Operatorul binar + care să verifice dacă al doilea operand are aceleași dimensiuni cu matricea voastră și să adune cele două matrice element cu element. Dacă nu se pot aduna, operatorul va întoarce un Mat cu 0 linii și 0 coloane. Acest operator va trebui să întoarcă un obiect de tip Mat și ca argument va trebui să ia un obiect de tip const Mat &.
Operatorul binar * care să verifice dacă al doilea operand are aceleași dimensiuni cu matricea voastră și să înmulțească cele două matrice element cu element. Dacă nu se pot înmulți, operatorul va întoarce un Mat cu 0 linii și 0 coloane. Acest operator va trebui să întoarcă un obiect de tip Mat și ca argument va trebui să ia un obiect de tip const Mat &.
Operatorul binar ^ care să verifice constrângerile necesare pentru al doilea operand și să înmulțească cele două matrice (linie cu coloană). Dacă nu se pot înmulți, operatorul va întoarce un Mat cu 0 linii și 0 coloane. Acest operator va trebui să întoarcă un obiect de tip Mat și ca argument va trebui să ia un obiect de tip const Mat &.
O metodă de tip getter numită getValue care să ia ca argumente două valori întregi fără semn ce reprezintă linia și coloana valorii din matrice ce trebuie întoarsă. Tipul de dată returnat trebuie să îl deduceți voi.
O metodă de tip setter numită setValue care să ia ca argumente două valori întregi fără semn ce reprezintă linia și coloana valorii din matrice ce trebuie întoarsă și o valoare ce trebuie plasată în matrice pe poziția respectivă. Tipul de dată returnat trebuie să îl deduceți voi.
Destructorul care să elibereze memoria alocată de constructor.
În funcția main se vor citi de la tastatură două matrice M și N. Dacă dimensiunile lor sunt egale, se va calcula matricea R = M * N + N și se va afișa pe ecran media elementelor din R cu două zecimale. Dacă matricele pot fi înmulțite linie cu coloană (prima cu a doua), se va realiza înmulțirea R = M ^ N și se va afișa tot media lui R cu două zecimale.
Date de intrare
Pe prima linie se află 4 valori întregi fără semn: linii_M coloane_M linii_N coloane_N
Pe următoarele linii_M linii se află coloane_M valori fracționare ce reprezintă valorile lui M.
Pe ultimele linii_N linii se află coloane_N valori fracționare reprezentând valorile lui N.
Date de ieșire
Se va afișa un singur număr cu două zecimale reprezentând media lui R calculată conform cerinței.
Observații
Alocați memorie dinamic pentru pentru mData, folosind operatorul new. Cel mai simplu (și eficient) este să alocați un array de (linii * coloane) locații și să accesați mereu locația din vector corespunzătoare liniei și coloanei unde vreți să accesați matricea. Numerotați celulele din matrice pornind cu celula din dreapta sus ca având indexul 0 și cea din dreapta jos ca având indexul (linii * coloane).
Răspunsuri la întrebare
Mat.h
#pragma once
#include <iostream>
using std::cout, std::cin, std::endl;
class Mat
{
private:
double **mData;
unsigned short mRows, mCols; // unsigned short <=> întreg fără semn pe 16 biți
public:
Mat(unsigned short, unsigned short);
Mat(unsigned short);
Mat();
Mat(const Mat &);
Mat operator+(const Mat &);
Mat operator*(const Mat &);
Mat operator^(const Mat &);
double getValue(unsigned short, unsigned short) const;
void setValue(unsigned short, unsigned short, double);
unsigned short getRows() const;
unsigned short getCols() const;
~Mat();
friend std::ostream &operator<<(std::ostream &COUT, Mat &matObj);
};
Explicație:
Ai cei 4 constructori ( 3 + ăla de copiere ) din enunț, primul care ia ca parametrii numărul de linii și coloane și ne alocă dinamic un tablou bidimensional cu valorile respective.
Al doilea care îți ia doar numărul de coloane și alocă o matrice cu o linie și N coloane.
Al treilea care efectiv îți crează o matrice goală ( 0 linii, 0 coloane ).
Și ăla de copiere.
Ai supraîncărcarea fiecărui operator (+, * și ^)
- pentru + verifică dacă cele 2 matrice au același număr de linii și coloane, dacă e îndeplinită condiția adună element cu element și salvează totul într-o matrice nouă pe care o și returnează
- pentru * e aceași idee doar că în loc să le adune le înmulțește ( dacă nu se îndeplinește condiția la niciuna dintre aceste 2 operații atunci returnăm o matrice goală ).
- pentru ^ verifică dacă numărul de linii din prima matrice este egal cu numărul de coloane din a doua. Dacă nu e îndeplinită condiția returnează o matrice goală, dacă e îndeplinită face calculele și le salvează într-o matrice nouă pe care o și returnează.
Ți-am supraîncărcat și operatorul << ca să poți vedea matricele.
Mai ai și niște gettere și settere și un destructor care îți dealocă memoria alocată-n constructori.