Operaciones con matrices – Clase Matriz en c++

El siguiente es el primer ejercicio que hice con matrices. Quizás alguno de estos ejercicios te puedan servir como una base.

Puedes leer el post anterior sobre vectores, matrices y punteros si aún no haz trabajo con ellos.

Recomiendo que intentes y practiques mucho antes de copiar y pegar.  Si te sientes estancado en algún problema recién trata de buscar una guía para llegar a la solución.

Esta clase matriz tendrá 3 atributos: Matriz, número de filas y número de columnas. Tiene un constructor por defecto, constructor copia y un constructor por parámetro que recibe el número de filas y columnas.

Las siguientes funciones fueron implementadas:

  • Generar una matriz dinámicamente
  • LLenar la matriz desde teclado:
  • Llenar la matriz aleatoriamente:
  • Imprimir la matriz
  • Hallar el mayor elemento.
  • Hallar el menor elemento.
  • Hallar la moda.
  • Intercambiar filas.
  • Intercambiar columnas.
  • Sumar otra matriz: Sumar 2 objetos de tipo matriz retornar otra matriz. Ejem: c = a + b.
  • Restar otra matriz: Igual que la suma. Ejem: c = a – b
  • Multiplicar por otra matriz: Ejem: c = a * b. El nro de filas de a debe ser igual al nro de columnas de b
  • Multiplicar por un escalar: Ingreso un número y todos los elementos de la matriz se multiplican por ese número.
  • Hallar matriz transpuesta: matr[m][n] su transpuesta es matr[n][m]. Se obtiene cambiando filas por columnas. Donde los elementos de la fila m ahora pertenecen a la columna m de la transpuesta.
  • Verificar si es simétrica: Una matriz es simétrica si es cuadrada(filas = columnas) y cuando su transpuesta es igual a su matriz original.
  • Verificar si es identidad: Es identidad si tiene todos sus elementos nulos excepto los de la diagonal principal que son iguales a 1.

NOTA: He aplicado un poco de templates para manejar la matriz con varios tipos de datos (int, float, char, double) y sobrecarga de operadores para la suma, resta y multiplicación de matrices.

Recien estoy aprendiendo a usar estas 2 características importantes del lenguaje (templates y sobrecarga de operadores). 

En breve:

Un template es una manera de que funciones, clases, métodos puedan ser usados con varios tipos de datos. Imagínense crear una lista de datos y tener que crear funciones insertar, eliminar, buscar, concatenar, etc para cada tipo de dato. Si los métodos y la clase tienen la misma lógica para que reescribir código si podemos reutilizar.

La sobrecarga de operadores es una manera de realizar las mismas operaciones que solemos hacer con tipos de datos comunes con tipos abstractos de datos. Por ejemplo, la sobrecarga me permitió sumar 2 objetos de tipo Matriz y almacenar el resultado en otro objeto Matriz, del modo c = a + b

Aquí está la definición e implementación de la clase Matriz.

matrix.h

#ifndef MATRIX_H
#define MATRIX_H

#include <iostream>
#include <time.h>
#include <stdlib.h>

using namespace std;

template <class T>
class Matrix {
    public:
        Matrix();
        Matrix(int, int);
        Matrix(const Matrix &m);
        ~Matrix();

        Matrix<T> operator+ (const Matrix &matrix_2);
        Matrix<T> operator- (const Matrix &matrix_2);
        Matrix<T> operator* (const Matrix &matrix_2);

        bool isSymmetric();
        bool isIdentity();

        T get_max();
        T get_min();
        T get_mode();

        void delete_matrix();
        void fill_by_user();
        void fill_random();
        void get_transpose();
        void multiply_by_scalar(T);
        void print_matrix();
        void swap_cols(int, int);
        void swap_rows(int, int);

    private:
        T m_ele;
        T m_max;
        T m_min;
        T m_mode;
        T **m_matrix;

        int m_dim_matrix;
        int m_cols;
        int m_rows;
};

#endif // MATRIX_H

matrix.cpp

#include "matrix.h"

// Constructor por defecto
template<typename T>
Matrix<T>::Matrix()
{
    m_rows = 4;
    m_cols = 4;
}

// Constructor copia
template<typename T>
Matrix<T>::Matrix(const Matrix &m)
{
    *this = m;
}

// Constructor por parámetro
template<typename T>
Matrix<T>::Matrix(int rows , int cols)
{
    m_cols = cols;
    m_rows = rows;
    m_matrix = new T*[m_rows];

    for (int i = 0; i < m_rows; i++) {
        m_matrix[i] = new T[m_cols];
    }
}

// Suma de matrices con sobrecarga de operadores
template<typename T>
Matrix<T> Matrix<T>::operator+ (const Matrix &matrix_2)
{
    Matrix matrix_result(m_rows, m_cols);
    for (int i = 0; i < m_rows; i++) {
        for (int j = 0; j < m_cols; j++) {
            matrix_result.m_matrix[i][j] = m_matrix[i][j] + matrix_2.m_matrix[i][j];
        }
    }
    return matrix_result;
}

// Resta de matrices con sobrecarga de operadores
template<typename T>
Matrix<T> Matrix<T>::operator- (const Matrix &matrix_2)
{
    Matrix matrix_result(m_rows, m_cols);
    for (int i = 0; i < m_rows; i++) {
        for (int j = 0; j < m_cols; j++) {
            matrix_result.m_matrix[i][j] = m_matrix[i][j] - matrix_2.m_matrix[i][j];
        }
    }
    return matrix_result;
}

// Multiplicación de matrices con sobrecarga de operadores
template<typename T>
Matrix<T> Matrix<T>::operator* (const Matrix &matrix_2)
{
    Matrix matrix_result(m_rows, matrix_2.m_cols);
    T total;

    for (int i = 0; i < m_rows; i++) {
        for (int j = 0; j < matrix_2.m_cols; j++) {
            for (int k = 0; k < m_cols; k++) {
                total += (m_matrix[i][k] * matrix_2.m_matrix[k][j]);
            }
            matrix_result.m_matrix[i][j] = total;

            // Limpiar el total sumado arriba
            total = 0;
        }
    }
    return matrix_result;
}

// Verificar si una Matriz es simétrica
template<typename T>
bool Matrix<T>::isSymmetric()
{
    if (m_rows != m_cols) {
        return false;
    }

    for (int i = 0; i < m_rows; i++) {
        for (int j = 0; j < m_cols; j++) {
            if (m_matrix[i][j] != m_matrix[j][i]) {
                return false;
            }
        }
    }
    return true;
}

// Verificar si una Matriz es identidad
template<typename T>
bool Matrix<T>::isIdentity()
{
    if (m_rows != m_cols) {
        return false;
    }

    for (int i = 0; i < m_rows; i++) {
        for (int j = 0; j < m_cols; j++) {
            if (i == j) {
                if (m_matrix[i][j] != 1)
                    return false;
            } else {
                if (m_matrix[i][j] != 0)
                    return false;
            }
        }
    }
    return true;
}

// Obtener el mayor de la Matriz
template<typename T>
T Matrix<T>::get_max()
{
    for (int i = 0; i < m_rows; i++) {
        for (int j = 0; j < m_cols; j++) {
            if (m_matrix[i][j] > m_max) {
                m_max = m_matrix[i][j];
            }
        }
    }
    return m_max;
}

// Obtener el menor de la Matriz
template<typename T>
T Matrix<T>::get_min()
{
    for (int i = 0; i < m_rows; i++) {
        for (int j = 0; j < m_cols; j++) {
            if (m_matrix[i][j] < m_min) {
                m_min = m_matrix[i][j];
            }
        }
    }
    return m_min;
}

// Obtener la moda de la Matriz
template<typename T>
T Matrix<T>::get_mode()
{
    // Creo una Matrix auxiliar
    Matrix matrix_aux(m_rows, m_cols);

    // Lleno la Matriz con ceros
    for (int i = 0; i < m_rows; i++) {
        for (int j = 0; j < m_cols; j++) {
            matrix_aux.m_matrix[i][j] = 0;
        }
    }

    m_dim_matrix = m_rows * m_cols;

    // Para retener una fila n veces
    int y = 0;

    // Para retener una columna n veces
    int z = 0;

    // Empiezo a comparar cada elemento n veces
    for (int x = 0; x < m_dim_matrix; x++) {
        for (int i = 0; i < m_rows; i++) {
            for (int j = 0; j < m_cols; j++) {
                if (m_matrix[y][z] == m_matrix[i][j]) {
                    matrix_aux.m_matrix[i][j]++;
                }
            }
        }

        // Pasar a la siguiente columna despues de n comparaciones
        z++;

        /* Empiezo a comparar con la siguiente fila
           despues empiezo nuevamente en la 1era columna
           Y luego paso a la siguiente fila */
        if (z == m_cols) {
            z = 0;
            y++;
        }
    }

    // Obtengo el mayor valor de la Matriz
    m_max = matrix_aux.get_max();

    // Si ningun valor se ha repetido más de una vez, entonces no hay moda
    if (m_max == 1) {
        return -1;
    } else {
        for (int i = 0; i < m_rows; i++) {
            for (int j = 0; j < m_cols; j++) {
                if (matrix_aux.m_matrix[i][j] == m_max) {
                    m_mode = m_matrix[i][j];
                }
            }
        }
    }

    return m_mode;
}

template<typename T>
void Matrix<T>::delete_matrix()
{
    for (int i = 0; i < m_rows; i++) {
        delete[] m_matrix[i];
    }
    delete[] m_matrix;
}

// Llenar una Matriz desde teclado
template<typename T>
void Matrix<T>::fill_by_user()
{
    for (int i = 0; i < m_rows; i++) {
        cout << "Fila " << i + 1 << endl;
        for (int j = 0; j < m_cols; j++) {
            cout << "Ingresa el elemento " << j + 1 << endl;
            cin >> m_ele;
            m_matrix[i][j] = m_ele;
        }
        cout << endl;
    }
    m_max = m_matrix[0][0];
    m_min = m_matrix[0][0];
}

// Llenar aleatoriamente una Matriz
template<typename T>
void Matrix<T>::fill_random()
{
    for (int i = 0; i < m_rows; i++) {
        for (int j = 0; j < m_cols; j++) {
            m_matrix[i][j] = rand() % 30;
        }
    }

    m_max = m_matrix[0][0];
    m_min = m_matrix[0][0];
    srand(time(NULL));
}

// Obtener la transpuesta de una Matriz
template<typename T>
void Matrix<T>::get_transpose()
{
    Matrix matrix_result(m_cols, m_rows);

    for (int i = 0; i < m_cols; i++) {
        for (int j = 0; j < m_rows; j++) {
            matrix_result.m_matrix[i][j]= m_matrix[j][i];
        }
    }
    matrix_result.print_matrix();
}

// Multiplicar a una Matriz por un escalar
template<typename T>
void Matrix<T>::multiply_by_scalar(T scalar)
{
    for (int i = 0; i < m_rows; i++) {
        for (int j = 0; j < m_cols; j++) {
            m_matrix[i][j] = m_matrix[i][j] * scalar;
        }
    }
    cout << "Se multiplicó a la Matriz por el escalar " << scalar << endl;
}

// Imprimir Matriz
template<typename T>
void Matrix<T>::print_matrix()
{
    for (int i = 0; i < m_rows; i++) {
        for (int j = 0; j < m_cols; j++) {
            cout << m_matrix[i][j] << " ";
        }
        cout << endl << endl;
    }
    cout << endl << endl;
}

// Intercambiar dos columnas en una Matriz
template<typename T>
void Matrix<T>::swap_cols(int col_1, int col_2)
{
    if (col_1 > m_cols || col_2 > m_cols) {
        cout << "Esa columna se encuentra fuera de rango." << endl;
    } else {
        T temp;
        col_1--;
        col_2--;

      for (int i = 0; i < m_rows; i++) {
          temp = m_matrix[i][col_1];
          m_matrix[i][col_1] = m_matrix[i][col_2];
          m_matrix[i][col_2] = temp;
      }
      cout << "Se intercambiaron las columnas " << col_1 + 1 << " y " << col_2 + 1 << endl;
    }
}

// Intercambiar dos filas en una Matriz
template<typename T>
void Matrix<T>::swap_rows(int row_1, int row_2)
{
    if (row_1 > m_rows || row_2 > m_rows) {
        cout << "Esa fila se encuentra fuera de rango." << endl;
    } else {
        T temp;
        row_1--;
        row_2--;

        for (int i = 0; i < m_cols; i++) {
            temp = m_matrix[row_1][i];
            m_matrix[row_1][i] =  m_matrix[row_2][i];
            m_matrix[row_2][i] = temp;
        }
        cout << "Se intercambiaron las filas: " << row_1 + 1 << " y " << row_2 + 1 << endl;
    }
}

template<typename T>
Matrix<T>::~Matrix() {}

main.cpp

#include <iostream>
#include "matrix.h"
#include "matrix.cpp"

using namespace std;

int main()
{
    // Para no generar los mismos números aleatorios
    srand(time(NULL));

    int n_rows;
    int n_cols;

    int col_1;
    int col_2;
    int row_1;
    int row_2;
    int scalar;

    cout << "Ingresa nro de filas: " << endl;
    cin >> n_rows;
    cout << "Ingresa nro de columnas: " << endl;
    cin >> n_cols;
    cout << endl;

    Matrix<int> a(n_rows, n_cols);
    Matrix<int> b(n_rows, n_cols);

    // Matriz para almacenar el resultado de las operaciones
    Matrix<int> c(n_rows, n_cols);

    a.fill_random();
    b.fill_random();

    cout << "********** Operaciones básicas con la Matriz A **********" << endl;
    cout << "Matriz A " << endl;
    a.print_matrix();

    cout << "El mayor de la Matriz es: " << a.get_max() << endl;
    cout << "El menor de la Matriz es: " << a.get_min() << endl;
    cout << "La moda de la Matrix es: " << a.get_mode() << endl;
    cout << (a.isSymmetric() ? "" : "No") << " Es simétrica." << endl;
    cout << (a.isIdentity() ? "" : "No") << " Es identidad." << endl;
    cout << endl;

    cout << "Ingresa el escalar: " << endl;
    cin >> scalar;
    a.multiply_by_scalar(scalar);
    a.print_matrix();

    cout << "Intercambio: Ingresa 2 columnas del 1 al " << n_cols << endl;
    cout << "Columna 1: " << endl;
    cin >> col_1;
    cout << "Columna 2: " << endl;
    cin >> col_2;
    a.swap_cols(col_1, col_2);
    a.print_matrix();

    cout << "Intercambio: Ingresa 2 filas del 1 al " << n_rows << endl;
    cout << "Fila 1: " << endl;
    cin >> row_1;
    cout << "Fila 2: " << endl;
    cin >> row_2;
    a.swap_rows(row_1, row_2);
    a.print_matrix();

    cout << "Transpuesta de A " << endl;
    a.get_transpose();

    cout << "********** Operaciones con matrices **********" << endl;
    cout << "Matriz A " << endl;
    a.print_matrix();

    cout << "Matriz B " << endl;
    b.print_matrix();

    cout << "Matriz A + B " << endl;
    c = a + b;
    c.print_matrix();

    cout << "Matriz A - B " << endl;
    c = a - b;
    c.print_matrix();

    cout << "Matriz A * B " << endl;
    c = a * b;
    c.print_matrix();

    a.delete_matrix();
    b.delete_matrix();
    c.delete_matrix();

    return 0;
}

Particularmente, todos los ejercicios no son nada difíciles de resolver, pero yo me estanqué en la moda y fue el problema que me hizo pensar un rato.

Lo resolví, pero sinceramente no me siento bien con la solución. No me gustó porque tiene una complejidad de O(n2) y con una matriz muy grande puede tarda mucho debido al número de recorridos y comparaciones.

La solución consistió en:

  • Crear otra matriz auxiliar con el mismo nro de filas columas y llenarla con ceros.
  • Recorrer la matriz original e ir comparando el primer elemento con todos los otros elementos de la matriz y contar sus repeticiones y así sucesivamente con los demás elementos.
  • Si encuentro una repetición, incremento en uno el valor en esa posición de la matriz auxiliar.
  • Luego hallar el mayor elemento de la matriz auxiliar.
  • Y finalmente recorrer la matriz auxiliar, cuando algún elemento de esta matriz sea igual al mayor elemento, quiere decir, que ese fue el elemento que más se repitió en la matriz original. Por lo tanto la moda se encuentra en esa posición.
Matriz original
17 16 10 26
10  3 9 19
29 7  1 6
6  20 0 10

Matriz auxiliar al inicio
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0

Matriz auxiliar despues de las comparaciones
con el nro de repeticiones de los valores
1 1 3 1
3 1 1 1
1 1 1 2
2 1 1 3

La moda de la matriz es: 10,
3 es el valor maximo que se repite en la matriz
Y en las posiciones en que se encuentra 3 se encuentra 10 en la matriz original.

Un tip que me dió el profe es reducir el nro de comparaciones creando una matriz booleana e ir poniendo unos y ceros a los números que ya comparé para luego no volver a comparar con los números que comparé anteriormente. Voy a seguir resolviendo el problema,si alguien tiene otra solución y puede explicarla fantástico.

El código fuente de este y otros ejercicios de C++ está disponible en Github: 

https://github.com/ronnyml/C---Tutorial

Gracias por tu visita al blog. Puedes seguirme en Twitter haciendo click en el siguiente enlace:

16 thoughts on “Operaciones con matrices – Clase Matriz en c++

  1. Buen blog, queria hacer una consulta:
    como se podria obtner los sub rectangulos de una matriz y su suma?

    osea una suma por cada sub rectangulo.

  2. man, esta excelente tu codigo. Estoy haciendo el mio para un ejercicio que tengo que hacer para la universidad y en varias cosas coincido contigo, pero algunas otras tengo errores. ¿Me podrias ayudar? ahi esta mi correo.

  3. Te queria agradecer por que me ayudaste a resolver un problema que yo tenia con la implementación de una clase Matriz también! =)
    Mi problema era que no sabia como “construir el objeto” en el main.cpp pero al ver como llamaste al constructor en tu código lo probé con mi Clase y vuala…

    Muchas Gracias!!

  4. Hola.
    Muy buen aporte!!!!
    Lo de la moda me rompió bien el craneo.
    A mi se me ha ocurrido lo siguiente:(compilado en DEV++).
    Lo que he hecho es un doble recorrido. Es decir tomo el primer M[i][j] de mi matriz, paso la posición i,j y me fabrico una función que me recorra otra vez la matriz, sacando así un contador siempre que coincida con el M[i][j] que he pasado. Luego lo que hago es comparar este contador. El problema me imagino que es el mismo en cuanto a tiempo de ejecución….

    #include
    #include
    using namespace std;
    const int COL = 3;

    int recorrerMatriz(int T[][COL],int,int ,int,int);

    // Funcion principal
    int i,j,Fila,contar,moda,max;
    //tenemos que poner un cin para poner las filas
    int M[Fila][COL];
    max=0;
    moda=0;
    //aqui va una funcion que rellena la matriz

    for (i=0;i<Fila;i=i+1)
    {
    for (j=0;j max)
    {
    max = contar;
    moda = M[i][j];
    }
    }
    }
    //FUNCIONES
    int recorrerMatriz(int T[][COL],int F,int C,int posI,int posJ)

    {int i,j,posicionI, posicionJ,contador;
    posicionI= posI;
    posicionJ= posJ;
    contador = 0;

    for (i=0;i<F;i=i+1){
    for (j=0;j<C;j=j+1){

    cout<< "el de ahora: "<< T[i][j]<<endl;
    cout<< "el mio "<< T[posicionI][posicionJ]<<endl;

    if (T[i][j]== T[posicionI][posicionJ]){
    contador = contador +1;
    cout<< "contador2: "<<contador<<endl;
    }
    }
    }
    cout<< "contador: "<<contador<<endl;
    return(contador);
    }

  5. Me encantan tus explicaciones, pero quisiera saber si me podes dar un ejemplo de una matriz más simple?

Leave a reply to Ronny Yabar Aizcorbe Cancel reply