Problemas para rellenar la matriz

Iniciado por 4PR3ND1Z, 28 Febrero 2019, 09:42 AM

0 Miembros y 1 Visitante están viendo este tema.

4PR3ND1Z

Buenos días. El problema me surge a la hora de rellenar la matriz puesto que me pide dos veces que ingrese los datos. Os adjunto el ejercicio entero por si alguien quisiera echarle una ojeada.

Citar
/* 15. Desarrollar una función que determine si una matriz es simétrica o no.

Una matriz es simétrica: A = A^t

- La matriz debe ser cuadrada.
- Aij = Aji

|2   5   9|                       |2   5   9|
|5   8   -1|          ->          |5   8   -1|
|9   -1   10|                     |9   -1   10|

*/

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

using namespace std;

void pedirDatos();
void matrizCuadrada(int, int);
void rellenarMatriz(int m[][10], int, int);
void mostrarMatriz(int m[][10], int, int);
void matrizSimetrica(int m[][10], int, int);

int m[10][10], nFil, nCol;

int main(){
   
   pedirDatos();
   matrizCuadrada(nFil, nCol);
   cout<<"\n";
   rellenarMatriz(m, nFil, nCol);
   mostrarMatriz(m, nFil, nCol);
   matrizSimetrica(m, nFil, nCol);
   cout<<"\n\n";
   
   system("pause");
   return 0;
}

void pedirDatos(){
   
   cout<<"Introduce el numero de filas de la matriz: ";
   cin>>nFil;
   cout<<"\nIntroduce el numero de columnas de la matriz: ";
   cin>>nCol;
}

void matrizCuadrada(int nFil, int nCol){
   
   if(nFil != nCol){
      cout<<"\nLa matriz debe ser cuadrada (ej. 4x4)";
      cout<<"\n\n";
      pedirDatos();
   }
   else rellenarMatriz(m, nFil, nCol);
}

void rellenarMatriz(int m[][10], int nFil, int nCol){
   
   for(int i=0; i<nFil; i++){
      for(int j=0; j<nCol; j++){
         cout<<"Introduce un numero para |"<<i<<"| |"<<j<<"|: ";
         cin>>m[j];
      }
   }
}

void mostrarMatriz(int m[][10], int nFil, int nCol){
   
   cout<<"\n\nLa matriz queda compuesta de la siguiente manera: ";
   for(int i=0; i<nFil; i++){
      cout<<"\n";
      for(int j=0; j<nCol; j++){
         cout<<"|"<<m[j]<<"|";
      }
   }
}

void matrizSimetrica(int m[][10], int nFil, int nCol){
   int cont = 0;
   
   for(int i=0; i<nFil; i++){
      for(int j=0; j<nCol; j++){
         if(m[j] == m[j]){
            cont++;
         }
      }
   }
   
   if(cont == (nFil * nCol)){
      cout<<"\nLa matriz es simetrica.";
   }
   else{
      cout<<"\nLa matriz no es simetrica.";
   }
}

Gracias por tu tiempo.

K-YreX

Te recomiendo que no uses variables globales ya que no es muy recomendado para casos como este... Además de que estás usando la mayoría de funciones con parámetros (excepto <pedirDatos()>) así que con cambiar esa por:
Código (cpp) [Seleccionar]

void pedirDatos(int &nFil, int &nCol);


Además hay un problema en <matrizCuadrada()>. Imagina que introduces <nFil> != <nCol>... Te dirá que la matriz debe ser cuadrada y te volverá a pedir los datos pero no la construirá. La forma más común de resolver esto es usar un filtro. Te dejo un ejemplo para que tú lo adaptes a tu código.
Código (cpp) [Seleccionar]

do{
    cout << "Introduce un numero positivo: ";
    cin >> positivo;
}   while(positivo < 0);
// una vez salgas del bucle el numero sera positivo


Ahora imagina el otro caso, a la primera introduces unas dimensiones iguales (<nFil> == <nCol>)... Primero se llamará a la función <rellenarMatriz()> que está en el <else> de la función <matrizCuadrada()> y después a la función <rellenarMatriz()> que está en el <main>. Por eso te pide los valores dos veces.

Unas recomendaciones de diseño, no de funcionalidad:
  • Sustituye el <system("pause")> por <cin.get()>. Va a hacer lo mismo y evitas una llamada al sistema.
  • Las librerías que acaban en ".h" (stdlib.h) tienen su propia versión de C++ sin el ".h" y empezando por "c" (cstdlib).
  • Y como he dicho al principio, evitar el uso de variables globales.

    Si ya de paso cambias las etiquetas de código por las de C++ mejor. Ya que las <i> entre corchetes [] no aparecen ya que se confunden con la etiqueta de letra cursiva y el código se hace más complicado de interpretar... :-X
Código (cpp) [Seleccionar]

cout << "Todos tenemos un defecto, un error en nuestro código" << endl;

4PR3ND1Z

Unos consejos geniales. Tomaré nota.

¿A qué te refieres con cambiar la función void pedirDatos() por void pedirDatos(int &nFil, int &nCol)?

Imagino que te refieres al paso de parámetros por referencia para así poder incluir las variables dentro del main.

Bien detectado el fallo de control típico de un novato (más aún si cabe  :xD) en la función matrizCuadrada.

Cómo no se iba a repetir si efectivamente tenía llamadas a la función en el main y en la propia función...  :-\

Perdona si he cometido errores en la publicación, aún tengo que familiarizarme.  :-\

4PR3ND1Z

Este sería el resultado tras aplicar los cambios. Muchas gracias.


/* 15. Desarrollar una función que determine si una matriz es simétrica o no.

Una matriz es simétrica: A = A^t

- La matriz debe ser cuadrada.
- Aij = Aji

|2   5   9|                       |2   5   9|
|5   8   -1|          ->          |5   8   -1|
|9   -1   10|                     |9   -1   10|

*/

#include <iostream>
#include <cstdlib>

using namespace std;

void pedirDatos(int&, int&);
void matrizCuadrada(int m[][10], int&, int&);
void rellenarMatriz(int m[][10], int&, int&);
void mostrarMatriz(int m[][10], int&, int&);
void matrizSimetrica(int m[][10], int&, int&);

int main(){
int m[10][10], nFil, nCol;

pedirDatos(nFil, nCol);
matrizCuadrada(m, nFil, nCol);
cout<<"\n";
mostrarMatriz(m, nFil, nCol);
matrizSimetrica(m, nFil, nCol);
cout<<"\n\n";

cin.get(); //system("pause");
return 0;
}

void pedirDatos(int& nFil, int& nCol){

cout<<"Introduce el numero de filas de la matriz: ";
cin>>nFil;
cout<<"\nIntroduce el numero de columnas de la matriz: ";
cin>>nCol;
}

void matrizCuadrada(int m[][10], int& nFil, int& nCol){

do{
cout<<"\nLa matriz debe ser cuadrada (ej. 4x4)";
cout<<"\n\n";
pedirDatos(nFil, nCol);
}while(nFil != nCol);

rellenarMatriz(m, nFil, nCol);
}

void rellenarMatriz(int m[][10], int& nFil, int& nCol){

for(int i=0; i<nFil; i++){
for(int j=0; j<nCol; j++){
cout<<"Introduce un numero para |"<<i<<"| |"<<j<<"|: ";
cin>>m[i][j];
}
}
}

void mostrarMatriz(int m[][10], int& nFil, int& nCol){

cout<<"\n\nLa matriz queda compuesta de la siguiente manera: ";
for(int i=0; i<nFil; i++){
cout<<"\n";
for(int j=0; j<nCol; j++){
cout<<"|"<<m[i][j]<<"|";
}
}
}

void matrizSimetrica(int m[][10], int& nFil, int& nCol){
int cont = 0;

for(int i=0; i<nFil; i++){
for(int j=0; j<nCol; j++){
if(m[i][j] == m[j][i]){
cont++;
}
}
}

if(cont == (nFil * nCol)){
cout<<"\nLa matriz es simetrica.";
}
else{
cout<<"\nLa matriz no es simetrica.";
}
}

K-YreX

Ahora ya funciona pero si te has dado cuenta, pides los datos dos veces seguidas. Una vez en el <main> y otra vez en la función <matrizCuadrada()>...
Te comento las diferencias entre el filtro <do while> y el <while> ya que esto se puede hacer de ambas formas.

Un bloque de código dentro de un <do while> siempre se ejecuta al menos una vez aunque la condición sea falsa ya que primero está el <do> y luego el <while>. Es como "primero haces y luego compruebas". En cambio un <while> es justo lo contrario; "primero compruebas y luego ya si eso, haces".

Aquí te muestro un bloque infinito. ¿Por qué si la condición de primeras no se cumple? Pues porque primero mostramos el 0, luego lo incrementamos <iteracion = 1> y luego comprobamos si es mayor que 0. Y como sí que lo es pues ya se ejecuta de forma infinita.
Código (cpp) [Seleccionar]

int iteracion = 0;
do{
    cout << iteracion << endl;
    iteracion++;
}   while(iteracion > 0);


Lo mismo con un <while>. La parte interna del bucle nunca se ejecuta. Al principio iteracion vale 0, comprobamos si es mayor que 0 y como no lo es, se acabó.
Código (cpp) [Seleccionar]

int iteracion = 0;
while(iteracion > 0){
    cout << iteracion << endl;
    iteracion++;
}


Entonces ahora te muestro las opciones que tienes:
  • Alternativa 1: Pedir los datos únicamente dentro de <matrizCuadrada()>. Entonces sería con un <do while>.
    Código (cpp) [Seleccionar]

    void matrizCuadrada(int m[][TAM], int &nFil, int &nCol){
        do{
            pedirDatos(nFil, nCol);
        }   while(nFil != nCol);
        // el resto de la funcion
    }


  • Alternativa 2: Pedir los datos en el <main> y dentro de la función <matrizCuadrada()> comprobarlos para que si no son iguales los pida de nuevo.
    Código (cpp) [Seleccionar]

    void matrizCuadrada(int m[][TAM], int &nFil, int &nCol){
        while(nFil != nCol)
            pedirDatos(nFil, nCol);
        // el resto de la funcion
    }


  • Alternativa 3: La más correcta creo yo. Para no tener que ir pasando siempre las dimensiones por referencia, que podrías acabar modificándolas sin querer; haz el filtro dentro de <pedirDatos()> y así te aseguras que cuando acaba esa función la matriz ya es cuadrada.
    Código (cpp) [Seleccionar]

    void pedirDatos(int &nFil, int &nCol){
        do{
            cout << "Introduce las dimensiones de la matriz (NxN): ";
            cin >> nFil >> nCol;
        }   while(nFil != nCol);
    }

    Con esta última opción ya no tienes que usar siempre el paso por referencia y así evitar errores mayores.

    Es más, yo dejaría el esquema general del programa en algo así :-X:
    Código (cpp) [Seleccionar]

    void pedirDatos(int&, int&);
    void generarMatriz(int [][TAM], const int, const int);
    void mostrarMatriz(const int [][TAM], const int, const int);
    bool esSimetrica(const int [][TAM], const int, const int);

    En el caso de <esSimetrica()>. Cuando se usa una función para comprobar algo es mejor que devuelva <true/false> y luego tú ya verás lo que haces con ello. Imagina un programa muy grande donde tienes que comprobar si 100 matrices son simétricas y vas mostrando por pantalla el resultado de cada una, cuando no es necesario.
    Entonces es mejor hacer que devuelva un <bool> de si es simétrica o no y ya en el <main> haces lo que quieras. Algo así:
    Código (cpp) [Seleccionar]

    bool esSimetrica(const int matriz[][TAM], const int nFil, const int nCol){
        bool simetrica = true;
        for(size_t i = 0; i < nFil && simetrica; i++)
            for(size_t j = 0; j < nCol && simetrica; j++)
                simetrica = matriz[i][j] == matriz[j][i];
        return simetrica;
    }

    int main(){
        // todo el programa
        if(esSimetrica(matriz, nFil, nCol))
            cout << "La matriz es simetrica" << endl;
        else
            cout << "La matriz no es simetrica" << endl;
    }
Código (cpp) [Seleccionar]

cout << "Todos tenemos un defecto, un error en nuestro código" << endl;

4PR3ND1Z

Excelente explicación. Poco más y me haces un programa nuevo.  :laugh:
Claro yo tenía entendido que si introducía las variables dentro del main, en caso de mandarlas a las funciones debía hacerlo siempre por referencia. Otra cosa que me acabas de enseñar es a usar más el const int una vez tengo definidos los valores para los enteros. Lo que no termino de entender es TAM. Entiendo que C++ pide al menos un tamaño máximo de columnas y también he visto como se pueden predefinir pero en este caso que no van predefinidas no le encuentro el sentido. Me imagino que habrás definido int TAM = 10; int m[][TAM];

Por cierto, no sé como organizarme ahora que empiezo a ver temas un poco más complejos. Me gustaría guardar tanta información como sea posible y no sé por donde empezar ¿Qué métodos usas?
Estoy siguiendo un videotutorial de YT para que te hagas una idea.  :-\

Gracias de nuevo por tu tiempo y por tan extensas explicaciones de calidad. De mayor quiero expresarme como tú.  ;-)

K-YreX

Primero: lo de usar <TAM> es para evitar "números mágicos" (magic numbers) que son números que aparecen por el código literalmente y que es difícil ver a qué están haciendo referencia. Siempre es mejor guardar esos valores en constantes y así en el caso de querer modificar el tamaño/alcance del programa, sólo tendrás que modificar el valor de la constante.
Te muestro un código muy sencillo donde guardamos un array y luego lo mostramos. Imagina el caso que queremos que el programa en vez de almacenar 10 valores, ahora queremos que almacene un máximo de 20... En el primer código tendríamos que cambiar cada 10 por 20; mientras que en el segundo bastaría con cambiar el valor de <TAM>
Código (cpp) [Seleccionar]

// version 1 usando numero magicos
int main(){
    int my_array[10];
    for(size_t i = 0; i < 10; i++){
        cout << "Elemento " << i+1 << ": ";
        cin >> my_array[i];
    }
    for(size_t i = 0; i < 10; i++)
        cout << my_array[i] << endl;
}

// version 2 usando constantes
const int TAM = 10;
int main(){
    int my_array[TAM];
    for(size_t i = 0; i < TAM; i++){
        cout << "Elemento " << i+1 << ": ";
        cin >> my_array[i];
    }
    for(size_t i = 0; i < TAM; i++)
        cout << my_array[i] << endl;
}

Otra buena utilidad es cuando todavía no sabemos usar memoria dinámica, es decir; que si queremos que el usuario introduzca n datos pero no sabemos cuánto va a valer n hasta el tiempo de ejecución, podemos crear un máximo para declarar el array y luego controlar la cantidad de valores que va a usar el usuario con otra variable. Se desperdicia memoria pero es la forma de hacerlo con memoria estática. Te dejo aquí un ejemplo:
Código (cpp) [Seleccionar]

const int TAM_MAX = 100;
int main(){
    int my_array[TAM_MAX];
    int tam_util;
    cout << "Numero de datos a almacenar: ";
    cin >> tam_util;
    for(size_t i = 0; i < tam_util; i++){ // no usamos TAM_MAX, sino tam_util
        // el resto del bloque
    }
}



Respecto al tema del aprendizaje, al final lo importante es que vayas practicando todo lo que vas viendo, si te limitas a ver códigos hechos nunca sabrás tratar con los errores que te van a surgir cuando seas tú quien los escriba. Siempre habrá temas más importantes que vas a usar en prácticamente todos los programas que hagas y otras cosas que no usarás casi nunca. Para empezar a programar tampoco necesitas haber memorizado TODO antes. Es suficiente con que sepas qué cosas se pueden hacer y cuando necesites usar algo poco común siempre podrás buscar por internet o en libros cómo se usaba eso que quieres implementar.
Normalmente los tutoriales que van por "capítulos" suelen ir bien estructurados para que la curva de aprendizaje no sea muy grande y te van metiendo conceptos poco a poco. Simplemente seguir aprendiendo e ir probando todo lo que vas viendo. Suerte :-X
Código (cpp) [Seleccionar]

cout << "Todos tenemos un defecto, un error en nuestro código" << endl;