I got a homework which looks something like this. I am working on it and hold beginner knowledge of the same.
I have to create a header file that contains this code and then use the header file to get the desired results.
I am working with C++ template, and operator overloading.
#include <iostream>
#include <memory>
#include "matrix.h"
#include "symetric_matrix.h"
using namespace std;
int main()
{
const Matrix<int, 3, 2> m1; // Creates 3*2 matrix, with all the default elements set to 0;
cout << m1 << endl;
Matrix<int, 3, 3> m2(4); // Creates 3*3 matrix, with the default elements equals to 4;
cout << m2 << endl;
const Matrix<int, 3, 3> m3 = m2; // C-py constructor may take O(MN) and not O(1).
cout << m3 << endl;
// min() returns the minimal value in the matrix.
if (min(m1) < min(m3))
cout << "Min value of m3(" << min(m3) << ") is bigger than min value of m1(" << min(m1) << ")" << endl;
if (m1.avg() < m3.avg()) // Compares the average of the elements
cout << "Average value of m3(" << m3.avg() << ") is bigger than average value of m1(" << m1.avg() << ")" << endl;
m2(0, 0) = 13;
cout << m2[0][0] << " " << m2[1][0] << endl; // Should print "13 4"
try
{
cout << m2 + m3 << endl;
cout << m3 * m1 << endl; // You can choose the format of matrix printing;
cout << m1 * m2; // This should throw an exception
}
catch (const Matrix<int, 3, 2>::IllegalOperation &e)
{
cout << e.what() << endl;
}
Matrix<int, 3, 3> m4;
m4 = m3;
cout << m4 << endl;
for (int i = 0; i < 3; ++i)
for (int j = 0; j < 3; ++j)
m4(i, j) = i + j;
cout << m4 << endl;
cout << "m4[1][1] = " << m4[1][1] << endl;
cout << "m4[1][1] = " << m4(1, 1) << endl; // m4(1,1) same result as m4[1][1]
Matrix<int, 3, 3> m5(3);
m5 = 2 * m4;
cout << m5 << endl;
Matrix<int, 3, 3> m6(m4);
cout << m6 << endl;
m5 += m4;
cout << m5 << endl;
if (m6 != m5)
cout << "m6 != m5" << endl;
Matrix<Matrix<int, 3, 2>, 4, 4> composite(m1); // Creates matrix, where each element is m1;
cout << composite;
unique_ptr<Matrix<int, 3, 3>> symetric_matrix(new SymetricMatrix<int, 3>(5)); // SymetricMatrix matrix 3*3 with default element equals to 5;
(*symetric_matrix)(1, 2) = 8;
cout << (*symetric_matrix)(1, 2) << " " << (*symetric_matrix)(2, 1) << endl; // Should print "8 8"
cout << (*symetric_matrix)[1][2] << " " << (*symetric_matrix)[2][1] << endl; // Should print "8 8"
(*symetric_matrix)[1][0] = 18;
cout << (*symetric_matrix)[1][0] << " " << (*symetric_matrix)[0][1] << endl; // Should print "18 18"
return 0;
}
My Updated solution for now.
template <class T, int M, int N>
class Matrix
{
private:
T mat[M][N];
int rows = M;
int cols = N;
public:
// constructor
Matrix(int v = 0)
{
for (int i = 0; i < M; i++)
{
for (int j = 0; j < N; j++)
mat[i][j] = v;
}
}
T &operator()(int i, int j)
{
return mat[i][j];
};
T *operator[](int index)
{
return mat[index];
};
// << overloading
friend std::ostream &operator<<(std::ostream &os, const Matrix<T, M, N> &L)
{
for (int i = 0; i < M; i++)
{
for (int j = 0; j < N; j++)
os << L.mat[i][j] << " ";
os << "\n";
}
return os;
};
template <class T1, int M1, int N1>
Matrix<T, M, M> operator*(Matrix<T1, M1, N1> const &other);
Matrix<T, M, M> operator+(Matrix<T, M, N> const &other);
friend T min(Matrix obj)
{
T result = obj.mat[0][0];
for (int i = 0; i < M; i++)
{
for (int j = 0; j < N; j++)
if (result < obj.mat[i][j])
result = obj.mat[i][j];
}
return result;
};
long double avg() const
{
long double result = 0;
for (int i = 0; i < M; i++)
{
for (int j = 0; j < N; j++)
if (result < mat[i][j])
result = result + mat[i][j];
}
return result / (M * N);
}
};
template <class T, int M, int N>
Matrix<T, M, M> Matrix<T, M, N>::operator+(Matrix const &other)
{
if ((this->rows == other.rows) && (this->cols == other.cols))
{
Matrix<T, M, N> resultantMatrix;
for (auto i = 0; i < this->rows; i++)
{
for (auto j = 0; j < this->cols; j++)
{
auto &valueFirst = this->mat[i][j];
auto &valueSecond = other.mat[i][j];
// if ((additionOverflow(valueFirst, valueSecond)) || (additionUnderflow(valueFirst, valueSecond)))
// throw std::out_of_range("Resultant value of matrix is out of range");
// else
resultantMatrix(i, j) = valueFirst + valueSecond;
}
}
return resultantMatrix;
}
else
throw std::runtime_error("Matrices cannot be added, sizes do not match");
}
template <class T, int M, int N>
template <class T1, int M1, int N1>
Matrix<T, M, M> Matrix<T, M, N>::operator*(Matrix<T1, M1, N1> const &other)
{
if ((this->rows == other.rows) && (this->cols == other.cols))
{
Matrix<T, M, N> resultantMatrix;
for (auto i = 0; i < this->rows; i++)
{
for (auto j = 0; j < this->cols; j++)
{
for (auto k = 0; k < this->cols; k++)
{
auto &valueFirst = this->mat[i][k];
auto &valueSecond = other(k, j);
// if ((additionOverflow(valueFirst, valueSecond)) || (additionUnderflow(valueFirst, valueSecond)))
// throw std::out_of_range("Resultant value of matrix is out of range");
// else
resultantMatrix(i, j) += valueFirst * valueSecond;
}
}
}
return resultantMatrix;
}
else
throw std::runtime_error("Matrices cannot be added, sizes do not match");
}
I am getting an error and I don't understand why.
error: no match for call to ‘(const Matrix<int, 3, 2>) (int&, int&)’
112 | auto &valueSecond = other(k, j);
| ~~~~~^~~~~~
matrix.h:20:8: note: candidate: ‘T& Matrix<T, M, N>::operator()(int, int) [with T = int; int M = 3; int N = 2]’ (near match)
20 | T &operator()(int i, int j)
| ^~~~~~~~
matrix.h:20:8: note: passing ‘const Matrix<int, 3, 2>*’ as ‘this’ argument discards qualifiers
It only happens at this line auto &valueSecond = other(k, j); and not this one resultantMatrix(i, j) += valueFirst * valueSecond;, Why?
I just need help with the whole program while I try and learn!
Any help is appreciated.
As for the error message
matrix.h:20:8: note: passing ‘const Matrix<int, 3, 2>*’ as ‘this’ argument discards qualifiers
other is const Matrix<int, 3, 2>&. The T &operator()(int i, int j) is implemented for non const Matrix<int, 3, 2>&. There should be provided two versions of the operators
T &operator()(int i, int j)
{
return mat[i][j];
};
const T &operator()(int i, int j) const
{
return mat[i][j];
};
T *operator[](int index)
{
return mat[index];
};
const T *operator[](int index) const
{
return mat[index];
};
Using operator() as a subscript operator may be confusing. It should be
T &operator[](int i, int j)
{
return mat[i][j];
};
const T &operator[](int i, int j) const
{
return mat[i][j];
};
Subscript operators usually are defined for size_t types.
Related
For the past few hours I have been trying to build a C++ module that, upon requesting the input of the user on the size and contents of 2 matrices (restricted to ones that can be multiplied together), then proceeds to multiply them together and return the values of a third answer matrix. While the matrix input commands seem to work based on thorough testing, I can't seem to get a correct answer from my multiplication command, despite going through each step of the algorithm and how it corresponds to actual matrix multiplication. The first value of the answer matrix is correct, and then all succeeding values are incorrect by some factor. Does anyone have any insight as to what could be going wrong?
#include <iostream>
#include <cmath>
using namespace std;
int r, c, a1, a2, b1, b2; //defines row and column indices
double m[1][1], m2[1][1], a[1][1]; //initializes matrices
double b;
int inflag = true;
int repflag = false;
void defmatrix() { //Defines the matrix size of the first inputted matrix
cout << "Matrix Rows: ";
cin >> r;
cout << "Matrix Columns: ";
cin >> c;
}
void fillmatrix() { //Fills the matrix with automatic or user-inputted values
if (inflag == true) {
for (int i = 0; i < r; i++) {
for (int j = 0; j < c; j++) {
cout << "Number in row " << i + 1 << " and column " << j + 1 << ": ";
cin >> b;
m[i][j] = b;
}
}
} else {
for (int i = 0; i < r; i++) {
for (int j = 0; j < c; j++) {
m[i][j] = 1;
}
}
}
for (int i = 0; i < r; i++) {
for (int j = 0; j < c; j++) {
cout << m[i][j] << " ";
}
}
}
void matrixmult() { //Multiplication function for matrix math
if (repflag == false) {
cout << "\n" << "Your second matrix will have " << c << " rows" << "\n";
b1 = c;
a1 = r;
r = c;
cout << "Second matrix columns: ";
cin >> c;
a2 = c;
double m2[r][c] = {};
if (inflag == true) {
for (int i = 0; i < r; i++) {
for (int j = 0; j < c; j++) {
cout << "Number in row " << i + 1 << " and column " << j + 1 << ": ";
cin >> b;
m2[i][j] = b;
}
}
} else {
for (int i = 0; i < r; i++) {
for (int j = 0; j < c; j++) {
m2[i][j] = 1;
}
}
}
a[a1][a2];
for (int i = 0; i < a1; i++) {
for (int j = 0; j < a2; j++) {
b = 0;
for (int d = 0; d < b1; d++) {
b = b + (m[i][d] * m2[d][j]);
}
a[i][j] = b;
}
}
for (int i = 0; i < a1; i++) {
for (int j = 0; j < a2; j++) {
cout << a[i][j] << " ";
}
}
}
}
int main() { //main file
defmatrix();
double m[r][c] = {};
fillmatrix();
matrixmult();
}
Thanks in advance!
There is a problem in almost every line. It is easier to point out the good parts. The central for loop that calculates the matrix product seems OK. Most everything else needs to be thrown out and rewritten. This includes all declarations and all function interfaces.
Here's how I would start writing the program.
int main()
{
int rows;
int cols;
int common_size; // columns in the matrix A and rows in the matrix B
std::cout << "Enter number of rows in the result : ";
std::cin >> rows;
std::cout << "Enter number of columns in the result : ";
std::cin >> cols;
std::cout << "Enter the common size : ";
std::cin >> common_size;
Now we need to declare the matrices, but here comes a problem. One would naïvely want to write something like
int a[rows][common_size], b[common_size][cols], c[rows][cols];
but alas, this is not legal C++. This declaration may work with your compiler, or it may not. There are several ways to declare the matrices correctly. The simplest one is to reserve a fixed amount of memory for each one, and just use a portion:
int a[10][10], b[10][10], c[10][10];
Naturally you need to check that the sizes provided by the user do not exceed 10.
Another method is to use the C++ version of variable length array, called [vector]. The declaration of a will look something like this:
std::vector<std::vector<int>> a(rows, std::vector<int>(common_size));
With the second method, there is no real limitation on the matrix sizes.
Neither method is adequate for any real software, but they are enough to complete your C++ exercise. Real software uses more advanced C++ concepts like classes to define matrices. Just something to be aware of.
I will not continue writing it at the same level of detail. This is your task. I will just show what the rest of main could look like.
input_matrix(a, rows, common_size);
input_matrix(b, common_size, cols);
multiply_matrices(a, b, c, rows, common_size, cols);
print_matrix(c, rows, cols);
Note function calls using arguments. Implement these functions, and you are good to go. Note, if you choose vectors, you will need to pass some arguments by reference. In this case you could also write the functions this way:
input_matrix(a);
input_matrix(b);
c = multiply_matrices(a, b);
print_matrix(c);
but this is a talk for another day.
Although you're asking to find mistakes in your code, as your task is interesting I decided to implement my own solution from scratch for you. Even if it is unacceptable answer, still it might be useful for educational purpose.
I made code in a form of class Matrix that supports multiplication operation between two matching matrices like A *= B;. As you did, I also implemented two methods .Input() and .Output() that correspondingly read matrix from std::cin and output to std::cout.
As a bonus there is also a method .At(i, j) that returns reference to element of a matrix with bounds checking, similar to std::vector.
Sure class can be extended to many other matrices operations (like addition A += B; or subtraction A -= B;), but I limited my class to only those things that you used in your original code.
To do what you wanted using my class is simple as few lines of code (that are located further in main() function of following code snippet):
Matrix A, B;
A.Input(); B.Input();
A *= B;
A.Output();
Full code:
Try it online!
#include <stdexcept>
#include <vector>
#include <sstream>
#include <iomanip>
#include <iostream>
#define ASSERT_MSG(cond, msg) { if (!(cond)) throw std::runtime_error("Assertion (" #cond ") failed at line " + std::to_string(__LINE__) + "! Msg: '" + std::string(msg) + "'."); }
#define ASSERT(cond) ASSERT_MSG(cond, "")
class Matrix {
public:
using FloatT = double;
Matrix(size_t rows = 0, size_t cols = 0)
: rows_(rows), cols_(cols) {
Clear();
}
Matrix & Clear() {
m_.clear();
m_.resize(rows_ * cols_);
return *this;
}
size_t Rows() const { return rows_; }
size_t Cols() const { return cols_; }
Matrix & operator = (Matrix && other) {
rows_ = other.rows_;
cols_ = other.cols_;
m_ = std::move(other.m_);
other.rows_ = 0;
other.cols_ = 0;
return *this;
}
FloatT & operator() (size_t i, size_t j) {
return m_[i * cols_ + j];
}
FloatT const & operator() (size_t i, size_t j) const {
return const_cast<Matrix &>(*this)(i, j);
}
FloatT & At(size_t i, size_t j) {
ASSERT_MSG(i < rows_ && j < cols_,
"Matrix index (" + std::to_string(i) + ", " + std::to_string(j) +
") out of bounds (" + std::to_string(rows_) + ", " + std::to_string(cols_) + ")!");
return (*this)(i, j);
}
FloatT const & At(size_t i, size_t j) const {
return const_cast<Matrix &>(*this).At(i, j);
}
Matrix & operator *= (Matrix const & B) {
Matrix const & A = *this;
ASSERT_MSG(A.Cols() == B.Rows(),
"Number of A.Cols " + std::to_string(A.Cols()) +
" and B.Rows " + std::to_string(B.Rows()) + " don't match!");
Matrix C(A.Rows(), B.Cols());
for (size_t i = 0; i < A.Rows(); ++i)
for (size_t j = 0; j < B.Cols(); ++j) {
FloatT sum = 0;
for (size_t k = 0; k < A.Cols(); ++k)
sum += A(i, k) * B(k, j);
C(i, j) = sum;
}
*this = std::move(C);
return *this;
}
Matrix & Input() {
std::cout << "Enter number of rows and columns: ";
std::cin >> rows_ >> cols_;
Clear();
std::cout << "Enter all matrix elements:" << std::endl;
for (size_t i = 0; i < rows_; ++i)
for (size_t j = 0; j < cols_; ++j)
std::cin >> (*this)(i, j);
return *this;
}
Matrix const & Output(size_t precision = 6) const {
std::vector<std::vector<std::string>> cells(Rows(),
std::vector<std::string>(Cols()));
std::ostringstream ss;
size_t max_len = 0;
for (size_t i = 0; i < Rows(); ++i)
for (size_t j = 0; j < Cols(); ++j) {
ss.str("");
ss << std::fixed << std::setprecision(precision)
<< (*this)(i, j);
cells[i][j] = ss.str();
max_len = std::max(max_len, cells[i][j].size());
}
for (auto const & row: cells) {
for (auto const & cell: row)
std::cout << std::string(max_len - cell.size() + 1, ' ')
<< cell;
std::cout << std::endl;
}
return *this;
}
private:
size_t rows_ = 0, cols_ = 0;
std::vector<FloatT> m_;
};
int main() {
try {
Matrix A, B;
A.Input();
B.Input();
A *= B;
A.Output();
return 0;
} catch (std::exception const & ex) {
std::cout << "Exception: " << ex.what() << std::endl;
return -1;
}
}
Input:
Enter number of rows and columns: 3 2
Enter all matrix elements:
1.01 2.02
3.03 4.04
5.05 6.06
Enter number of rows and columns: 2 4
Enter all matrix elements:
7.07 8.08 9.09 10.10
11.11 12.12 13.13 14.14
Output:
29.582900 32.643200 35.703500 38.763800
66.306500 73.447200 80.587900 87.728600
103.030100 114.251200 125.472300 136.693400
I'm trying to split a matrix given into 4, but I'm getting some errors and I can't figure out why. I'm kind of new to the language. Does anybody knows what am I doing wrong?
void split_tl(T **matrice, unsigned int dim){
if(dim == 1){
return;
}
T **tl = new T*[dim/4];
for(unsigned int i = 0; i<dim/4;++i){
tl[i] = new T[dim/4];
}
for(unsigned int i=0; i<dim;++i){
for(unsigned int j=0; j<dim;j++){
if((i<dim/2) && (j<dim/2)){
tl[i][j] = matrice[i][j];
} else{
std::cout << "no ";
}
}
std::cout << std::endl;
}
}
In this function I'm trying to obtain the top left corner of the matrix.
int **matrice = new int*[2];
for(unsigned int i = 0; i<2;++i){
matrice[i] = new int[2];
}
for(unsigned int i = 0; i<2;++i){
for(unsigned int j = 0; j<2;++j){
matrice[i][j] = i+j;
}
}
This is the matrix I'm sending. It is a 2x2 matrix, just for testing purposes.
These are the errors from Valgrind:
==133== Invalid read of size 8
==133== Invalid write of size 4
==133== Process terminating with default action of signal 11 (SIGSEGV)
==133== Access not within mapped region at address 0x0
If dim is the side of a matrix, allocating to a quarter matrix should be dim/2.
Below in the code you are using :
if((i<dim/2) && (j<dim/2)){
tl[i][j] = matrice[i][j];
}
here tl may exceed the allocation
What you're doing wrong? You're allocating memory and passing pointers around.
Build a proper matrix class, e.g. (very simplified version):
template <typename T, unsigned Rows, unsigned Cols>
struct generic_matrix {
using datatype = T;
static constexpr unsigned rows = Rows;
static constexpr unsigned cols = Cols;
datatype data[rows][cols];
constexpr datatype& operator()(unsigned row, unsigned col) noexcept
{ return data[row][col]; }
constexpr const datatype& operator()(unsigned row, unsigned col) const noexcept
{ return data[row][col]; }
};
Submatrix:
/* Returns a submatrix of the matrix m,
* by deleting the row r and the column c.*/
template <typename M>
auto submatrix(const M& m, unsigned r, unsigned c) noexcept
{
generic_matrix<typename M::datatype, M::rows-1, M::cols-1> res;
for (unsigned row = 0, i = 0; row < M::rows; ++row) {
if (row == r) continue; //this row we do not want
for (unsigned col = 0, j = 0; col < M::cols; ++col) {
if (col == c) continue; //this col we do not want
res(i,j) = m(row,col);
++j;
}
++i;
}
return res;
}
Print:
template <typename M>
void printmatrix(const M& m) noexcept
{
for (unsigned r=0; r < M::rows; ++r) {
for (unsigned c=0; c < M::cols; ++c) {
std::cout << m(r,c) << ' ';
}
std::cout << '\n';
}
}
Test:
int main()
{
int n=0;
generic_matrix<int, 3, 3> m;
for (int r = 0; r < 3; ++r)
for (int c = 0; c < 3; ++c)
m(r,c) = ++n;
printmatrix(m);
std::cout << '\n';
printmatrix(submatrix(m, 0, 0));
std::cout << '\n';
printmatrix(submatrix(m, 1, 1));
std::cout << '\n';
printmatrix(submatrix(m, 2, 2));
return 0;
}
Note: It is just a hint. As you can see, no allocation nor casting is needed.
I'm having issues to implement the determinant of a matrix inside my matrix template. I'm trying to obtain the determinant by calculating the product of the principal diagonal in the reduced associated matrix. The problem is that it only works for some cases and is not reliable. This is the code for the determinant
template <typename T>
T Matrix<T>::Det() const {
if (Rows != Cols) {
cout << "Matrix must be square" << endl;
}
Matrix<T> r = Reduced();
T Det = 1;
for (int i=0; i<Rows; i++) {
Det *= r.getValue(i, i);
}
return Det;
}
Since the Gaussian elimination works, the reduced matrix that I obtain for any given matrix (which fits the criteria to be reduced) is fine, so i thought there wouldn't be any issue, but I haven't been able to see what I'm missing.
Any advice will be welcomed!
Edit:
As someone pointed out, here is a minimal reproducible example.
This would be the template
#ifndef MATRIX_H
#define MATRIX_H
#include <vector>
#include <iostream>
using namespace std;
template <typename T>
class Matrix {
private:
unsigned int Rows;
unsigned int Cols;
T *Mat;
public:
Matrix(unsigned int Dim);
Matrix(unsigned int Rows, unsigned int Cols);
Matrix(unsigned int Rows, unsigned int Cols, const std::vector<T>& Vec);
Matrix(unsigned int Dim, const std::vector<T>& Vec);
Matrix(const Matrix<T>& M);
~Matrix();
T& getValue(unsigned int Row, unsigned int Col) const;
T Det() const;
Matrix<T> Reduced() const;
};
template <typename T>
Matrix<T>::Matrix(unsigned int Rows, unsigned int Cols)
: Rows(Rows), Cols(Cols) {
if (Rows<=0 || Cols<=0) {
cout << "Número inválido de filas o columnas" << endl;
}
Mat = new T[Rows*Cols];
for (int i=0; i<Rows*Cols; i++) {
Mat[i] = 0;
}
}
template <typename T>
Matrix<T>::Matrix(unsigned int Dim)
: Rows(Dim), Cols(Dim) {
if(Rows<=0 || Cols<=0) {
cout << "Número inválido de filas o columnas" << endl;
}
Mat = new T[Rows*Cols];
for (int i=0; i<Rows*Cols; i++) {
Mat[i] = 0;
}
}
template <typename T>
Matrix<T>::Matrix(unsigned int Rows, unsigned int Cols, const std::vector<T>& Vec)
: Rows(Rows), Cols(Cols) {
if (Rows<=0 || Cols<=0) {
cout << "Número inválido de filas o columnas" << endl;
}
Mat = new T[Rows*Cols];
if (Vec.size() != Rows*Cols) {
cout << "Los tamaños de la matriz y el vector no son iguales" << endl;
}
for (int i=0; i<Rows*Cols; i++) {
Mat[i] = Vec[i];
}
}
template <typename T>
Matrix<T>::Matrix(unsigned int Dim, const std::vector<T>& Vec)
: Rows(Dim), Cols(Dim) {
if (Rows<=0 || Cols<=0) {
cout << "Número inválido de filas o columnas" << endl;
}
Mat = new T[Rows*Cols];
if (Vec.size() != Rows*Cols) {
cout << "Número inválido de filas o columnas" << endl;
}
for (int i=0; i<Rows*Cols; i++) {
Mat[i] = Vec[i];
}
}
template <typename T>
Matrix<T>::Matrix(const Matrix<T>& M)
: Rows(M.Rows), Cols(M.Cols), Mat(new T[Rows * Cols]) {
for (int i = 0; i < Rows * Cols; i++)
Mat[i] = M.Mat[i];
}
template <typename T>
Matrix<T>::~Matrix() {
delete[] Mat;
}
template <typename T>
T &Matrix<T>::getValue(unsigned int Row, unsigned int Col) const {
if (Row<0 || Row>=Rows || Col<0 || Col>=Cols) {
cout << "Índice incorrecto" << endl;
}
return Mat[Row*Cols + Col];
}
template <typename T>
Matrix<T> Matrix<T>::Reduced() const {
if (Rows != Cols) {
cout << "La matriz debe ser cuadrada para reducirse" << endl;
}
Matrix<T> Tri(*this);
int n = Rows;
int m = 0;
for (int k=0; k<n-1; k++) {
if (Tri.Mat[k*Cols + k] == 0)
cout << "La matriz es singular" << endl;
for (int i = k+1; i<n; i++) {
m = Tri.Mat[i*Cols + k]/Tri.Mat[k*Cols + k];
for (int j = k+1; j<n; j++) {
Tri.Mat[i*Cols + j] = Tri.Mat[i*Cols + j] - m*Tri.Mat[k*Cols + j];
}
Tri.Mat[i*Cols + k] = 0;
}
}
return Tri;
}
template <typename T>
T Matrix<T>::Det() const {
if (Rows != Cols) {
cout << "Matrix must be square" << endl;
}
Matrix<T> r = Reduced();
T Det = 1;
for (int i=0; i<Rows; i++) {
Det *= r.getValue(i, i);
}
return Det;
}
template <class T>
ostream & operator<<(ostream &os, const Matrix<T> &Shw) {
for (int i=0; i<Shw.getRows(); i++) {
os << "| ";
for (int j=0; j<Shw.getCols(); j++) {
os << Shw.getValue(i,j) << " ";
}
os << "|\n";
}
return os;
}
#endif
This is the implementation on main.cc
#include "matrix.h"
int main() {
Matrix<double> M1(2, 2, {1, 3, 5, 7});
cout << M1.Det() << endl;
Matrix<double> M2(3, 3, {1, 3, 5, 7, 8, 9, 11, 2, 14});
cout << M2.Det() << endl;
return 0;
}
And the output is the following
-8 //This one is okay
-143 //This one is not
After a little inspection, I think I've found your problem.
You've made a simple mistake defining one of your key variables m. This is as you know, the main scaler in Gaussian elimination, and its job is to zero out the next row's element.
Instead of defining it as int m = 0 you should've defined it as float m = 0 or double m = 0.
Also, in the line that you calculate m you need to force cast the operands of the division to the appropriate types. So after redefining m, you should change this line:
m = Tri.Mat[i*Cols + k]/Tri.Mat[k*Cols + k]
to this
m = Tri.Mat[i*Cols + k] / (double)Tri.Mat[k*Cols + k]
Keep in mind that you're template type should be double as well or again this will not work.
Also, I recommend you to take a look at here to understand some of Gaussian Elimination's limits. There are some cases in which the algorithm will not work effectively.
What begun as a revision of some aspects of C++ has become a sketch of something that could be useful for me. The idea is to use a single array as a double array where indexing is transparent for the user.
class matrice {
private:
int* data;
int rows, cols;
public:
matrice(int _rows, int _cols) : rows(_rows), cols(_cols) { data = new int[_rows*_cols]; std::cout << "matrice ctr" << std::endl;}
// hardcoded 2x2 matrix
void set_value() { data[0] = 1; data[1] = 0; data[3] = 1; data[4] = 0;}
int get_rows() { return rows;}
int get_cols() { return cols;}
class proxy {
private:
int i;
int j;
const int rows; // it is constant so we don't mess up with the size of the matrix
int *proxy_data;
public:
proxy(int* _proxy_data, int _i, const int& _rows) : proxy_data(_proxy_data), i(_i), rows(_rows) { }
int operator[] (int _j) { j = _j; std::cout << "proxy:\n\tj = " << j << " " << proxy_data[j] << std::endl; return proxy_data[i*rows + j];}
};
proxy operator[] (int i) { std::cout << "matrice:\n\ti = " << i << std::endl; return proxy(data, i, rows); }
};
int main()
{
int rows = 2;
int cols = 2;
matrice matp(rows, cols);
matp.set_value();
matp[0][0] = 2;
for (int i = 0;i < rows;i++) {
for (int j = 0;j < cols;j++) {
std::cout << "matp[" << i << "][" << j << "] = " << matp[i][j] << std::endl;
}
}
}
So far I can access the data in the array however, I want to assign values to it i.e.:
matp[0][0] = 2;
How could I do it ?
int& operator[] (int j)const&&
Also delets int j member it is pointless.
int& operator[] (int j) const&& {
std::cout << "proxy:\n\tj = " << j << " " << proxy_data[j] << std::endl;
return proxy_data[i*rows + j];}
};
or better:
template<class X>
class proxy {
private:
X *proxy_data;
public:
proxy(X* _proxy_data, std::size_t i, std::size_t stride) : proxy_data(_proxy_data+i*stride) { }
X& operator[] (std::size_t j) const&& { return proxy_data[j]; }
};
use proxy<int> and proxy<const int> as the return value from [] and [] const.
I have implemented a heap data structure, and use it to sort. My understanding is that it is O(nlogn) complexity. However, when compared to bubble sort, it is order of magnitude slower -- and yeah, I tried running it for larger arrays. I checked some answers at SO (in particular this and this), but still lost. Could anyone point out what am I doing wrong here, please?
The results are:
HEAP SORT: 12415690ns
QUICK SORT: 71ns
BUBBLE SORT: 541659ns
Here are the codes:
main.cpp:
#include <chrono>
#include <iostream>
#include <stdexcept>
#include <vector>
// #include "heap.cpp"
// #include "pqueue.cpp"
#include "sort.cpp"
using namespace std;
using namespace std::chrono;
template <class T>
void printVector (vector<T> A) {
for (std::vector<int>::iterator it = A.begin(); it != A.end(); ++it) {
std::cout << *it << ' ';
}
cout << endl;
}
template <class T>
vector<T> constructVector(int A[], std::size_t len, std::size_t num) {
vector<T> res (A, A+len);
for (std::size_t idx = 0; idx < num-1; ++idx) {
res.push_back(A[idx%len]);
}
return res;
}
int main() {
high_resolution_clock::time_point t1;
high_resolution_clock::time_point t2;
int a[] = {4, 1, 3, 2, 16, 9, 10, 14, 8, 7};
std::size_t len = sizeof(a) / sizeof(int);
vector<int> HEAP = constructVector<int>(a, len, 32000); // (a, a + sizeof(a) / sizeof(int));
vector<int> QUICK = constructVector<int>(a, len, 32000); // (a, a + sizeof(a) / sizeof(int));
vector<int> BUBBLE = constructVector<int>(a, len, 32000);
// cout << "Original Array: "; printVector(HEAP);
cout << "HEAP SORT: ";
t1 = high_resolution_clock::now();
heapsort(HEAP);
t2 = high_resolution_clock::now();
cout << duration_cast<nanoseconds>( t2 - t1 ).count() << "ns\n";
// cout << "New Array: "; printVector(HEAP);
// cout << "Original Array: "; printVector(QUICK);
cout << "QUICK SORT: ";
t1 = high_resolution_clock::now();
quicksort(QUICK, 0, QUICK.size());
t2 = high_resolution_clock::now();
cout << duration_cast<nanoseconds>( t2 - t1 ).count() << "ns\n";
// cout << "New Array: "; printVector(HEAP);
// cout << "Original Array: "; printVector(QUICK);
cout << "BUBBLE SORT: ";
t1 = high_resolution_clock::now();
bublesort(BUBBLE);
t2 = high_resolution_clock::now();
cout << duration_cast<nanoseconds>( t2 - t1 ).count() << "ns\n";
// cout << "New Array: "; printVector(HEAP);
}
sort.cpp:
#ifndef __SORT_CPP_INCLUDED_
#define __SORT_CPP_INCLUDED_
#include <vector>
#include "heap.cpp"
template <class T>
void heapsort(std::vector<T> &A, bool increasing = true) {
Heap<T> H(A, increasing);
H.sort();
A = H.get();
}
template <class T>
std::size_t partition(std::vector<T> &A, std::size_t p, std::size_t r) {
T x = A[r-1];
std::size_t i = p - 1;
for (std::size_t j = p; j < r; ++j) {
if (A[j] <= x) {
++i;
A[i] ^= A[j];
A[j] ^= A[i];
A[i] ^= A[j];
}
}
A[i+1] ^= A[r-1];
A[r-1] ^= A[i+1];
A[i+1] ^= A[r-1];
return i + 1;
}
template <class T>
void quicksort(std::vector<T> &A, std::size_t p, std::size_t r) {
if (p-1 < r) {
std::size_t q = partition(A, p, r);
quicksort(A, p, q);
quicksort(A, q+1, r);
}
}
template <class T>
void bublesort(std::vector<T> &A) {
bool swapped = false;
do {
swapped = false;
for (std::size_t idx = 1; idx < A.size(); ++idx) {
if (A[idx-1] > A[idx]) {
// swap them
A[idx] = A[idx-1];
A[idx-1] = A[idx];
A[idx] = A[idx-1];
swapped = true;
}
}
} while (swapped);
}
#endif
heap.cpp:
#ifndef __HEAP_CPP_INCLUDED__
#define __HEAP_CPP_INCLUDED__
#include <vector>
template <class T>
class Heap {
public:
Heap(bool maxHeap = true) : heap_size(0), max_heap(maxHeap) {}
Heap(const std::vector<T> &a, bool maxHeap = true) : A(a), max_heap(maxHeap) {
if (maxHeap) this->build_max_heap(); else this->build_min_heap(); }
~Heap() {}
protected:
std::vector<T> A;
std::size_t heap_size;
bool max_heap;
public:
std::size_t parent(std::size_t idx) { return (idx - 1) >> 1; }
std::size_t left(std::size_t idx) { return (idx << 1) + 1; }
std::size_t right (std::size_t idx) { return (idx + 1) << 1; }
public:
std::vector<T> get() { return A; }
std::size_t size() { return heap_size; }
void sort();
void build_max_heap();
void build_min_heap();
void max_heapify(std::size_t idx);
void min_heapify(std::size_t idx);
};
template <class T>
void Heap<T>::sort() {
if (this->heap_size <= 0) return; // Already sorted or empty
if (this->heap_size != this->A.size()){ // Not sorted and not heapified
max_heap ? build_max_heap() : build_min_heap();
}
for (std::size_t idx = this->A.size()-1; idx > 0; --idx) {
A[0] ^= A[idx];
A[idx] ^= A[0];
A[0] ^= A[idx];
--this->heap_size;
max_heap ? max_heapify(0) : min_heapify(0);
}
}
template<class T>
void Heap<T>::build_max_heap() {
this->heap_size = this->A.size();
for (std::size_t idx = (this->A.size() - 1) >> 1; idx > 0; --idx)
this->max_heapify(idx);
this->max_heapify(0);
}
template<class T>
void Heap<T>::build_min_heap() {
this->heap_size = this->A.size();
for (std::size_t idx = (this->A.size()-1) >> 1; idx > 0; --idx)
this->min_heapify(idx);
this->min_heapify(0);
}
template <class T>
void Heap<T>::max_heapify(std::size_t idx) {
std::size_t l = this->left(idx);
std::size_t r = this->right(idx);
std::size_t largest;
if (l < this->heap_size && A[l] > A[idx]) largest = l;
else largest = idx;
if (r < this->heap_size && A[r] > A[largest]) largest = r;
if (largest != idx) {
this->A[idx] ^= this->A[largest];
this->A[largest] ^= this->A[idx];
this->A[idx] ^= this->A[largest];
this->max_heapify(largest);
}
}
template <class T>
void Heap<T>::min_heapify(std::size_t idx) {
std::size_t l = this->left(idx);
std::size_t r = this->right(idx);
std::size_t smallest;
// std::cout << "DEBUG: " << idx << std::endl;
if (l < this->heap_size && A[l] < A[idx]) smallest = l;
else smallest = idx;
if (r < this->heap_size && A[r] < A[smallest]) smallest = r;
if (smallest != idx) {
this->A[idx] ^= this->A[smallest];
this->A[smallest] ^= this->A[idx];
this->A[idx] ^= this->A[smallest];
this->min_heapify(smallest);
}
}
#endif
Your bubble sort doesn't swap, but only copy. This would make it somewhat faster. Not sure this alone explains being so fast, though.
Your Heap<T> makes a copy of the array. This can explain the slowness. I guess you forgot a &.
You should have noticed 71ns to sort 32k array is not real. Your quicksort never sorts anything. You can use std::sort for a reliable quicksort.
Too much is known at compile time and the numbers are far from random. Switch to arrays with random numbers in this kind of testing.