EXAMEN FINAL: EJERCICIOS RESUELTOS

Se presentan resueltos a continuación los 12 ejercicios -4 por cada grupo- que constituyeron el Examen Final de Febrero de 1995.

Ejercicio 1: Programa que realiza la misma función que el comando copy de MS-DOS.
Ejercicio 2: Centro de masas de un sistema de masas puntuales.
Ejercicio 3: Cálculo de una raíz de una función por el método de bisección.
Ejercicio 4: Girar 90º una matriz rectangular.
Ejercicio 5: Cambiar la forma de de una matriz, manteniendo el orden (por filas) de sus elementos.
Ejercicio 6: Cálculo de una raíz de una función por el método de Newton.
Ejercicio 7: Integración numérica de una función.
Ejercicio 8: Crear el comando more.
Ejercicio 9: Diferenciación numérica.
Ejercicio 10: Calculadora elemental interactiva.
Ejercicio 11: Tabla de logaritmos con formatos adecuados por columnas.
Ejercicio 12: Máximo valor y vector propio por iteración directa.


Ejercicio 1: Programa que realiza la misma función que el comando copy de MS-DOS

PrevioSiguienteTop

Se trata de realizar un programa que copie el contenido de un fichero de texto en otro fichero con nombre distinto. El programa pedirá en primer lugar el nombre de un fichero ya existente; una vez introducido dicho nombre pedirá otro nombre que será el del nuevo fichero donde irá la copia. Con estos datos, el programa realizará una copia del texto contenido en el primer fichero en otro fichero con el segundo nombre en el mismo directorio. El nombre del programa será copiar.c

Para nota: se trata de realizar un programa que realice la copia de ficheros pero dando los nombres de los programas en la línea de comandos, es decir, pasando los nombres como argumentos de la función main(). Este programa se llamará copiar1.c

Solución comentada del Ejercicio 1.


/* fichero copiar.c */
#include <stdio.h>
#include <stdlib.h>
void main()
{
        int ch;
        char file1[13], file2[13];
        FILE *f1, *f2;
        
        printf("Teclea el nombre de fichero original:");
        scanf("%s", &file1);
        printf("\ny ahora el nombre de fichero de copia:\n");
        scanf("%s", &file2);
        f1 = fopen(file1, "r");
        f2 = fopen(file2, "w");
        if(!f1)
                printf("Fichero %s no existe!!", file1);
        else
                while ((ch = getc(f1)) != EOF) 
                        putc(ch,f2);
        fclose(f1);
        fclose(f2);
}

Comentario: La resolución del problema es muy sencilla: basta con abrir dos ficheros: uno de lectura y otro de escritura, que contengan respectivamente el fichero original y el fichero copia. Una vez abiertos estos ficheros, se leen los caracteres del fichero original y se copian en el fichero copia. Para ello se emplea el bucle while.


Ejercicio 2: Centro de masas de un sistema de masas puntuales

PrevioSiguienteTop

Se pide realizar un programa que calcule el centro de masas de un sistema de n masas puntuales. Para ello el programa pedirá conjuntos de tres números reales: la coordenada x del punto, la coordenada y y su masa m. El número n de masas no estará limitado de antemano y la entrada de datos se detendrá cuando se introduzca por el teclado un carácter fin de fichero (control-z). El programa imprimirá por pantalla como resultado las coordenadas xg e yg del centro de masas del conjunto de puntos que se han introducido. El programa se llamará cdg.c. Las fórmulas que dan la posición del centro de gravedad son las siguientes:

Solución comentada del Ejercicio2.


/* fichero cdg.c */
/* centro de gravedad de un sistema de masas puntuales */
#include<stdio.h>
#define SIZE 20
void main(void)
{
        double sumat1, sumat2, sumat3;
        double xg, yg, x[SIZE], y[SIZE], m[SIZE];
        int i=0, k=0;
        char c='a';    /* iniciar a un carácter cualquiera */
        printf("Cálculo del cdg de un sistema discreto de masas\n");
        while (c!=EOF) {
                printf("\nCoordenadas(x,y) y masa del punto %d: ", i+1);
                scanf("%lf %lf %lf", &x[i], &y[i], &m[i]);
                c = getchar();  
                k=k+1;
                i=i+1;
        }
        sumat1=sumat2=sumat3=0;
        for(i=0; i<k; i++){
                sumat1 += m[i]*x[i];
                sumat2 += m[i];
                sumat3 += m[i]*y[i];
        }
        xg = sumat1/sumat2;
        yg = sumat3/sumat2;
        printf("\n\nEl centro de gravedad es (%5.2lf, %5.2lf)\n", xg, yg);
}

Comentario: Mientras el carácter leído no se corresponda con el de fin de fichero el programa lee las coordenadas x e y, y la masa m de cada punto. Al introducir el carácter de fin de fichero (o pulsar intro) se sale del bucle de lectura y se calcula la posición del centro de gravedad.


Ejercicio 3: Cálculo de una raíz de una función por el método de bisección

PrevioSiguienteTop

Según el teorema de Bolzano, si una función continua tiene valores de distinto signo en los extremos a y b de un intervalo, la función tiene al menos un cero (una raíz) en ese intervalo. Este teorema se utilizará como base para un método de cálculo de raíces de funciones, llamado Método de la Bisección. Se parte de un par de puntos a y b en los que se cumple la condición f(a)*f(b)<0 (la función tiene signos opuestos, luego existe al menos una raiz en [a, b]). A continuación se estudia la función en el punto c, que es el punto medio del intervalo (c=(a+b)/2), y se estudia el signo de f(c). Si f(c) tiene el mismo signo que f(a) se hace a=c y se vuelve a comenzar (se tiene un nuevo intervalo[a, b] en el que está la raíz y que es la mitad que el anterior); si f(c) tiene el mismo signo que f(b) se hace b=c y se vuelve a comenzar. El proceso prosigue con intervalos [a, b] cada vez más pequeños hasta que se cumple una de las siguientes condiciones:

- f(c)=0 c es la solución buscada

- f(c) < eps1 c es la solución aproximada buscada

- b-a < eps2 c=(b-a)/2 es la solución aproximada buscada

Aplicar este método para encontrar la raiz de la función f(x)=sen(x)-x, en el intervalo [1, 4]. Probar con valores eps1=10e-08 y eps2=10e-12. Este programa se llamará bisec.c.

Solución comentada del Ejercicio 3.


/* cálculo de una raíz por el método de la bisección */
/* fichero bisec.c */
#include <stdio.h>
#include <math.h> 
#define EPS1 10E-8
#define EPS2 10E-12
void main(void)
{
        double a=1, b=4, c, fa, fb, fc;
        double func(double); 
        c = (a+b)/2.0;
        while (func(c)>EPS1 && (b-a)>EPS2){
                c = (a+b)/2;
                if (func(c)*func(a) > 0)
                        a = c;
                else 
                        b = c;
        }
        printf("El valor de la raíz es: %12.6e\n", c);
}
double func(double x)
{
        return (sin(x)-x);
}

Comentario: Mientras se cumplan las dos condiciones (que func(c) sea mayor que EPS1 y que la distancia entre a y b sea mayor que EPS2), el programa continúa haciendo aproximaciones de la solución. Conviene darse cuenta de que la condición de que func(c)=0 es más fuerte que la de que sea menor que EPS1, por lo que no es necesario ponerla. Al salir del bucle while se imprime la solución que ya se considera suficientemente aproximada.


Ejercicio 4: Girar 90º una matriz rectangular

PrevioSiguienteTop

Se trata de realizar un programa que gire una matriz rectangular (mxn) 90 grados en sentido antihorario. A continuación se ilustra con un ejemplo lo que se pretende realizar.

Matriz original

Matriz girada 90 grados

Si se vuelve a girar 90 grados dará

Pensar bien, a la vista de estos ejemplos, como queda modificada la matriz con un giro de 90º en sentido antihorario. Hacer que el programa pueda aplicar varios giros consecutivos, de forma que se pueda comprobar que con cuatro giros se vuelve a la posición inicial. Llama a este programa rotamat.c.

Solución comentada del Ejercicio 4.


/* fichero rotamat.c */
/* girar una matriz rectangular */
#include<stdio.h>
double **crear_matriz(int, int);
void main(void)
{
        int numfil, numcol;
        int i, j, num, giro;
        double **a;
        
        printf("\nIntroduzca el número de filas: ");
        scanf("%d", &numfil);
        printf("\nIntroduzca el número de columnas: "); 
        scanf("%d", &numcol);
        a = crear_matriz(numfil, numcol);
        for (i=0; i<numfil; i++)
                for(j=0; j<numcol; j++){
                        printf("\nElemento (%d,%d):  ", i+1, j+1);
                        scanf("%lf", &a[i][j]);
                }
        printf("\n\nLa matriz original es: ");
        for (i=0; i<numfil; i++) {
                printf("\n");
                for (j=0; j<numcol; j++)
                        printf("%12.4lf  ", a[i][j]);
        }
        printf("\n\n¿Cuántas veces desea girar la matriz?: ");
        scanf("%d", &num);
        giro = num%4;
        if(giro == 1) {
                printf("\n\nLa matriz girada %d veces es:\n", num);
                for (j=numcol-1; j>=0; j--) {
                        printf("\n");
                        for(i=0; i<numfil; i++)
                                printf("%12.4lf  ", a[i][j]);
                }
        }
        else if(giro == 2) {
                printf("\n\nLa matriz girada %d veces es:\n", num);
                for(i=numfil-1; i>=0; i--) {
                        printf("\n");
                        for(j=numcol-1; j>=0; j--)
                                printf("%12.4lf  ", a[i][j]);
                }
        }
        else if(giro == 3) {
                printf("\n\nLa matriz girada %d veces es:\n",num);
                for(i=numcol-1; i>=0; i--) {
                        printf("\n");
                        for(j=numfil-1; j>=0; j--)
                                printf("%12.4lf  ", a[j][i]);
                }      
        }
        else { 
                printf("\n\nLa matriz girada %d veces es:\n", num);
                for(i=0; i<numfil; i++) {
                        printf("\n");
                        for (j=0; j<numcol; j++)
                                printf("%12.4lf  ", a[i][j]);
                }
        }
}
double **crear_matriz(int m, int n)
{
        double **matriz;
        int i;
        matriz = calloc(m, sizeof(double *));
        matriz[0] = calloc(m*n, sizeof(double));
        for(i=0; i<m; i++)
                matriz[i] = matriz[0]+n*i;
        return matriz;
}

Comentario: Se guarda memoria para la matriz y se pregunta el número de veces que se desea girar. Girar 7 veces es equivalente a girar 3 veces, y por eso se calcula el resto de la división entre 4.

En primer lugar se imprime la matriz original y luego la que corresponde a cada giro. Hay que darse cuenta de que no es necesario reservar memoria para una nueva matriz.


Ejercicio 5: Cambiar la forma de de una matriz, manteniendo el orden (por filas) de sus elementos

PrevioSiguienteTop

Una matriz con m filas y n columnas tiene (mxn) elementos, y en C se almacena en la memoria por filas. Se pide realizar un programa que haga lo siguiente: 1) leer m, n y los elementos de una matriz (mxn). 2) Leer unos valores p y q, y con los mismos elementos de la matriz (mxn) leída previamente, crear otra matriz de p filas y q columnas, que tenga el mismo número de elementos que la anterior (comprobar que pxq = mxn, y si no se cumple volver a leer p y q), de modo que los elementos estén almacenados en la memoria en el mismo orden, o dicho de otra forma, que el orden por filas se mantenga. El siguiente ejemplo ilustra lo que se pretende realizar.

Dada la siguiente matriz 3x4:

hallar una matriz 2x6 que tenga sus elementos en el mismo orden en la memoria.

Solución: 3x4 es igual que 2x6, luego los números son correctos. Los elementos de la matriz original están en la memoria ordenados por filas, es decir, su orden es:

Reorganizándolos como matriz 2x6 se obtiene:

Se pide pues realizar un programa que pida una matriz (las filas, las columnas y los elementos), pida otros valores de filas y columnas y presente la matriz de la nueva forma. El programa deberá chequear, previamente, si los nuevos valores de filas y columnas que se introducen dan el mismo producto filas*columnas de la matriz original. Llámese a este programa cambio.c

Solución comentada del Ejercicio 5.


/* fichero cambio.c */
#include<stdio.h>
void main(void)
{
        int m, n, p, q;
        int i, j, k, l;
        double **a, **b;
        double **crear_matriz(int, int);
        
        printf("\nNúmero de filas y de columnas de la matriz: ");
        scanf("%d%d", &m, &n);
        a = crear_matriz(m,n);
        printf("\nIntroduzca los elementos de la matriz.\n");
        for (i=0; i<m; i++)
                for(j=0; j<n; j++){
                        printf("\nElemento (%d,%d): ", i+1, j+1);
                        scanf("%lf", &a[i][j]);
                }
        do {
                printf("\nNúmero de filas y columnas de la nueva matriz: ");
                scanf("%d%d", &p, &q);
        } while(p*q != m*n);
        b = crear_matriz(p, q);
        for (i=0, k=0; i<m; i++)
                for(j=0, l=0; j<n; j++){
                        k = (i*n+j)/q;
                        l = (i*n+j)-k*q;
                        b[k][l] = a[i][j];
                }
        
        for (i=0; i<p; i++){
                printf("\n");
                for(j=0; j<q; j++)
                        printf("%5.1lf", b[i][j]);
        }
}
double **crear_matriz(int m, int n)
{
        double **matriz;
        int i;
        matriz =c alloc(m, sizeof(double *));
        matriz[0] = calloc(m*n, sizeof(double));
        for(i=0; i<m; i++)
                matriz[i] = matriz[0]+n*i;
        return matriz;
}

Comentario: El problema se resuelve introduciendo primeramente una matriz (reservando memoria de modo dinámico e introduciendo los datos). Luego se introducen las dimensiones de otra matriz de tal forma que el producto de filas por columnas de la nueva matriz sea igual al de la matriz anterior. Una vez que hemos reservado sitio para la segunda matriz, se va rellenando sabiendo que la equivalencia de índices entre las dos matrices es: k=(i*n+j)/q y l=(i*n+j)-k*q, siendo q el número de filas de la segunda matriz y n el número de filas de la primera. Los índices de la primera matriz son i y j y los de la segunda k y l.


Ejercicio 6: Cálculo de una raíz de una función por el método de Newton

PrevioSiguienteTop

El método de Newton permite hallar de modo iterativo raíces (ceros) de funciones no lineales. Este método está basado en el desarrollo en serie de Taylor de la función f(x) en el punto xi, siendo xi una cierta aproximación de la solución x que se desea calcular. El desarrollo en serie de Taylor produce:

(1)

Utilizando sólo los dos primeros términos del desarrollo en serie (es decir, sustituyendo la función por la tangente en el punto xi), se tiene la siguiente ecuación lineal:

(2)

de donde se puede despejar el valor de la x que anula esta expresión (la raíz de la ecuación linealizada), es decir, una nueva aproximación a la verdadera solución. A esta nueva aproximación le llamaremos xi+1. Este valor se puede calcular a partir de (2) con la expresión:

(3)

Ahora volvemos a aplicar el mismo procedimiento, sustituyendo la función no lineal por los dos primeros términos (de nuevo la tangente) de su desarrollo en serie alrededor del punto xi+1, y calculando una nueva y mejor aproximación xi+2:

(4)

expresión equivalente a la (3) con los subíndices actualizados. El proceso prosigue de la misma manera hasta que el valor absoluto de la diferencia relativa entre dos aproximaciones consecutivas sea menor que un determinado número epsilon,

(5)

La siguiente figura ilustra el significado geométrico del método de Newton para hallar la raíz de una función no lineal: se parte de un primer punto en el que se linealiza la función (se sutituye la función por la recta tangente) y se halla la raiz de la función linealizada. En esa raíz se vuelve a linealizar la función original y se halla una nueva aproximación a la verdadera solución. El proceso prosigue hasta que la diferencia entre dos aproximaciones consecutivas es suficientemente pequeña.

Aplica este método a la función x3-3x2-x+3=0, comenzando con un valor inicial x=10. Guarda este programa en un fichero que se llame newton.c.

Solución comentada del Ejercicio 6.


/* fichero newton.c */
/* cálculo de una raíz de un polinomio por el método de Newton */
#include<stdio.h>
#include<math.h>
#define EPS 10e-6
void main(void)
{
        double x;
        double raiz=10.0, error=5.0;
        double f(double);
        double fd(double);
        double absoluto(double);
        while(error>EPS) {
                x = raiz;
                raiz = x-f(x)/fd(x);
                error = absoluto((raiz-x)/raiz);
        }
        printf("\nEl valor de la raiz es: %9lf\n",raiz);
}
double f(double x) {
        double y;
        y = x*x*x-3*x*x-x+3;
        return y;
}
double fd(double x) {
        double y;
        y = 3*x*x-6*x-1;
        return y;
}
double absoluto(double x) {
        if (x>=0)
                return x;
        else
                return -x;
}

Comentario: La función f evalúa el valor de la función f(x) en un punto dado. La función fd evalúa el valor de la derivada de la función en ese mismo punto. La función absoluto devuelve el valor absoluto de la variable pasada como argumento. Mientras el error sea mayor que el permitido, se calculan iterativamente los nuevos valores de x.


Ejercicio 7: Integración numérica de una función

PrevioSiguienteTop

La integral definida entre los puntos a y b de una función continua y acotada f(x) representa el área comprendida debajo de esa función. En ocasiones es necesario calcular integrales (áreas) de modo numérico, es decir, sin conocer la integral de la función f(x). Existen varios posibles métodos para calcular este área. Quizás el más sencillo sea sustituir el área por un conjunto de n rectángulos elementales de base h=(b-a)/n y altura f(a+ih), i=0, 1, 2, ..., n-1. El área sería:

(1)

La representación gráfica de esta forma de aproximar la integral se presenta en la figura siguiente (parte izquierda). Resulta que si n se hace muy grande (h muy pequeño) el error será pequeño.

Otro método algo más complicado (representado en la figura de la derecha) consiste en sustituir el área por un conjunto de n trapecios elementales de base h y lados f(a+ih) y f(a+(i+1)h), o lo que es lo mismo, por n rectángulos de altura (f(a+ih)+f(a+(i+1)h))/2. Es obvio que con este segundo método los errores van a ser más pequeños. En este caso, si llamamos fi a f(a+ih), la fórmula resulta ser:

(2)

A la vista de estos métodos, se pide realizar un programa que calcule el área de la función f(x)=x3+x2–5x+3 entre 0 y 3, por el segundo de los métodos explicados. El programa deberá pedir el valor de n. Llama a este programa integra.c

Solución comentada del Ejercicio 7.


/* fichero integra.c */
/* cálculo de la integral definida de una función */
#include<stdio.h>
void main(void)
{
        int n, k;
        double area=0.0, h;
        double f(double);
        printf("\nIntroduzca el número de trapecios de la aproximación: ");
        scanf("%d", &n);
        h = 3./n;
        for (k=0; k<n; k++)
                area += ((f(k*h)+f((k+1)*h))*h)/2;
        printf("\nEl área calculada es %9.3lf", area);
}
double f(double x){
        double y;
        y = x*x*x+x*x-5*x+3;
        return y;
}

Comentario: La función f evalúa el valor de la función en un punto dado. Una vez determinado el número de trapecios que se van a utilizar para calcular el área, ésta se determina mediante la segunda expresión (2) del enunciado.


Ejercicio 8: Crear el comando more

PrevioSiguienteTop

Existe un comando en MS-DOS y en UNIX llamado more que permite presentar los archivos grandes por pantalla de forma paginada, es decir, de modo que la presentación del archivo se detiene al llegar a un número determinado de líneas (normalmente 2 ó 3 líneas menos de las que caben en la pantalla), y prosigue con las líneas siguientes al pulsar una tecla cualquiera (distinta de la "q", que tiene un uso especial como ahora se verá).

Se pide realizar un programa que sea capaz de presentar cualquier archivo de texto de forma paginada: el programa pedirá el nombre del archivo a presentar y escribirá tantas líneas como permita el monitor del PC (en este caso, 18 líneas). Al cabo de la primera página, aparecerá la palabra "more?" de tal forma que si se pulsa la letra "q" se detiene la ejecución y se acaba el programa, pero si se pulsa cualquier otra letra, se continúa mostrando el fichero con la siguiente página (18 líneas) donde se repetirá el mismo proceso. La ejecución del programa terminará cuando se haya presentado el fichero en su totalidad o se haya pulsado la tecla "q", como ya se ha señalado. El programa se llamará "masmore.c". Pruébalo con un fichero que tenga al menos 50 líneas.

Solución comentada del Ejercicio 8.


/* fichero masmore.c */
#include<stdio.h>
void main(void)
{
        char archivo[13];
        FILE *fp;
        char c;
        int li=0;
        printf("\nIntroduzca el nombre del fichero: ");
        scanf("%s", archivo);
        fp = fopen(archivo, "r");
        for( ; c!=EOF; ) {
                if (li==18) {
                        printf("\nmore?\n");
                        c = getchar();
                        if((c=='Q')||(c=='q'))
                                exit(1);
                        else {
                                while(getchar()!='\n');
                                li=0;
                        }
                }
                c = fgetc(fp);
                printf("%c", c);
                if (c == '\n')
                        li++;
        }
}

Comentario: Se leen e imprimen todos los caracteres hasta que se acabe el fichero, y se suma 1 a un contador cada vez que se salta de línea. En el caso de llegar a la línea 18 sin haber acabado el fichero, se puede salir tecleando q o Q, o se puede reinicializar el contador a cero y seguir leyendo


Ejercicio 9: Diferenciación numérica

PrevioSiguienteTop

La derivada de una función y=f(x) con respecto a x se representa geométricamente mediante la pendiente angular de la recta tangente a la función en el punto considerado. Supongamos que tenemos una función f(x) cuyos valores podemos calcular, pero que no conocemos la función derivada f'(x). En este caso la derivada se puede aproximar numéricamente de varios modos, dos de los cuales se muestran gráficamente en la siguiente figura.

Una primera forma de aproximar el valor de f'(x) es utilizar la pendiente de una de las dos secantes correspondientes a los intervalos [x-h, x] y [x, x+h], resultando:

(1)

(2)

pero es mucho más exacto (se ve a simple vista) el utilizar la siguiente fórmula, correspondiente a la secante definida entre los puntos [x-h, x+h]:

(3)

Se pide realizar un programa que comience calculando los 101 valores de una función seno(x) desde x=0.0 hasta x=1.0, con intervalos de h=0.01, almacenándolos en un vector f[ ]. A continuación se creará otro vector d[ ] que contenga las derivadas numéricas de la función f[ ], calculadas con las fórmulas anteriores. En principio se utilizará la ecuación (3) que es la más exacta, pero para el primer valor de d[ ] habrá que utilizar la expresión (2) que calcula la derivada en x sin necesidad de valores anteriores a x, y para el último valor de d[ ] se utilizará la ecuación (1), que aproxima la derivada sin necesidad de valores de f[ ] posteriores a x. Finalmente se escribirán en cuatro columnas con al menos 8 decimales los valores de x, de la función f[i], de la derivada aproximada d[i] y de la derivada exacta (lógicamente cos(f[i])). Guarda este programa con el nombre de derivada.c.

Solución comentada del Ejercicio 9.


/* fichero derivada.c */
/* cálculo numérico de la derivada de una función en un punto */
#include<stdio.h>
#include<math.h>
#define SIZE 101
void main(void)
{
        int i;
        double h=0.01;
        double x[SIZE], f[SIZE], d[SIZE], e[SIZE];
        x[0] = 0.0;
        for(i=1; i<SIZE; i++)
                x[i] = i*h;
        for(i=0; i<SIZE; i++)
                f[i] = sin(x[i]);
        d[0] = (f[1]-f[0])/h;
        for(i=1; i<SIZE-1; i++)
                d[i] = (f[i+1]-f[i-1])/(2*h);
        d[SIZE-1] = (f[SIZE-1]-f[SIZE-2])/h;  
        for(i=0; i<SIZE; i++)
                e[i] = cos(x[i]);
        for(i=0; i<SIZE; i++) 
        printf("%15.8lf %15.8lf %15.8lf %15.8lf\n",
                 x[i], f[i], d[i], e[i]);
}

Comentario: Se reserva espacio en memoria para los vectores que se van a utilizar: x será un vector donde se van a guardar los puntos en los que se va a calcular la función y su derivada.; f el valor de la función y d el valor de la aproximación de la derivada. Por último, e será el vector donde se almacene el valor exacto de la derivada.


Ejercicio 10: Calculadora elemental interactiva.

PrevioSiguienteTop

Se pide realizar un programa que realice de modo interactivo las cuatro operaciones básicas (suma, resta, multiplicación y división). Al arrancar, el programa presentará un menú principal con cinco opciones: suma, resta, multiplicación, división y salida (para terminar la ejecución). Estas opciones se elegirán mediante un número o una letra indicada en el propio menú. Una vez elegida una de las cuatro primeras opciones, se pedirán dos números -los operandos- y el programa presentará el resultado de la operación. Una vez realizado esto, el programa volverá al menú principal y se podrá realizar una nueva operación, hasta que se utilice la opción de salida.

El programa se llamará calcula.c.

Solución comentada del Ejercicio 10.


/* fichero calcula.c */
/* calculadora elemental interactiva */
#include <stdio.h>
#include <stdlib.h>
void main(void)
{
        int i, fin=1; 
        double a, b;
        char c;
        double suma(double, double);
        double resta(double, double);
        double multiplicacion(double, double);
        double division(double, double);
        void valor(double *, double *);
        void menu(void);
        do {
                menu();
                c = getchar();
                if (c!='\n' && (c<'1'||c>'5')) { 
                        printf("%s%s", "\nNo es una opción válida", 
                                " introduzca de nuevo el número");
                        printf("\nPulse una tecla y repita el proceso");
                        getchar();
                        getchar();
                }
                switch(c){
                        case '1':
                                valor(&a, &b);
                                suma(a, b);
                                break;
                        case '2':
                                valor(&a, &b);
                                resta(a, b);
                                break;
                        case '3':
                                valor(&a, &b);
                                multiplicacion(a, b);
                                break;
                        case '4':
                                valor(&a, &b);
                                division(a, b);
                                break;
                        case '5':
                                fin = 0;
                                break;
                }
        } while(fin);
}
void menu(void){
        system("cls");
        printf("\n\n\n\tBienvenido al programa matem tico.\n");
        printf("\tIntroduce una de las siguientes opciones");
        printf("\n\n\t\t1.- Sumar cifras");
        printf("\n\n\t\t2.- Restar cifras");
        printf("\n\n\t\t3.- Multiplicar cifras");
        printf("\n\n\t\t4.- Dividir cifras");
        printf("\n\n\t\t5.- Salir\n\n");
}
double suma(double a, double b){
        printf ("\n\nLa suma introducida es la siguiente:\n");
        printf ("%.2lf + %.2lf = %.2lf", a, b, (a+b));
        getchar();
        getchar();
}
double resta(double a, double b){
        printf ("\n\nLa resta introducida es la siguiente:\n");
        printf ("%.2lf - %.2lf = %.2lf", a, b, (a-b));
        getchar();
        getchar();
}
double multiplicacion (double a, double b){
        printf ("\n\nLa multiplicaci¢n introducida es la siguiente:\n");
        printf ("%.2lf * %.2lf = %.2lf", a, b, (a*b));
        getchar();
        getchar();
}
double division(double a, double b){
        printf ("\n\nLa divisi¢n introducida es la siguiente:\n");
        printf ("%.2lf / %.2lf = %.2lf", a, b, (a/b));
        getchar();
        getchar();
}
void valor(double *a, double*b){
        system ("cls");
        printf("Introduce el valor del primer término: ");
        scanf("%lf", a);
        printf("Introduce el valor del segundo término: ");
        scanf("%lf", b);
}

Comentario: Mientras fin no valga cero (inicialmente vale 1) se despliega el menú inicial y, dependiendo del valor introducido , se llama a la función correspondiente que, una vez leídos los valores efectúa la operación que se desee. Si en el menú inicial se elige "5" fin vale 0 y termina la ejecución.


Ejercicio 11: Tabla de logaritmos con formatos adecuados por columnas.

PrevioSiguienteTop

Se pide realizar un programa que calcule los logaritmos decimal y neperiano de los números comprendidos entre 1 y 100, con intervalos de 5 en 5. Para calcular los logaritmos se empleará la librería matemática math.h. La función que calcula el logaritmo decimal es log10(d) siendo d un número cualquiera, y la que calcula el logaritmo neperiano es log(d).

La salida por pantalla que debe dar este programa deberá ser exactamente la siguiente:

Este programa calcula el logaritmo decimal
y el logaritmo neperiano de los números entre el 
uno y el 100, de 5 en 5.
    número                 log                  ln
---------------------------------------------------
         1            0.000000            0.000000
         5            0.698970            1.609438
        10            1.000000            2.302585
        15            1.176091            2.708050
        20            1.301030            2.995732
        25            1.397940            3.218876
        30            1.477121            3.401197
        35            1.544068            3.555348
        40            1.602060            3.688879
        45            1.653213            3.806662
        50            1.698970            3.912023
        55            1.740363            4.007333
        60            1.778151            4.094345
        65            1.812913            4.174387
        70            1.845098            4.248495
        75            1.875061            4.317488
        80            1.903090            4.382027
        85            1.929419            4.442651
        90            1.954243            4.499810
        95            1.977724            4.553877
       100            2.000000            4.605170

Se pondrá atención especial en la alineación de las columnas, a los encabezamientos con su subrayado y a los puntos decimales. El nombre del programa será logarit.c

Solución comentada del Ejercicio 11.


/* fichero logarit.c */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
void main(void)
{
        int i;
        system ("cls");
        printf ("Este programa calcula el logaritmo decimal");
        printf("\ny el logaritmo neperiano de los números entre el\n");
        printf("uno y el 100, de 5 en 5.\n");
        printf ("%10s%20s%20s\n", "número", "log", "ln");
        for (i=0; i<51; i++)
                putchar('-');
        printf ("\n%10d%20lf%20lf", 1, log10(1), log(1));
        for (i=5; i<=100; i=i+5)
                printf ("\n%10d%20lf%20lf", i, log10(i), log(i));
}

Comentario: En este ejercicio sólo hay que tener cuidado con el formato de la salida de los datos. También se debe de tener en cuenta que al incrementarse de forma diferente, el primer término debe de ir fuera del bucle.


Ejercicio 12: Máximo valor y vector propio por iteración directa.

PrevioSiguienteTop

Existe un método iterativo muy sencillo para calcular el mayor valor propio de una matriz y su vector propio correspondiente. Este método se llama método de la iteración directa. Este método procede de la siguiente forma:

Se parte de un vector arbitrario (por ejemplo, con todos los elementos del mismo valor) que cumple la condición de tener módulo unidad (esta condición está indicada por la raya encima del nombre del vector). A partir de este vector, mediante el producto por la matriz A hallamos un nuevo vector (sin raya porque no será unitario):

(1)

que normalizamos (es decir, hacemos unitario) dividiendo por el módulo cada uno de sus elementos:

(2)

A partir de hallamos mediante el producto por la matriz A un nuevo vector que normalizamos a continuación:

(3)

(4)

Proseguimos este proceso con pasos sucesivos basados en las fórmulas iterativas:

(5)

(6)

Se puede demostrar que para i suficientemente elevado el módulo || tiende al valor propio de máximo valor absoluto, que supondremos es l1 (supondremos que los valores propios están ordenados de modo decreciente | l1| > | l2| > ... > | ln|). Al mismo tiempo, el vector vector tiende al vector propio correspondiente x1.

Se pide hacer un programa que lea una matriz cuadrada de dimensión n y calcule el máximo valor propio y el vector propio asociado con él. Para garantizar que ambos -el valor y el vector propio son reales- se considerará que la matriz A es simétrica. Una vez leída la matriz se formará un vector arbitrario (por ejemplo, todo "1.0") que se normalizará para que tenga módulo unitario. A partir de ese vector, comenzará un proceso iterativo basado en las expresiones (5) y (6) que terminará cuando dos estimaciones consecutivas del mayor valor propio (||) sean suficientemente próximas en valor relativo, o diciéndolo de otra forma, cuando se cumpla la condición:

(7)

donde epsilon es un número pequeño, definido por el usuario (por ejemplo 10e-06). Una vez calculados el valor y vector propio, se ecribirán en la pantalla y terminará la ejecución del programa. Guarda este programa con el nombre maxvalp.c.

NOTA: Para satisfacer la curiosidad de los aficionados a las demostraciones algebraicas se incluye un breve esbozo de la demostración que está detrás del metodo de la iteración directa. La demostración que sigue se separa un poco del algoritmo, sobre todo en el tema de la normalización de los vectores. Desde luego, no es en absoluto necesaria para hacer el programa. Sea cual sea el vector arbitrario elegido inicialmente, si la matriz A es simétrica dicho vector siempre se podrá expresar como combinación lineal de los n vectores propios (con coeficientes desconocidos )

(i)

Por otra parte, definiendo el vector yi+1 = A yi, tendremos que al aplicar m veces la fórmula iterativa llegamos a:

(ii)

Sustituyendo (i) en (ii) y utilizando las propiedades de los vectores propios:

(iii)

Si sacamos factor común y tenemos en cuenta que por ser el mayor valor propio, todos los cocientes serán menores que 1, pera m suficientemente elevado todos los términos del sumatorio serán despreciables frente al primero, y se podrá escribir:

(iv)

Para la siguiente la iteración:

(v)

de donde se deduce que el vector tiende realmente al vector propio buscado. Por otra parte, dividiendo miembro a miembro los módulos de las ecuaciones (v) y (iv) se obtiene:

En esta demostración se ha supuesto que el coeficiente era distinto de cero. Eso es lo más probable si el vector inicial se ha escogido de modo arbitrario, pero aunque no fuera así el método también funciona, aunque la demostración nos llevaría un poco lejos...

Solución comentada del Ejercicio 12.


/* fichero maxvalp.c */
/* cálculo del máximo valor propio por itaración directa */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
void main(void)
{
        int i, j,orden;
        double z, z2, x, x2;
        double **a, *vect, *vect2;
        double **ma_alloc(int, int);
        double vabs(double);
        void normalizar(double *, int);
        void matvec(double **, double*, double*, int);
        printf ("\nIntroduzca el orden de la matriz: ");
        scanf ("%d", &orden);
        a = ma_alloc(orden, orden);
        vect = malloc(orden*sizeof(double));
        vect2 = malloc(orden*sizeof(double));
        printf("\nIntroduzca los datos de la matriz:\n");
        for (i=0; i<orden; i++)
                for(j=0;j<orden;j++) {
                        printf("\nElemento (%d,%d): ", (i+1), (j+1));
                        scanf("%lf", &a[i][j]);
                }
        for (i=0; i<orden; i++)   /* aproximación inicial */
                vect2[i]=1.0;
        do {
                for (j=0; j<orden; j++)
                        vect[j] = vect2[j];
                normalizar (vect, orden);
                matvec(a, vect, vect2, orden);
                for (j=0; j<orden; j++)
                        vect[j] = vect2[j];
                for (j=0; j<orden; j++)
                        z += vect[j]*vect[j];
                x = sqrt (z);
                for (j=0; j<orden; j++)
                        z += vect2[j]*vect2[j];
                x2 = sqrt (z);
        } while (vabs((x2-x)/x2)<0.0000001);
        printf ("El vector buscado es el:\n(");
        for (i=0; i<orden; i++)
                printf ("%lf ", vect[i]);
        printf(")");
        printf("\nEl valor propio es: %lf", x);
}
double **ma_alloc(int filas, int columnas){
        int i;
        double **a;
        a = malloc(filas*sizeof(double *));
        for (i=0; i<filas; i++)
                a[i] = malloc(columnas*sizeof(double));
        return a;
}
void normalizar(double *v, int n){
        double x, z=0.0;
        int i;
        for (i=0; i<n; i++)
                z += v[i]*v[i];
        x = sqrt (z);
        for (i=0; i<n; i++)
                v[i] = v[i]/x;
}
void matvec(double **matriz, double *vector, double *vector2, int orden)
{
        int i,j;
        double sum;
        for (i=0; i<orden; i++) {
                sum = 0;
                for(j=0; j<orden; j++)
                        sum += matriz[i][j]*vector[j];
                vector2[i] = sum;
        }
}
double vabs(double a){
        if (a<0.0)
                return (-a);
        else
                return a;
}

Comentario: Este programa calcula iterativamente el mayor valor propio de una matriz. Para eso mientras no se cumpla la condición de error de la fórmula (7), continúa calculando nuevos valores con más iteraciones (bucle do while). Las funciones que utiliza sonlas siguientes: ma_alloc para reservar dinámicamente memoria para una matriz, vabs para calcular el valor absoluto, normalizar para que la norma de un vector dado sea unitaria y matvect para multiplicar una matriz por un vector.