Advertisement
Guest User

Untitled

a guest
Jul 22nd, 2018
465
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 15.70 KB | None | 0 0
  1. #include <iostream>
  2. #include <vector>
  3. #include <cmath>
  4. #include <string>
  5. #include <iomanip>
  6.  
  7. using std::cout;
  8. using std::cin;
  9. using std::endl;
  10.  
  11. class Matrix {
  12. public:
  13.     friend Matrix addMatrices(const Matrix&, const Matrix&);
  14.     friend Matrix subtractMatrices(const Matrix&, const Matrix&);
  15.     friend Matrix multiplyMatrices(const Matrix&, const Matrix&);
  16.  
  17.     Matrix() = default;
  18.     //constructs zero matrix of size rows x columns
  19.     Matrix(size_t rows, size_t columns) : matrix(std::vector<std::vector<double>>(rows, std::vector<double>(columns))) { }
  20.     virtual ~Matrix() = default;
  21.  
  22.     std::ostream& print(std::ostream&) const;
  23.     std::istream& modifyMatrixByInput(std::istream&);
  24.     size_t getRows() const;
  25.     size_t getColumns() const;
  26.     void setMatrixValue(size_t, size_t, double);
  27.     double getMatrixValue(size_t, size_t) const;
  28.     Matrix scalarMultiplication(double) const;
  29.     Matrix transpose() const;
  30.     Matrix findEchelonForm() const;
  31.     Matrix findReducedRowEchelonForm() const;
  32.  
  33. protected:
  34.     //Echelon Form helper functions
  35.     void swapRows(size_t, size_t);
  36.     void multiplyRowByCoefficient(size_t, double);
  37.     size_t findLeftmostNonzeroColumnInSubmatrix(size_t) const;
  38.     size_t findFirstNonzeroRowInColumnInSubmatrixFromTop(size_t, size_t) const;
  39.     void addRowTimesCoefficientToRow(size_t, size_t, double);
  40.     //Reduced Row Echelon Form helper functions
  41.     size_t findColumnOfLeadingOneInRow(size_t) const;
  42.     bool isNonzeroRow(size_t) const;
  43.     size_t findFirstNonzeroRowFromBottom() const;
  44.  
  45.     std::vector<std::vector<double>> matrix;
  46. };
  47.  
  48. class SquareMatrix : public Matrix {
  49. public:
  50.     SquareMatrix() = default;
  51.     SquareMatrix(size_t rs) : Matrix(rs, rs) {}
  52.     SquareMatrix(Matrix sqrM) : Matrix(sqrM) {}
  53.  
  54.     double trace() const;
  55.     double findDeterminant() const;
  56.     SquareMatrix findInverse() const;
  57.  
  58. private:
  59.     //Inverse helper function
  60.     Matrix addIdentityToRightSide() const;
  61.     //Determinant helper function
  62.     SquareMatrix squareMatrixCutter(size_t, size_t) const;
  63. };
  64.  
  65. std::ostream& Matrix::print(std::ostream& os) const {
  66.     for (auto & rows : matrix) {
  67.         for (auto & el : rows) {
  68.             os << std::fixed << std::setprecision(3) << el << "\t\t";
  69.         }
  70.         os << endl;
  71.     }
  72.     return os;
  73. }
  74. //for user input of matrix, as constructor makes zero matrices only
  75. std::istream& Matrix::modifyMatrixByInput(std::istream& is) {
  76.     for (auto & rows : matrix) {
  77.         for (auto & el : rows) {
  78.             is >> el;
  79.         }
  80.     }
  81.     return is;
  82. }
  83.  
  84. size_t Matrix::getRows() const {
  85.     return matrix.size();
  86. }
  87.  
  88. size_t Matrix::getColumns() const {
  89.     if (matrix.size() == 0) {
  90.         return 0;
  91.     }
  92.     else {
  93.         return (matrix[0]).size();
  94.     }
  95. }
  96.  
  97. void Matrix::setMatrixValue(size_t row, size_t column, double value) {
  98.     matrix[row][column] = value;
  99. }
  100.  
  101. double Matrix::getMatrixValue(size_t row, size_t column) const {
  102.     return matrix[row][column];
  103. }
  104.  
  105.  
  106.  
  107. Matrix Matrix::scalarMultiplication(double scalar) const {
  108.     Matrix result(*this);
  109.     for (auto & row : result.matrix) {
  110.         for (auto & el : row) {
  111.             el *= scalar;
  112.         }
  113.     }
  114.     return result;
  115. }
  116.  
  117.  
  118. Matrix addMatrices(const Matrix& mat1, const Matrix& mat2) {
  119.     Matrix result(mat1.getRows(), mat1.getColumns());
  120.     for (size_t i = 0; i < mat1.getRows(); ++i) {
  121.         for (size_t j = 0; j < mat1.getColumns(); ++j) {
  122.             result.setMatrixValue(i, j, (mat1.getMatrixValue(i, j) + mat2.getMatrixValue(i, j)));
  123.         }
  124.     }
  125.     return result;
  126. }
  127.  
  128. Matrix subtractMatrices(const Matrix& mat1, const Matrix& mat2) {
  129.     //uses addMatrices and scalarMultiplication of RHS with -1 for same effect
  130.     Matrix rhs = mat2.scalarMultiplication(-1);
  131.     Matrix result = addMatrices(mat1, rhs);
  132.     return result;
  133. }
  134.  
  135. Matrix multiplyMatrices(const Matrix& mat1, const Matrix& mat2) {
  136.     //because result has same number of rows as first, and columns as second
  137.     Matrix result(mat1.getRows(), mat2.getColumns());
  138.     for (size_t i = 0; i < mat1.getRows(); ++i) {
  139.         for (size_t j = 0; j < mat2.getColumns(); ++j) {
  140.             double tempSum = 0;
  141.             for (size_t k = 0; k < mat1.getColumns(); ++k) {
  142.                 tempSum += (mat1.getMatrixValue(i, k) * mat2.getMatrixValue(k, j));
  143.             }
  144.             result.setMatrixValue(i, j, tempSum);
  145.         }
  146.     }
  147.     return result;
  148. }
  149.  
  150. //sums diagonal
  151. double SquareMatrix::trace() const {
  152.     double result = 0;
  153.     for (size_t i = 0; i < this->getRows(); ++i) {
  154.         result += this->getMatrixValue(i, i);
  155.     }
  156.     return result;
  157. }
  158.  
  159. //extracts the submatrix by excluding the given row and column
  160. SquareMatrix SquareMatrix::squareMatrixCutter(size_t rowpos, size_t colpos) const {
  161.     //size will be current-1 due to exclusion
  162.     SquareMatrix result((this->getRows()) - 1);
  163.     //Indices of result are kept seperate from indices of source matrix (i and j) to search and fill correctly
  164.     size_t resultRowIndex = 0;
  165.  
  166.     for (size_t i = 0; i < (this->getRows()); ++i) {
  167.         if (i != rowpos) {
  168.             size_t resultColumnIndex = 0;
  169.             for (size_t j = 0; j < (this->getRows()); ++j) {
  170.                 if (j != colpos) {
  171.                     result.setMatrixValue(resultRowIndex, resultColumnIndex, this->getMatrixValue(i, j));
  172.                     ++resultColumnIndex;
  173.                 }
  174.             }
  175.             ++resultRowIndex;
  176.         }
  177.     }
  178.     return result;
  179. }
  180.  
  181. double SquareMatrix::findDeterminant() const {
  182.     if ((this->getRows()) == 2) {
  183.         //ad-bc for trivial 2x2 matrix
  184.         double result = (((this->getMatrixValue(0,0)) * (this->getMatrixValue(1,1))) - ((this->getMatrixValue(0, 1)) * (this->getMatrixValue(1, 0))));
  185.         return result;
  186.     }
  187.     else {
  188.         double finalResult = 0;
  189.         //arbitrarily chose first row, and finding sum of cofactors using it.
  190.         for (size_t j = 0; j < (this->getRows()); ++j) {
  191.             int cofactorCoefficient = 1;
  192.             //since first row is set arbitrarily, can deduce that odd index columns have negative coefficient
  193.             if (j % 2 != 0) {
  194.                 cofactorCoefficient = -1;
  195.             }
  196.             SquareMatrix cutMatrix = this->squareMatrixCutter(0, j);
  197.             finalResult += (cutMatrix.findDeterminant())*(this->getMatrixValue(0, j))*cofactorCoefficient;
  198.         }
  199.         return finalResult;
  200.     }
  201. }
  202.  
  203. Matrix Matrix::transpose() const {
  204.     Matrix result((this->getColumns()), (this->getRows()));
  205.     for (size_t i = 0; i < (this->getRows()); ++i) {
  206.         for (size_t j = 0; j < (this->getColumns()); ++j) {
  207.             result.setMatrixValue(j, i, (this->getMatrixValue(i, j)));
  208.         }
  209.     }
  210.     return result;
  211. }
  212.  
  213. void Matrix::swapRows(size_t row1, size_t row2) {
  214.     std::vector<double> temp = matrix[row1];
  215.     matrix[row1] = matrix[row2];
  216.     matrix[row2] = temp;
  217. }
  218. //for EF and RREF usage
  219. void Matrix::multiplyRowByCoefficient(size_t row, double coefficient) {
  220.     for (auto & el : matrix[row]) {
  221.         el *= coefficient;
  222.     }
  223. }
  224. //to avoid floating point arithmetic errors, -10 chosen arbitrarily
  225. bool isZero(double num) {
  226.     if (abs(num) < exp(-10)) {
  227.         return true;
  228.     }
  229.     else {
  230.         return false;
  231.     }
  232. }
  233. //for EF and RREF usage
  234. size_t Matrix::findLeftmostNonzeroColumnInSubmatrix(size_t upperLim) const {
  235.     for (size_t j = 0; j < (this->getColumns()); ++j) {
  236.         for (size_t i = upperLim; i < (this->getRows()); ++i) {
  237.             if (!isZero(this->getMatrixValue(i, j))) {
  238.                 return j;
  239.             }
  240.         }
  241.     }
  242.     //implies a full zero submatrix;
  243.     return (this->getColumns());
  244. }
  245. //for EF and RREF usage
  246. size_t Matrix::findFirstNonzeroRowInColumnInSubmatrixFromTop(size_t upperLim, size_t colNum) const {
  247.     for (size_t i = upperLim; i < (this->getRows()); ++i) {
  248.         if (!isZero(this->getMatrixValue(i, colNum))) {
  249.             return i;
  250.         }
  251.     }
  252.     //implies a zero column.
  253.     return (this->getRows());
  254. }
  255. //for EF and RREF usage
  256. void Matrix::addRowTimesCoefficientToRow(size_t sourceRow, size_t destinationRow, double coefficient) {
  257.     for (size_t j = 0; j < (this->getColumns()); ++j) {
  258.         this->setMatrixValue(destinationRow, j, ((this->getMatrixValue(destinationRow,j)) + ((this->getMatrixValue(sourceRow,j))*coefficient)));
  259.     }
  260. }
  261.  
  262. Matrix Matrix::findEchelonForm() const {
  263.     Matrix result(*this);
  264.     //moving from top to bottom
  265.     size_t upperLim = 0;
  266.     while (upperLim != (this->getRows())) {
  267.         size_t currColumn = result.findLeftmostNonzeroColumnInSubmatrix(upperLim);
  268.         //if currColumn was matrix's columns, it implies a full zero submatrix (from that method's definition), so do not calculate
  269.         if (currColumn != result.getColumns()) {
  270.             //swaps to ensure pivot is non-zero. may swap with self.
  271.             size_t firstNonZeroRow = result.findFirstNonzeroRowInColumnInSubmatrixFromTop(upperLim, currColumn);
  272.             result.swapRows(upperLim, firstNonZeroRow);
  273.             //sets pivot to 1
  274.             result.multiplyRowByCoefficient(upperLim, (1.0 / (result.getMatrixValue(upperLim,currColumn))));
  275.             //sets cells below pivot to 0
  276.             for (size_t i = upperLim + 1; i < (this->getRows()); ++i) {
  277.                 result.addRowTimesCoefficientToRow(upperLim, i, (-1.0)*(result.getMatrixValue(i,currColumn)));
  278.             }
  279.         }
  280.         ++upperLim;
  281.     }
  282.     return result;
  283. }
  284.  
  285. //for RREF usage
  286. size_t Matrix::findColumnOfLeadingOneInRow(size_t row) const {
  287.     for (size_t j = 0; j < (this->getColumns()); ++j) {
  288.         //checks if value == 1. floating point arithmetic errors workaround.
  289.         if (isZero((this->getMatrixValue(row, j))-1.0)) {
  290.             return j;
  291.         }
  292.     }
  293.     //implies could not find a leading one.
  294.     return (this->getColumns());
  295. }
  296.  
  297. //for RREF usage
  298. bool Matrix::isNonzeroRow(size_t row) const {
  299.     for (size_t j = 0; j < (this->getColumns()); ++j) {
  300.         if (!isZero(this->getMatrixValue(row, j))) {
  301.             return true;
  302.         }
  303.     }
  304.     return false;
  305. }
  306.  
  307. //assuming non-zero matrix
  308. size_t Matrix::findFirstNonzeroRowFromBottom() const {
  309.     for (size_t i = (this->getRows()) - 1; i >= 0; --i) {
  310.         //need not check if top row reached since assuming non-zero matrix
  311.         if ((i == 0)||(this->isNonzeroRow(i))) {
  312.             return i;
  313.         }
  314.     }
  315.     return 0;
  316. }
  317.  
  318.  
  319. Matrix Matrix::findReducedRowEchelonForm() const {
  320.     //first send to Echelon form
  321.     Matrix result = this->findEchelonForm();
  322.     //then work from bottom to top, starting at first pivot
  323.     for (size_t lowerLim = result.findFirstNonzeroRowFromBottom(); lowerLim > 0; --lowerLim) {
  324.  
  325.         size_t currColumn = result.findColumnOfLeadingOneInRow(lowerLim);
  326.         for (size_t i = lowerLim-1; i >= 0; --i) {
  327.             //set cells above pivot to 0
  328.             result.addRowTimesCoefficientToRow(lowerLim, i, (-1.0)*(result.getMatrixValue(i,currColumn)));
  329.             //to avoid decrementing a size_t below 0
  330.             if (i == 0) {
  331.                 break;
  332.             }
  333.         }
  334.        
  335.     }
  336.     return result;
  337. }
  338.  
  339. //for inverse usage
  340. SquareMatrix buildIdentity(size_t n) {
  341.     SquareMatrix result(n);
  342.     for (size_t i = 0; i < n; ++i) {
  343.         result.setMatrixValue(i, i, 1);
  344.     }
  345.     return result;
  346. }
  347. //for inverse usage
  348. Matrix SquareMatrix::addIdentityToRightSide() const {
  349.     Matrix result((this->getRows()), (this->getRows()) * 2);
  350.     SquareMatrix tempIdentity(buildIdentity((this->getRows())));
  351.     for (size_t i = 0; i < (this->getRows()); ++i) {
  352.         for (size_t j = 0; j < (this->getRows()); ++j) {
  353.             result.setMatrixValue(i,j,this->getMatrixValue(i,j));
  354.         }
  355.         for (size_t j = (this->getRows()); j < (this->getRows()) *2; ++j) {
  356.             result.setMatrixValue(i, j, tempIdentity.getMatrixValue(i, j- (this->getRows())));
  357.         }
  358.     }
  359.     return result;
  360. }
  361. //for inverse usage
  362. SquareMatrix extractRightSideSquareMatrix(const Matrix& mat1)  {
  363.     SquareMatrix result(mat1.getRows());
  364.     for (size_t i = 0; i < mat1.getRows(); ++i) {
  365.         for (size_t j = 0; j < mat1.getRows(); ++j) {
  366.             result.setMatrixValue(i, j, mat1.getMatrixValue(i, j+mat1.getRows()));
  367.         }
  368.     }
  369.     return result;
  370. }
  371.  
  372. SquareMatrix SquareMatrix::findInverse() const {
  373.     //adds identity to right side of matrix. performs RREF. extracts inverse from right side.
  374.     Matrix matrixWithIdentity = this->addIdentityToRightSide();
  375.     Matrix solvedReducedRowEchelonForm = matrixWithIdentity.findReducedRowEchelonForm();
  376.     SquareMatrix result = extractRightSideSquareMatrix(solvedReducedRowEchelonForm);
  377.     return result;
  378. }
  379. //menu system. true indicates continuation. false indicates exit.
  380. bool matrixCalculatorTurn() {
  381.     cout << "Choose option: \n"
  382.         << "a) Addition\n"
  383.         << "b) Subtraction\n"
  384.         << "c) Multiplication\n"
  385.         << "d) Scalar multiplication\n"
  386.         << "e) Echelon form\n"
  387.         << "f) Reduced row echelon form\n"
  388.         << "g) Transpose\n"
  389.         << "h) Trace\n"
  390.         << "i) Determinant\n"
  391.         << "j) Inverse\n"
  392.         << "k) EXIT\n" << endl;
  393.     cout << "Select: ";
  394.     char selection;
  395.     cin >> selection;
  396.     if (selection == 'k') {
  397.         cout << "Exiting calculator.\n";
  398.         return false;
  399.     }
  400.     //addition, subtraction, or multiplication (binary operations)
  401.     else if (selection == 'a' || selection == 'b' || selection == 'c') {
  402.         size_t mat1Rows, mat1Columns, mat2Rows, mat2Columns;
  403.         cout << "[Matrix #1] No. of Rows: ";
  404.         cin >> mat1Rows;
  405.         cout << "[Matrix #1] No. of Columns: ";
  406.         cin >> mat1Columns;
  407.         cout << "[Matrix #2] No. of Rows: ";
  408.         cin >> mat2Rows;
  409.         cout << "[Matrix #2] No. of Columns: ";
  410.         cin >> mat2Columns;
  411.         //multiplication validity check
  412.         if (selection == 'c') {
  413.             if (!(mat1Columns == mat2Rows)) {
  414.                 cout << "Error, no. of columns of Matrix #1 and no. of rows of Matrix #2 should match." << endl;
  415.                 return true;
  416.             }
  417.         }
  418.         //addition/subtraction validity check
  419.         else if (!((mat1Rows == mat2Rows) && (mat1Columns == mat2Columns))) {
  420.             cout << "Error, matrices are of different dimensions." << endl;
  421.             return true;
  422.         }
  423.         Matrix mat1(mat1Rows,mat1Columns), mat2(mat2Rows,mat2Columns);
  424.         cout << "Enter Matrix #1 (" << mat1Rows << "x" << mat1Columns << "):\n";
  425.         mat1.modifyMatrixByInput(cin);
  426.         cout << "Enter Matrix #2 (" << mat2Rows << "x" << mat2Columns << "):\n";
  427.         mat2.modifyMatrixByInput(cin);
  428.         Matrix result;
  429.         if (selection == 'a') {
  430.             result = addMatrices(mat1, mat2);
  431.             cout << "Matrix #1 + Matrix #2 = \n";
  432.         }
  433.         else if ((selection == 'b')) {
  434.             result = subtractMatrices(mat1, mat2);
  435.             cout << "Matrix #1 - Matrix #2 = \n";
  436.         }
  437.         else {
  438.             result = multiplyMatrices(mat1, mat2);
  439.             cout << "Matrix #1 * Matrix #2 = \n";
  440.         }
  441.         result.print(cout);
  442.         cout << endl;
  443.         return true;
  444.     }
  445.     //scalar, echelon form, reduced row echelon form, transpose (unary)
  446.     else if (selection == 'd' || selection == 'e' || selection == 'f' || selection == 'g') {
  447.         size_t matRows, matColumns;
  448.         cout << "[Matrix] No. of Rows: ";
  449.         cin >> matRows;
  450.         cout << "[Matrix] No. of Columns: ";
  451.         cin >> matColumns;
  452.         double scalar;
  453.         if (selection == 'd') {
  454.             cout << "Scalar: ";
  455.             cin >> scalar;
  456.         }
  457.         Matrix mat(matRows, matColumns);
  458.         cout << "Enter Matrix (" << matRows << "x" << matColumns << "):\n";
  459.         mat.modifyMatrixByInput(cin);
  460.         Matrix result;
  461.         if (selection == 'd') {
  462.             result = mat.scalarMultiplication(scalar);
  463.             cout << scalar << " * Matrix = \n";
  464.         }
  465.         else if (selection == 'e') {
  466.             result = mat.findEchelonForm();
  467.             cout << "Echelon form = \n";
  468.         }
  469.         else if (selection == 'f') {
  470.             result = mat.findReducedRowEchelonForm();
  471.             cout << "Reduced row echelon form = \n";
  472.         }
  473.         else {
  474.             result = mat.transpose();
  475.             cout << "Transpose = \n";
  476.         }
  477.         result.print(cout);
  478.         cout << endl;
  479.         return true;
  480.     }
  481.     // trace, determinant, inverse (square matrix operations)
  482.     else if (selection == 'h' || selection == 'i' || selection == 'j') {
  483.         size_t dim;
  484.         cout << "[Matrix] Dimension: ";
  485.         cin >> dim;
  486.         SquareMatrix mat(dim);
  487.         cout << "Enter Matrix (" << dim << "x" << dim << "):\n";
  488.         mat.modifyMatrixByInput(cin);
  489.         if (selection == 'h') {
  490.             double result = mat.trace();
  491.             cout << "Trace = " << result << endl;
  492.             return true;
  493.         }
  494.         else if (selection == 'i') {
  495.             double result = mat.findDeterminant();
  496.             cout << "Determinant = " << result << endl;
  497.             return true;
  498.         }
  499.         else if (selection == 'j') {
  500.             double determinantTest = mat.findDeterminant();
  501.             if (determinantTest != 0) {
  502.                 SquareMatrix result = mat.findInverse();
  503.                 cout << "Inverse =\n";
  504.                 result.print(cout);
  505.                 cout << endl;
  506.                 return true;
  507.             }
  508.             else {
  509.                 cout << "Matrix is non-invertible." << endl;
  510.                 return true;
  511.             }
  512.         }
  513.     }
  514.     else {
  515.         cout << "Invalid selection." << endl;
  516.         return true;
  517.     }
  518.  
  519. }
  520.  
  521. int main() {   
  522.     while (matrixCalculatorTurn()) {
  523.         cout << "Continue? [Y/N]: ";
  524.         char continueChoice;
  525.         cin >> continueChoice;
  526.         if (continueChoice == 'N') {
  527.             break;
  528.         }
  529.     }
  530. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement