PRÁCTICA 9: EJERCICIOS RESUELTOS.

Ejercicio 1: Norma sub-infinito de una matriz.
Ejercicio 2: Función para crear una matriz con reserva dinámica de memoria.
Ejercicio 3: Resolución de un sistema de ecuaciones lineales.
Ejercicio 4: Modifica el programa anterior.
Ejercicio 5: Resolución de sistemas de ecuaciones lineales con pivotamiento por columnas.
Ejercicio 6: Cálculo del determinante de una matriz nxn.
Ejercicio 7: Resolución de sistemas de ecuaciones lineales (Gauss-Seidel).
Ejercicio 8: Direcciona un vector de 1 a n (y no de 0 a n-1).
Ejercicio 9: Direcciona una matriz de 1 a n, y de 1 a m (y no de 0 a n-1, y de 0 a m-1).



Ejercicio 1: Norma sub-infinito de una matriz.

PrevioSiguienteTop

El módulo de un vector es una forma de indicar el tamaño de dicho vector mediante una única magnitud. Existe algo parecido para las matrices y es la norma de una matriz. Se pueden encontrar distintas definiciones para la norma de una matriz. Una de ellas, particularmente sencilla, es la llamada norma sub-infinito, que es el máximo valor de la suma de los valores absolutos de cada fila de la matriz. Dicho de otra forma: se calcula la suma de los valores absolutos de los elementos de cada fila, y se elige el valor más grande de entre todos los obtenidos.

Crea una función llamada norma_inf() que reciba como argumentos los números de filas y de columnas de la matriz, así como la propia matriz, que será de tipo double. Como valor de retorno se devolverá la norma sub-infinito de la matriz. Haz un programa principal que lea los datos y llame a la función norma_inf(). Guarda todo en un fichero llamado norma.c.

Solución comentada del Ejercicio 1.


La solución de este ejercicio se va a presentar conjuntamente con la del Ejercicio 2.



Ejercicio 2: Función para crear una matriz con reserva dinámica de memoria.

PrevioSiguienteTop

Es muy frecuente el tener que utilizar matrices con reserva dinámica de memoria. Por eso puede ser útil que te crees tu propia función para realizar esta tarea. Llámala ma_alloc(). Sus argumentos serán el número de filas y el número de columnas de la matriz (que podrán ser diferentes, en el caso de las matrices rectangulares). El valor de retorno será un puntero al primer elemento del vector de punteros de la matriz. La matriz será de tipo double. Guarda esta función junto con una función main() que te permita probarla en un fichero llamado ma_alloc.c.

Solución comentada de los Ejercicios 1 y 2.


A continuación se presenta un programa principal que llama a las funciones ma_alloc() y norma_inf(). La primera de ellas reserva dinámicamente memoria para una matriz double de m filas y n columnas. La segunda calcula la norma sub-infinito de dicha matriz. En aras de la brevedad del programa, se han suprimido los printf() que piden los datos al usuario.


/* fichero norma.c */
#include <stdio.h>
#include <math.h>
void main(void)         /* funcion o programa principal */
{
    int i, j, m, n;
    double **a;
    double **ma_alloc(int m, int n);
    double norma_inf(int m, int n, double **a);
    scanf("%d%d", &m, &n);
    a = ma_alloc(m, n);
    for(i=0; i<m; i++)
        for(j=0; j<n; j++)
            scanf(" %lf", &a[i][j]);
    printf("\nLa norma sub-infinito es: %lf", norma_inf(m, n, a));
}

Comentario: En la función main() no se reserva espacio para toda la matriz, sino sólo para el nombre de la matriz a, que como es sabido es un puntero a puntero a double. La función de reserva dinámica de memoria ma_calloc() devuelve también un puntero a puntero a double, valor de retorno que será asignado al nombre de la matriz cuando esta función sea llamada. En este ejemplo, la matriz puede ser rectangular, con m filas y n columnas.

Es interesante observar cómo se declara y cómo se llama la función norma_inf(). Su valor de retorno es de tipo double, pues va a devolver el valor de la norma. En la declaración no es necesario poner los nombres de las variables que constituyen los argumentos (basta poner los tipos; el compilador ignora los nombres). En concreto, hay que poner el tipo del nombre de la matriz, que es (double **); en la llamada, basta pasar el nombre de la matriz (a).

double **ma_alloc(int m, int n)
{
    double **matrix;
    int i;
    matrix = calloc(m, sizeof(double *));
    for(i=0; i<m; i++)
        matrix[i] = calloc(n, sizeof(double));
    return matrix;
}

Comentario: Esta función reserva memoria para una matriz de tipo double, de nombre matrix, con m filas y n columnas. Lo primero que se hace es declarar el nombre de la matriz como puntero a puntero a double. Después se reserva memoria para los m elementos del vector de punteros a double. El valor de retorno se asigna al nombre de la matriz. Después, con un bucle for se reserva memoria para cada una de las filas de la matriz. El espacio para los n elementos de cada fila se reserva con una nueva llamada a calloc(), por lo que no hay garantía de que las filas se guarden de modo contiguo (una a continuación de la otra).

double **ma_alloc(int m, int n)
{
    double **matrix;
    int i;
    matrix = calloc(m, sizeof(double *));
    matrix[0] =  calloc(m*n, sizeof(double));
    for(i=1; i<m; i++)
        matrix[i] = matrix[i-1] + n;
    return matrix;
}

Comentario: Esta nueva versión de la función ma_alloc() garantiza que las filas de la matriz están contiguas en la memoria del ordenador. Para ello, al igual que en el caso anterior, se comienza reservando memoria para el vector de punteros. Después se reserva memoria para toda la matriz en una sola llamada a calloc(). El valor de retorno se asigna al primer elemento del vector de punteros, que debe contener la dirección del primer elemento de la primera fila. Después, se asigna a cada elemento restante del vector de punteros la dirección del primer elemento de la fila correspondiente, que está n lugares más atrás que el primer elemento de la fila anterior. Esta operación se realiza teniendo en cuenta las propiedades de la aritmética de punteros. Finalmente, el valor de retorno

double norma_inf(int m, int n, double **a)
{
    double norma=0.0, suma;
    int i, j;
    for(i=0; i<m; i++) {
        suma =0.0;
        for(j=0; j<n; j++)
            suma += fabs(a[i][j]);
        if(norma < suma)
            norma = suma;
    }
    return norma;
}

Comentario: Esta función calcula el máximo de la suma de los valores absolutos de los elementos de cada fila. Se podría crear un vector que contuviera dichas sumas de valores absolutos de cada fila, pero no es necesario si lo único que se quiere es la suma máxima: se halla la suma de los valores absolutos de cada fila y se compara con norma, que contiene la suma máxima hallada hasta ese momento. Si norma es más pequeña que la última suma se actualiza norma y se sigue la comparación con la suma correspondiente a la fila siguiente. Al final se devuelve norma como valor de retorno.



Ejercicio 3: Resolución de un sistema de ecuaciones lineales.

PrevioSiguienteTop

Este ejercicio te lo vamos a hacer más fácil. Crea o copia del disco del servidor el fichero resolve.c que contiene un programa para resolver sistemas de hasta 10 ecuaciones lineales. El programa está basado en el uso de varias funciones distintas para leer los datos, hacer la triangularización, hacer la vuelta atrás, imprimir los resultados e incluso calcular el valor absoluto de una variable double. Fíjate muy bien en cómo son los argumentos formales (definición y declaración de la función) y los argumentos actuales (llamada), en el caso de las matrices y los vectores.

Podrás observar que la función triang() se asegura de que los pivots sean diferentes de cero. Prueba con dos o tres ejemplos sencillos de pequeño tamaño.

Solución comentada del Ejercicio 3.


/* fichero resolve.c */
#include <stdio.h>
#define   N     10
#define   EPS   1.e-12
void main(void)
{
     int    n, v_ret;
     double a[N][N], x[N], b[N];
     void   lect(int *n, double a[][N], double b[]);
     int    triang(int n, double (*a)[N], double *b);
     void   sust(int n, double a[][N], double b[], double x[]);
     void   escribir(int n, double x[]); 
     
     /* Lectura de datos */
     lect(&n, a, b);
     /* Triangularizacion de la matriz */
     v_ret = triang(n, a, b);
     if(v_ret != 1) {
          printf("Ha aparecido un pivot nulo o muy pequeño. Agur! \n");
          exit(1);
     }
     /* Vuelta atras */
     sust(n, a, b, x);
     /* Escritura de resultados */
     escribir(n, x); 
}


Comentario: En este caso se ha reservado memoria para la matriz a y para los vectores b y x de modo convencional, con un tamaño fijo de N=10. Es interesante observar cómo se realiza en este caso la declaración de las funciones que tienen vectores y matrices como argumentos formales. Para poder utilizar adecuadamente la fórmula de direccionamiento dentro de la función es necesario pasarle la información del número de columnas de la matriz (número de elementos que tiene cada fila); no es necesario pasar el número de filas porque no interviene en la fórmula citada. Por ejemplo, la función lect() se declara en la forma:

     void   lect(int *n, double a[][N], double b[]);

donde el número de ecuaciones n se pasa por referencia pues es un valor que la función debe leer y devolver al programa principal. Obsérvese que el primer par de corchetes de la matriz a está vacío, lo mismo que los del vector b, pues dicha información es innecesaria para el direccionamiento. La función triang() se ha declarado de una forma distinta, igualmente correcta.

     int    triang(int n, double (*a)[N], double *b);

En este caso es equivalente declarar b como un vector double o como un puntero a double. La variable a puede ser declarada como una matriz con N elementos por fila o como un puntero a una fila de N elementos (el paréntesis es necesario para que no se declare como un vector de N punteros a double). En este ejemplo se han presentado pues las dos formas posibles de introducir vectores y matrices como argumentos en las declaraciones de las funciones

void lect(int *n, double mat[][N], double rhs[]) 
{
     int i, j;
     printf("Numero de ecuaciones: ");
     scanf("%d", n);
     printf("\nMatriz del sistema:\n ");
     for (i=0; i<*n; i++)
          for(j=0; j<*n; j++) {
               printf("\na(%d, %d): ", i+j, j+1);
               scanf("%lf", &mat[i][j]);
          }
     printf("\nTermino independiente:\n ");
     for (i=0; i<*n; i++) {
          printf("\nb(%d): ", i+1);
          scanf("%lf", &rhs[i]);
     }
     return;
}

Comentario: Obsérvese que el número de ecuaciones n se ha pasado por referencia a la función lect(). Esto quiere decir que dentro de esta función n es la dirección del número de ecuaciones. Cuando se quiere hacer uso de dicho número, n debe ir precedido por el operador indirección, en la forma (*n). Por otra parte, para pasar a scanf() la dirección del número de ecuaciones no hace falta preceder n por el operador &.

int triang(int nec, double a[][N], double b[]) 
{
    int i, j, k, error;
    double fac;
    double va( double );
    for(k=0; k<nec-1; k++)
        for(i=k+1; i<nec; i++) {
            if(va(a[k][k]) < EPS) 
                return error = -1;
            fac = -a[i][k]/a[k][k];
            for(j=k; j<nec; j++)
                a[i][j] += a[k][j]*fac;
            b[i] += b[k]*fac;
       }
    return error=1;
}
void sust(int n, double a[][N], double b[], double sol[]) 
{
    int i, k;
    double sum;
    for(k=n-1; k>=0; k--) {
        sum=0.0;
        for(i=k+1; i<n; i++)
            sum += a[k][i]*sol[i];
        sol[k] = (b[k]-sum)/a[k][k];
    }
}
void escribir(int n, double solucion[]) 
{
    int i;
    printf("El vector solucion es: \n");
    for(i=0; i<n; i++)
         printf("solucion(%d)= %lf\n", i, solucion[i]);
}
double va( double u ) 
{
    if( u<0.0 )
        return -u;
    else
        return u;
}



Ejercicio 4: Modifica el programa anterior.

PrevioSiguienteTop

Modifica el programa anterior para que trabaje con reserva dinámica de memoria. Podrías utilizar la función que has programado en el Ejercicio 2. Compruébala con los mismos ejemplos que antes y asegúrate de que obtienes el mismo resultado. Guarda el resultado de este ejercicio en un fichero llamado resolve2.c.

Solución comentada del Ejercicio 4.


La solución de este ejercicio se presentará conjuntamente con la del Ejercicio 5.



Ejercicio 5: Resolución de sistemas de ecuaciones lineales con pivotamiento por columnas.

PrevioSiguienteTop

En este ejercicio te pedimos que vuelvas a modificar el programa anterior. Como habrás podido comprobar, los programas anteriores fallan si se encuentra un pivot nulo o muy pequeño. Ya has estudiado en Álgebra Lineal una forma de solventar esta dificultad: hacer pivotamiento. También sabes que si se permutan dos ecuaciones del sistema, la solución no varía.

El pivotamiento por columnas consiste en buscar el máximo elemento en valor absoluto de cada columna y utilizarlo como pivot. Al hacer esta búsqueda, no se deben considerar los elementos de esa columna situados en filas en las que han aparecido los pivots anteriores (así pues, excepto en el caso de la primera columna en que no hay pivots anteriores, no todos los elementos de la columna pueden ser pivots).

Te sugerimos dos formas de hacer este ejercicio. La primera de ellas consiste en guardar la posición (la fila) en que ha aparecido cada pivot. La segunda, que aprovecha mejor las características del lenguaje C, consiste en permutar filas de forma que el pivot siempre esté sobre la diagonal; de esta forma puedes aprovechar mejor el código del Ejercicio 3. Recuerda que para permutar las filas de una matriz basta con permutar el valor de los punteros que apuntan al primer elemento de cada fila. Recuerda también que para permutar ecuaciones correctamente debes permutar también los elementos del vector de términos independientes.

Te sugerimos que construyas una función llamada pivot() que calcule el máximo elemento en valor absoluto de una columna de la matriz, teniendo en cuenta los elementos de las filas donde han aparecido pivots en las columnas anteriores. El valor de retorno será la posición del pivot en la columna.

Guarda este ejercicio en un fichero llamado resolve3.c.

Solución comentada de los Ejercicios 4 y 5.


/* fichero resolve3.c */
#include  <stdio.h>
#include  <math.h>
#define   EPS   1.e-12
void main(void)
{
    int    i, j, n, v_ret;
    double **a, *x, *b;
    double **crear_matriz(int m, int n);
    int    triang(int n, double **a, double *b);
    int    pivot(int n, int k, double **a);
    void   sust(int n, double **a, double *b, double *x);
    void   escribir(int n, double *x); 
    /* Lectura de datos y creaci¢n dinamica de matrices y vectores */
    printf("Numero de ecuaciones: ");
    scanf("%d", &n);
    a = crear_matriz(n, n);  
    printf("\nMatriz del sistema:\n ");
    for (i=0; i<n; i++)
        for(j=0; j<n; j++) {
            printf("\na(%d, %d): ", i+1, j+1);
            scanf(" %lf", &a[i][j]);
        }
    b = calloc(n, sizeof(double));
    x = calloc(n, sizeof(double));
    printf("\nTermino independiente:\n ");
    for (i=0; i<n; i++) {
        printf("\nb(%d): ", i+1);
        scanf(" %lf", &b[i]);
    }
    /* Triangularizacion de la matriz */
    v_ret = triang(n, a, b);
    if (v_ret != 1) {
        printf("Ha aparecido un pivot nulo o muy peque¤o. Agur! \n");
        exit(1);
    }
    /* Vuelta atras */
   sust(n, a, b, x);
    /* Escritura de resultados */
    escribir(n, x); 
}    /* Fin de main() */
/* Funcion para hallar el pivot de la columna k */
int  pivot(int n, int k, double **a)
{
    int i, imaximo;
    double maximo = 0.0;
    double va(double);
    for (i=k; i<n; i++) {
        if (maximo < va(a[i][k])) {
            maximo = va(a[i][k]);
            imaximo = i;
        }
    }
    return imaximo;
}
/* Funcion para realizar la triangularizacion de una matriz */
int triang(int nec, double **a, double *b) 
{
    int i, j, k, ipivot, error;
    double fac, *temp;
    double va( double );
    for (k=0; k<nec-1; k++) {
        ipivot = pivot(nec, k, a);
        /* intercambio de los punteros a las filas k e imaximo */
        temp = a[k];
        a[k] = a[ipivot];
        a[ipivot] = temp;
        fac = b[k];
        b[k] = b[ipivot];
        b[ipivot] = fac;
        for (i=k+1; i<nec; i++) {
            if (va(a[k][k]) < EPS) 
                return error = -1;
            fac = -a[i][k]/a[k][k];
            for (j=k; j<nec; j++)
                a[i][j] += a[k][j]*fac;
            b[i] += b[k]*fac;
        }
    }
    return error=1;
}
void sust(int n, double **a, double *b, double *sol) 
{
    int i, k;
    double sum;
    for (k=n-1; k>=0; k--) {
        sum=0.0;
        for (i=k+1; i<n; i++)
            sum += a[k][i]*sol[i];
        sol[k] = (b[k]-sum)/a[k][k];
    }
}
double **crear_matriz(int m, int n)
{
    double **matriz, *mat;
    int i;
    matriz = calloc(m, sizeof(double *));
    matriz[0] = mat = calloc(m*n, sizeof(double));
    for(i=1; i<m; i++)
        matriz[i] = mat + n*i;
    return matriz;
}
void escribir(int n, double *solucion) 
{
    int i;
    printf("El vector solucion es: \n");
    for (i=0; i<n; i++)
        printf("solucion(%d)= %lf\n", i, solucion[i]);
}
double va( double u ) 
{
    if( u<0.0 )
        return -u;
    else
        return u;
}



Ejercicio 6: Cálculo del determinante de una matriz nxn.

PrevioSiguienteTop

Como sabes, el determinante de una matriz no cambia si se sustituye una ecuación por esa misma ecuación a la que se suma otra de las ecuaciones multiplicada por un factor cualquiera. Esto quiere decir que la triangularización de Gauss no cambia el valor del determinante. Ya sabes también que en una matriz triangularizada el determinante es simplemente el producto de los elementos de la diagonal.

Por otra parte, si se permutan dos filas de una matriz, el determinanate cambia de signo. Si quieres calcular el determinanate de una matriz mediante la triangularización de Gauss con pivotamiento, tendrás que llevar cuenta de las permutaciones de filas que has realizado.

Haz un programa principal llamado determin.c que lea una matriz cuadrada de cualquier tamaño y llame a la función determinante() que devuelve el valor de su determinante. El programa principal deberá ofrecer al usuario dos opciones para introducir los datos: bien por teclado bien leyéndolos de un fichero, en cuyo caso se pedirá el nombre del fichero que contiene la matriz.

Solución comentada del Ejercicio 6.


/* fichero determin.c */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define   EPS   1.e-12
void main(void)
{
    int    i, j, n, v_ret, signo=1;
    double **a, det;
    double **crear_matriz(int m, int n);
    int    triang2(int n, double **a, int *signo);
    int    pivot(int n, int k, double **a);
    /* Lectura de datos y creacion dinamica de matrices y vectores */
    printf("Numero de filas y de columnas: ");
    scanf("%d", &n);
    a =crear_matriz(n, n);  
    printf("\nMatriz del sistema:\n ");
    for (i=0; i<n; i++)
        for (j=0; j<n; j++) {
            printf("\na(%d, %d): ", i+1, j+1);
            scanf("%lf", &a[i][j]);
        }
/* Triangularizacion de la matriz */
    v_ret = triang2(n, a, &signo);
    if (v_ret != 1) {
        printf("El determinante es cero o muy pequeño. Agur! \n");
        exit(1);
    }
    /* Calculo del determinante */
    det = 1.0;
    for (i=0; i<n; i++)
         det *= a[i][i]; 
    det *= signo;
    printf("\n\nEl determinante de la matriz es %lf", det);
}
/* Funcion para hallar el pivot de la columna k */
int  pivot(int n, int k, double **a)
{
    int    i, imaximo;
    double maximo = 0.0;
    double va(double);
    for (i=k; i<n; i++) {
        if (maximo < va(a[i][k])) {
            maximo = va(a[i][k]);
            imaximo = i;
        }
    }
    return imaximo;
}
/* Funcion para realizar la triangularizacion de una matriz */
int triang2(int nec, double **a, int *signo) 
{
    int i, j, k, ipivot, error;
    double fac, *temp;
    double va(double);
    
    for (k=0; k<nec-1; k++) {
        ipivot = pivot(nec, k, a);
        /* intercambio de los punteros a las filas k e imaximo */
        if (ipivot != k) {
               temp = a[k];
               a[k] = a[ipivot];
               a[ipivot] = temp;
               *signo = - *signo;
        }
        for (i=k+1; i<nec; i++) {
            if (va(a[k][k]) < EPS) 
                return error = -1;
            fac = -a[i][k]/a[k][k];
            for (j=k; j<nec; j++)
                a[i][j] += a[k][j]*fac;
        }
     }
     return error=1;
}
double **crear_matriz(int m, int n)
{
    double **matriz, *mat;
    int i;
    matriz = malloc(m*sizeof(double *));
    matriz[0] = mat = malloc(m*n*sizeof(double));
    for(i=1; i<m; i++)
        matriz[i] = mat + n*i;
    return matriz;
}
double va(double u) 
{
    if(u<0.0)
        return -u;
    else
        return u;
}



Ejercicio 7: Resolución de sistemas de ecuaciones lineales por el método iterativo de Gauss-Seidel.

PrevioSiguienteTop

El método de Gauss no es el único método para resolver sistemas de ecuaciones lineales (aunque sí el más eficiente en la mayor parte de los casos). El mismo Gauss se inventó otro método para resolver sistemas de ecuaciones lineales de modo iterativo, al parecer con un amigo llamado Seidel del que no se tienen muchos más datos.

El método de Gauss-Seidel procede del siguiente modo: supongamos un sistema de tres ecuaciones con tres incógnitas:

Si suponemos que los elementos de la diagonal son mayores que los demás elementos de una fila, una primera aproximación de la solución podría ser la siguiente:

donde con el superíandice (1) indicamos que se trata de la primera aproximación.

La segunda aproximación la podríamos calcular a partir de la primera utilizando el sistema original. Para ello haríamos:

Observa que x1(2) (segunda aproximación de x1) se obtiene a partir de la primera aproximación de x2 y x3; x2(2) se obtiene a partir de la segunda aproximación de x1 (que ya está disponible) y de la primera aproximación de x3 (pues aún no se dispone de la segunda); la segunda aproximación x3 se obtiene a partir de la segunda aproximación de x1 y x2 , que ya están disponibles.

En general:

Generaliza estas expresiones para el caso de sistemas de n ecuaciones con n incógnitas y haz un programa que realice estas iteraciones hasta que el error relativo entre dos iteraciones consecutivas sea menor que una constante EPS, que harás igual a 10-8. El error relativo lo tendrás que medir del siguiente modo:

Guarda el programa en un fichero llamado gseidel.c y pruébalo con el siguiente sistema de ecuaciones:

Comprueba que obtienes el mismo resultado que con el método que inventó Gauss solo (sin Seidel).

Solución comentada del Ejercicio 7.


/* fichero gseidel.c */
#include <stdio.h>
#define  EPS   10.e-12
void main(void)
{
    double a[10][10], x[10], b[10], xn;
    double error, modulo, suma;
    int n, i, j, nit=0;
    printf("\nNúmero de ecuaciones: ");
    scanf("%d", &n);
    for (i=0; i<n; i++) {
        for (j=0; j<n; j++) {
            printf("\nElemento a(%d,%d): ", i+1, j+1);
            scanf(" %lf", &a[i][j]);
        }
    }
    for (i=0; i<n; i++) {
        printf("\nElemento b(%d): ", i+1);
        scanf(" %lf", &b[i]);
    }
 
    /* valores para iteración inicial */
    for (i=0; i<n; i++)
        x[i] = b[i]/a[i][i];
    do {
        error = 0.0;
        modulo = 0.0;
        nit++;
        for(i=0; i<n; i++) {
             suma = 0.0;
            for (j=0; j<n; j++)
                if (j!=i)
                    suma += a[i][j]*x[j];
            xn = (b[i] - suma)/a[i][i];
            error += (xn - x[i]) * (xn - x[i]);
            x[i] = xn;
            modulo += xn * xn;
        }
    } while (abs(error/modulo)>EPS);
    printf("\nNúmero de iteraciones: %d", nit);    
    printf("\nEl vector solución es:");
    for (i=0; i<n; i++)
        printf("\nx(%d) = %lf", i+1, x[i]);
}



Ejercicio 8: Direcciona un vector de 1 a n (y no de 0 a n-1).

PrevioSiguienteTop

El C tiene algunas características un poco incómodas, pero es casi siempre lo suficientemente versátil como para permitir superar esos inconvenientes. Una forma de hacerlo es reservar espacio para n+1 elementos (del 0 al n) y no utilizar el primero. Aparte de que esta solución no optimiza el uso de la memoria, hay soluciones más elegantes que se basan en la aritmética de los punteros: Si disminuimos en una unidad el valor del puntero que apunta al primer elemento del vector (es decir, el nombre del vector, que supondremos es vector), cuando utilicemos vector[1] estamos direccionando igual que si hiciésemos *(vector+1), con lo cual apuntamos al primer elemento del vector. La siguiente función indica cómo se crea un vector de estas características. Crea una programa main() que reserve espacio para dos vectores llamando a crear_vector() y halle el producto escalar de estos dos vectores. Guarda el programa resultante en un fichero llamado vector1n.c.

Solución comentada del Ejercicio 8.


/* fichero vector1n.c */
double *crear_vector(int n)
{
    int    i;
    double *vector;
    vector = calloc(n, sizeof(double));
    return --vector;
}



Ejercicio 9: Direcciona una matriz de 1 a n, y de 1 a m (y no de 0 a n-1, y de 0 a m-1).

PrevioSiguienteTop

También pueden crearse matrices de forma que sus elementos se direccionen a partir de 1 y no a partir de 0. La función crear_matriz() lo hace de esta forma. La función destruir_matriz() libera el espacio de memoria correspondiente cuando ya no es necesario. Crea un fichero llamado mat1n1m.c que contenga estas funciones y un programa main() que las utilice.

Solución comentada del Ejercicio 9.


/* fichero mat1n1m.c */
double **crear_matriz(int m, int n)
{
    int    i;
    double **matriz;
    matriz = calloc(m, sizeof(double *));
    --matriz;
    for (i=1; i<=m; i++) {
        matriz[i] = calloc(n, sizeof(double));
        --matriz[i];
    }
    return matriz;
}

Comentario: La idea es decrementar el puntero que es el nombre de la matriz, de forma que apunta al valor anterior al primer elemento del vector de punteros. Para acceder a este primer elemento habrá que utilizar el subíndice 1. De la misma forma hay que decrementar (asimismo según la aritmética de los punteros) los elementos del vector de punteros que apuntan a los primeros elementos de cada fila de la matriz. Para acceder a dichos elementos habrá queutilizar el subíndice incrementado en una unidad.

void destruir_matriz(double **matriz, int m)
{
    int i;
    for (i=1; i<=m; i++)
        free(++matriz[i]);
    free(++matriz);
}

Comentario: De forma análoga, hay que incrementar los punteros antes decrementados para liberar correctamente la zona de memoria correspondiente.

void main(void)
{
    double **a;
    int    n, i, j;
    printf(\nTeclea el valor de n: ");
    scanf("%d", &n);
    a = crear_matriz(n, n);
    for (i=1; i<=n; i++)
        for (j=1; j<=n; j++) {
            printf("\nTeclee a(%d, %d): ", i, j);
            scanf(" %lf", &a[i][j]);
        }
    for (i=1; i<=n; i++) {
        for (j=1; j<=n; j++){
            printf("   a(%d, %d) = ", i, j);
                printf("%lf ", a[i][j]);
          }
        printf("\n");
    }
    destruir_matriz(a, n);
}

Comentario: La función main() simplemente lee la matriz y la vuelve a escribir, para comprobar que el direccionamiento comenzando por 1 funciona correctamente..