3.4 Matrices

Hasta ahora, vimos los vectores, que son variables compuestas que pueden contener varios valores del mismo tipo simultáneamente. Un vector puede verse como un agrupamiento lineal de variables del mismo tipo, en la que cada una tiene una posición dentro de la línea:

    
       int v[6];

Este vector puede representarse gráficamente como:


Este agrupamiento lineal o unidimensional puede extenderse fácilmente a dos dimensiones, en las que una dimensión puede interpretarse como la cantidad de elementos y la otra como la cantidad de componentes en cada elemento. Este agrupamiento se llama matriz, y se declara como:
 

        int m[4][6];


En esta matriz podemos guardar hasta 24 valores enteros. Estos valores están ordenados lógicamente como una tabla con cuatro filas de 6 elementos cada una. (Esta estructura es sólo una estructura lógica, físicamente la matriz se guarda en 24 posiciones consecutivas de la memoria)






Cada elemento puede accederse individualmente indicando sus dos subíndices, en lugar de uno solo. Así, la expresión:

       cout << m[2][5];


mostrará por pantalla el valor 23, que es el dato guardado en la celda [2][5] de la matriz.

Para trabajar con matrices, necesitaremos utilizar dos subíndices, uno para cada dimensión.



#include "stdafx.h"
#include <iostream>
using namespace std;

#define F 3
#define C 2   // DEFINO LA CANTIDAD DE FILAS Y COLUMNAS

void main()
{
       int i,j;
       int m[F][C];

       // CARGAR UNA MATRIZ

       for (i=0; i<F; i++)     // Con i recorro las filas
             for (j=0; j<C; j++)   // Para cada valor de i, recorro todas las columnas
             {
                    cout<< "Ingrese el elemento de la posicion "<< i <<","<< j<<":";
                    cin >> m[i][j];
             }

       // MOSTRAR UNA MATRIZ

    for (i=0; i<F; i++)
       {
             for (j=0; j<C; j++)
             {
                    cout << m[i][j] <<"  ";
             }
             cout<< endl;   // Cuando termino una fila, antes de pasar a la siguiente, debo ingresar un endl
       }

   cin >> i;
}


En este ejemplo, primero cargamos una matriz con valores enteros, y luego mostramos dichos valores.

En este ejemplo y los que siguen, trabajaremos con matrices de dos dimensiones, que pueden imaginarse como tablas. Todo esto es extensible a más dimensiones. Aunque no sea lo común, las matrices en C++ puedn tener hasta 16 dimensiones.


3.5 Operaciones con matrices y vectores

Vamos a ver con un ejemplo algunas de las principales aplicaciones de matrices. Consideremos el siguiente problema:
Un comercio tiene 5 vendedores, cada uno de los cuales registrado con su número de legajo y DNI. Cada vendedor realiza ventas a través de alguna de las tres cajas que posee el comercio. Se quiere un programa que permita al usuario las siguientes opciones:
- Ingresar una venta: Se ingresa el legajo del vendedor, el número de caja y el importe vendido.
- Total por caja: Al final del día, se quiere saber el total facturado en cada caja, para facilitar el arqueo correspondiente.
- Vendedores: Listado de ventas por vendedor, ordenado por cantidad vendida de mayor a menor.

Veremos las funciones que ejecutan estas opciones. En el caso de la primera (ingresar una venta) usamos la función siguiente:



void ingresar_venta(float m[][C], int leg[], int f, int c)
{
       int legajo,pos,caja;
       float venta;
       do
       {
           cout<<"Ingrese legajo del vendedor:";
           cin >> legajo;
             pos = buscar(leg, f, legajo);
       }
       while (pos == -1);
      
       do
       {
           cout<<"Ingrese numero de caja:";
           cin >> caja;
       }
       while (caja < 0 || caja >= c);
      
       cout << "Ingrese monto de la venta:";
       cin >> venta;

       m[pos][caja] = m[pos][caja]+venta;
}

El usuario ingresa un legajo. Mediante la función buscar que vimos anteriormente, obtengo la posición del vector en la que está dicho legajo, y me aseguro de que sea un legajo existente o lo vuelvo a pedir.
Luego ingreso la caja por la que se vendió (me aseguro de que sea un número válido) y el monto de la venta.
En este ejemplo, el vector de legajos está usado como índice de la matriz: No me importa en qué fila de la matriz está un dato, sé que corresponde al vendedor cuyo legajo esté en la misma posición. Así, por ejemplo la fila [0] de la matriz puede corresponder al legajo 1.230.
Distinto es el caso de las columnas, donde la columna [0] corresponde a la caja 0 y no se puede cambiar.
 Finalmente, la matriz la estoy usando como acumulador: Un vendedor puede realizar varias ventas distintas en la misma caja, y debo ir sumando sus importes para calcular el total.

Otras operaciones con matrices las podemso ver en el segundo punto:



void total_sucursal(float m[][C], float ts[], int f, int c)
{
       int i,j;
       // Inicializo el vector en cero
       for (j=0; j<c; j++)
             ts[j]=0;
       // Acumulo cada fila de la matriz en la posición correspondiente del vector
       for (i=0; i<f; i++)
             for (j=0; j<c; j++)
                    ts[j] = ts[j] + m[i][j];
       for (j=0; j<c; j++)
                    cout <<"La caja "<<j<< " ha facturado "<< ts[j] <<" pesos." <<endl;
}


En este caso, cada posición del vector ts está acumulando el total de las ventas de la fila. Este uso se llama vector acumulador o totalizador.

Si vemos el último punto:



void listado_vendedores(float m[][C], float tv[], int leg[], long dni[], int f, int c)
{
       int i,j,k,auxi;
       float auxf;
       long auxl;
       for (i=0; i<f; i++)
             tv[i]=0;
       for (i=0; i<f; i++)
             for (j=0; j<c; j++)
                    tv[i] = tv[i] + m[i][j];

       for (i=0; i<f-1; i++)
             for (k=i+1; k<f; k++)
                    if (tv[i] < tv[k])   //Si voy a intercambiar debo intercambiar no sólo el vector
                    {
                           auxf = tv[i];
                           tv[i] = tv[k];
                           tv[k] = auxf;

                           for (j=0; j<c; j++)
                           {
                                  auxf = m[i][j];         // También las filas correspondientes de la matriz
                                  m[i][j] = m[k][j];
                                  m[k][j] = auxf;
                           }
                           auxi = leg[i];              // Y los vectores de legajo y DNI
                           leg[i] = leg[k];
                           leg[k] = auxi;

                           auxl = dni[i];
                           dni[i] = dni[k];
                           dni[k] = auxl;
                    }
       cout <<endl<<" LEGAJO   DNI    TOTAL VENDIDO"<<endl;
       for (i=0; i<f; i++)
       {
             cout <<"  "<<leg[i]<<"    "<<dni[i]<<"       "<<tv[i]<<endl;
       }
}


Hace lo mismo, pero con una complejidad adicional: El listado debe estar ordenado por total vendido. Debo ordenar el vector totalizador, pero cuando tengo que intercambiar dos posiciones del vector, también debo intercambiar toda la información asociada: las posiciones respectivas del vector de legajos que actúa como índice, del vector de dni, y las filas correspondientes de la matriz, sino la información resultante sería inconsistente.

Estas son algunas formas fundamentales en las que se puede trabajar con vectores y matrices combinados, hay muchas más, pero casi todos los problemas que surgen en las aplicaciones reales pueden solucionarse con estas técnicas.

Para completar el ejercicio, agregamos el programa principal:



#include "stdafx.h"
#include <iostream>
using namespace std;

#define F 5
#define C 3

void ingresar_venta(float [][C], int[], int, int);
void total_sucursal(float [][C], float[], int, int);
void listado_vendedores(float[][C], float[], int[], long[], int, int);
int buscar(int[], int, int);

void main()
{
       int i,j,op;
       float m[F][C];
       int leg[F];
       long dni[F];
       float TotVend[F];
       float TotSuc[C];

       for(i=0; i<F; i++)
                    TotVend[i]=0;
       for(j=0; i<C; j++)
                    TotSuc[j]=0;
       for(i=0; i<F; i++)
             for(j=0; j<C; j++)
                    m[i][j]=0;
       for(i=0; i<F; i++)
       {
             cout <<"Ingrese legajo y DNI de un vendedor:";
             cin>>leg[i]>>dni[i];
       }
       do
       {
             cout<<"MENU PRINCIPAL"<<endl<<endl;
             cout<<"1-Ingresar una venta"<<endl;
             cout<<"2-Total por sucursal"<<endl;
             cout<<"3-Listado de vendedores"<<endl;
             cout<<"4-Salir."<<endl;
             cin>>op;
             switch(op)
             {
             case 1: ingresar_venta(m,leg,F,C);
                                  break;
             case 2: total_sucursal(m,TotSuc,F,C);
                    break;
             case 3: listado_vendedores(m,TotVend,leg,dni,F,C);
                    break;
             case 4: break;
             default: cout<<"Opcion Incorrecta."<<endl;
             }
       }
       while (op!=4);
}


 Este programa resuelve cada punto con llamadas a funciones específicas. Todo está organizado en un menú que utiliza la instrucción de selección múltiple switch.