PRÁCTICA 11: EJERCICIOS RESUELTOS

Se presentan resueltos a continuación los 9 ejercicios -3 por cada grupo- que constituyeron la Práctica nº 11. Algunas de las soluciones presentadas son más complicadas de lo estrictamente necesario y de lo que se exigía en dicha práctica. Se hace así con objeto de presentar diversas técnicas de programación que puedan ser útiles en la realización de otros ejercicios de dificultad similar o superior.

Ejercicio 1: Contar las vocales acentuadas de un fichero.
Ejercicio 2: Evaluación de una forma cuadrática.
Ejercicio 3: Encontrar un número en una lista e insertarlo si no está en ella.
Ejercicio 4: Trasponer una matriz rectangular.
Ejercicio 5: Intersección de una recta con una circunferencia.
Ejercicio 6: Dar la vuelta a cada una de las palabras de un texto.
Ejercicio 7: Escribir las palabras de un texto en orden inverso.
Ejercicio 8: Descomposición de una matriz en suma de una simétrica y otra antisimétrica.
Ejercicio 9: Calcular sen(x) por interpolación a partir de una tabla.


Ejercicio 1: Contar las vocales acentuadas de un fichero.

PrevioSiguienteTop

Este ejercicio consta de dos partes:

1.- Deberás realizar un programa que te permita conocer el número según el código ASCII de las vocales minúsculas acentuadas (á, é, í, ó, ú). Guarda este programa con el nombre de acentos.c

2.- Deberás realizar un programa que indique el número de cada una de las vocales acentuadas de un determinado fichero. Guarda este programa como cacentos.c

Solución comentada del Ejercicio 1.


/* fichero acentos.c */
/* Código ASCII de las vocales minúsculas acentuadas */ 
#include <stdio.h>
void main(void)
{
   printf("Código ASCII de las vocales minúsculas acentuadas");
   printf("\n\nEl código ASCII de la %c es el: %d.", 'á', 'á');
   printf("\n\nEl código ASCII de la %c es el: %d.", 'é', 'é');
   printf("\n\nEl código ASCII de la %c es el: %d.", 'í', 'í');
   printf("\n\nEl código ASCII de la %c es el: %d.", 'ó', 'ó');
   printf("\n\nEl código ASCII de la %c es el: %d.", 'ú', 'ú');
}


Comentario: Para conocer el número ASCII asociado con un carácter cualquiera basta escribir dicho carácter con formato %d, propio de los números enteros. Si por el contrario se utiliza el formato %c, propio de los caracteres, se imprime el carácter correspondiente. En el ejemplo anterior cada carácter acentuado se escribe con los dos formatos.


/* fichero cacentos.c */
/* Número de vocales acentuadas en un determinado fichero */ 
#include <stdio.h>
void main(void)
{
   char ch;
   int  nas=0, nes=0, nis=0, nos=0, nus=0;
   
   while ((ch=getchar())!=EOF) {
      switch (ch) {
         case 'á': 
            nas++;
            break;
         case 'é': 
            nes++;
            break;
         case 'í': 
            nis++;
            break;
         case 'ó': 
            nos++;
            break;
         case 'ú': 
            nus++;
      }
   }
   printf("\nEl número de 'á' es: %d", nas);
   printf("\nEl número de 'é' es: %d", nes);
   printf("\nEl número de 'í' es: %d", nis);
   printf("\nEl número de 'ó' es: %d", nos);
   printf("\nEl número de 'ú' es: %d", nus);
}


Comentario: Éste es un ejemplo típico de uso de la sentencia switch/case. Cada vez que se encuentra una vocal acentuada se incrementa el contador correspondiente. Es muy importante la presencia de los break, pues si no se incrementarían también todos los demás contadores que aparecen hasta el final del switch.



Ejercicio 2: Evaluación de una forma cuadrática.

PrevioSiguienteTop

Este ejercicio consiste en evaluar una forma cuadrática del tipo: f=xTAx

Para ello debes crear una función a la que pases por referencia el vector x, su tamaño y la matriz A y que devuelva f, que es un escalar. Para reservar espacio en memoria para todas las variables deberás usar reserva dinámica de memoria.

El tamaño del vector x debe ser una variable que introduzca el usuario durante la ejecución del programa. Por otra parte se considera que el vector x es una matriz con una única columna. Guarda el fichero como formcuad.c.

Solución comentada del Ejercicio 2.


/* fichero formcuad.c */
/* cálculo de una forma cuadrática */
#include <stdio.h>
#include <stdlib.h>
void main(void)
{
   double *x, **a;
   int    n, i, j;
   double fc(int n, double **a, double *x);
   
   printf("\nTeclee el número de filas y de columnas: ");
   scanf("%d", &n);
   
   a = calloc(n, sizeof(double*));
   for(i=0; i<n; i++)
      a[i] = calloc(n, sizeof(double));
   x = calloc(n, sizeof(double));
   
   for(i=0; i<n; i++)
      for(j=0; j<n; j++) {
         printf("\nTeclee a(%d, %d): ", i+1, j+1);
         scanf(" %lf", &a[i][j]);
      }
   for(i=0; i<n; i++) {
      printf("\nTeclee x(%d): ", i+1);
      scanf(" %lf", &x[i]);
   }   
   printf("\n\nLa forma cuadrática vale: %lf", fc(n, a, x));
}
/* función para calcular el valor de la forma cuadrática */
double fc(int n, double **a, double *x)
{
   int    i, j;
   double fac=0.0;
    
   for (i=0; i<n; i++)
      for (j=0; j<n; j++)
         fac += x[i]*a[i][j]*x[j];
    
   return (fac);
}


Comentario: Este Ejercicio se ha resuelto con reserva dinámica de memoria para la matriz y el vector con los que se calcula la forma cuadrática. La forma cuadrática se calcula por medio de una función llamada fc(). Esta función utiliza directamente la fórmula del resultado de la forma cuadrática, basada en un doble sumatorio.



Ejercicio 3: Encontrar un número en una lista e insertarlo si no está en ella.

PrevioSiguienteTop

A partir de una lista ordenada de n números, realizar un programa que detecte sin un número determinado forma parte de la lista, en el caso negativo, tendrás que insertarlo en el lugar correspondiente para que la lista siga conservando el orden inicial.

La impresión final del programa nos mostrará la nueva lista incluyendo al nuevo numero. Guarda este programa en un fichero llamado listord.c.

Solución comentada del Ejercicio 3.


/* fichero listord.c */
/* Comprobar si un número está en un vector ordenado,
   y si no está, incluirlo */
   
#include <stdio.h>
#include <stdlib.h>
void main(void)
{
   int i, j, n, num, temp, vector[100];
   
   printf("Teclee el número de elementos: ");
   scanf("%d", &n);
   
   /* lectura de los elementos del vector */
   for (i=0; i<n; i++) {
      printf("\nvector[%d]: ", i+1);
      scanf("%d", &vector[i]);
   }
   
   /* ordenación de menor a mayor */
   for (i=0; i<n-1; i++)
      for (j=i+1; j<n; j++)
         if (vector[i]>vector[j]) {
            temp = vector[i];
            vector[i] = vector[j];
            vector[j] = temp;
         }
         
   /* Escritura del vector ordenado */
   printf("\n\nEl vector ordenado es:\n");
   for (i=0; i<n; i++)
      printf("%6d", vector[i]);
   printf("\n\nTeclee el número deseado: ");
   scanf("%d", &num);
   
   /* Comprobar si está en la lista. Búsqueda binaria */
   if (num < vector[0]) {           /* esta antes del primero */
      i=0;
      j=1;
   }
   else if (num > vector[n-1]) {    /* esta despues del ultimo */
      i=n-2;
      j=n-1;
   }
   else {   
      i = 0;
      j = n-1;
      while (j>i+1) {
         temp = (j+i)/2;
         if (num == vector[temp]) {
            printf("\n\nEl número %d pertenece al vector, en posición %d.", 
               num, temp+1);
            break;
         }
         else if (num < vector[temp]) 
            j = temp;
         else
            i = temp;
      }
   
      if (num == vector[i]) {
         printf("\n\nEl número %d pertenece al vector, en posición %d.", 
               num, i);
         exit(0);
      }
      else if (num == vector[j]) {
         printf("\n\nEl número %d pertenece al vector, en posición %d.", 
               num, j);
         exit(0);
      }
   }
   /* Insertar num en vector[] */
   if (num < vector[i]) {          /* se inserta delante de i */
      for (j=n-1; j>=i; j--)
         vector[j+1] = vector[j];
      vector[i] = num;
   }         
   else if (num < vector[j]) {
      for (j=n-1; j>i; j--)        /* se inserta detrás de i */
         vector[j+1] = vector[j];
      vector[i+1] = num;
   }
   else                            /* se inserta detrás de j */
      vector[j+1] = num;
   n++;
   /* Escritura del nuevo vector ordenado */
   printf("\n\nEl nuevo vector ordenado es:\n");
   for (i=0; i<n; i++)
      printf("%6d", vector[i]);
}   


Comentario: En primer lugar se lee el vector, y si no está ordenado se ordena. Después se lee el número que hay que chequear. Una posible forma de ver si este número ya pertenece al vector es irlo comparando con todos los elementos del vector, al menos hasta que se encuentre un elemento del vector diferente y mayor que el número leído (en ese momento podría pararse la búsqueda, teniendo en cuenta que los elementos del vector han sido ordenados previamente. En este programa se ha seguido un método diferente y más rápido para averiguar si el número leído pertenece al vector: la llamada búsqueda binaria. Se parte de un intervalo definido por los índices i y j, que iniciamente corresponden al primer (0) y último elementos del vector (n-1). A continuación se calcula el elemento central de este intervalo y se compara con el número; de esta forma sabemos en cual de los dos subintervalos [i, central] o [central, j] está el número, y -según en cual esté- actualizamos los valores de i o de j. Seguimos haciendo esto sucesivas veces, con intervalos cada vez más pequeños hasta que se encuantra el número, o se encuentran dos elementos consecutivos del vector entre los que está el número. Hay que tener también en cuenta los casos particulares de que el número sea menor que el primer elemento o mayor que el último.



Ejercicio 4: Trasponer una matriz rectangular.

PrevioSiguienteTop

Realiza un programa en el cual a partir de una matriz rectangular por medio de una función, nos calcule su matriz transpuesta.

Es decir, si tenemos una matriz de mxn, tendrás primero que crear esta matriz con dichas dimensiones con alocación dinámica de memoria, a continuación pedir los datos de la matriz y finalmente llamar a una función que se encargará de transponer la matriz inicial a una matriz de nxm.

La salida del programa nos mostrará las dos matrices tanto la inicial como la transpuesta. Guarda el programa en un fichero llamado trasp.c.

Solución comentada del Ejercicio 4.


/* fichero trasp.c */
/* Trasponer una matriz con reserva dinámica de memoria */
#include <stdio.h>
#include <stdlib.h>
void main(void)
{
   double **a, **b;
   int  m, n, i, j;
   void *crear_matriz(int m, int n, double **a);
   void trasponer(int m, int n, double **a, double **b);
   
   printf("Teclee el número de filas y el de columnas: ");
   scanf("%d%d", &m, &n);
   a = crear_matriz(m, n, a);
   b = crear_matriz(n, m, b);
   
   /* Lectura de la matriz */
   for (i=0; i<m; i++)
      for (j=0; j<n; j++) {
         printf("\nElemento a(%d, %d): ", i+1, j+1);
         scanf(" %lf", &a[i][j]);
      }
   
   /* Impresión de la matriz original */
   printf("\n\nLa matriz original es: ");
   for (i=0; i<m; i++) {
      printf("\n");
      for (j=0; j<n; j++) 
         printf("%10.4lf", a[i][j]);
   }
   
   trasponer(m, n, a, b);
   
   /* Impresión de la matriz traspuesta */
   printf("\n\nLa matriz traspuesta es: ");
   for (i=0; i<n; i++) {
      printf("\n");
      for (j=0; j<m; j++) 
         printf("%10.4lf", b[i][j]);
   }   
}
/* función crear_matriz() */
void *crear_matriz(int m, int n, double **a)
{
   int i;
   
   a = calloc(m, sizeof(double*));
   for (i=0; i<m; i++)
      a[i] = calloc(n, sizeof(double));
   return a;
}
/* función trasponer() */
void trasponer(int m, int n, double **a, double **b)
{
   int i, j;
   
   for (i=0; i<m; i++)
      for (j=0; j<n; j++) 
         b[j][i] = a[i][j];
}         


Comentario: Este Ejercicio se ha resuelto con reserva dinámica de memoria y con dos funciones crear_matriz() y trasponer() cuya aplicación resulta obvia. Por lo demás el programa no tiene mucho de particular: se crean las dos matrices -la original y la traspuesta-, se lle y escribe la matriz original, se traspone y se escribe la matriz traspuesta.



Ejercicio 5: Intersección de una recta con una circunferencia.

PrevioSiguienteTop

En este programa deberás estudiar la intersección de una recta con una circunferencia, ambas contenidas en el plano XY, cuyas ecuaciones generales son:

y = m*x + c (1)

(x-a)2 + (y-b)2 = r2 (2)

Para hallar la solución basta con sustituir la ecuación (1) en la (2) y resolver la ecuación de segundo grado que resulta.

(1+m2) x2+(2mc-2a-2mb) x+(a2+c2+b2-2bc-r2) = 0

Los tres casos posibles son:

1. Dos raíces reales distintas: La recta es secante a la circunferencia.

2. Una raíz doble. La recta es tangente a la circunferencia en un punto.

3. Dos raíces imaginarias: La recta y la circunferencia no se cortan.

En los casos 1 y 2 el programa deberá proporcionar el punto o puntos de intersección con sus dos coordenadas.

Los datos de entrada al programa serán los coeficientes m, c, a, b y r.

Guarda el programa como recta.c.

Solución comentada del Ejercicio 5.


/* fichero recta.c */
#include <stdio.h>
#include <math.h>
void main(void)
{
   double m, c, a, b, r;
   double a1, b1, c1;
   double discr;
   double sol1x, sol2x;
   double sol1y, sol2y;
   printf("Intersección de una circunferencia con una recta.\n\n");
   printf("Introduzca los datos de la circunferencia:\n");
   printf("Centro. Coordenadas x e y: ");
   scanf("%lf%lf", &a, &b);
   printf("\nRadio: ");
   scanf("%lf", &r);
   printf("\nIntroduzca los datos de la recta:\n");
   printf("Pendiente m y ordenada en el origen b: ");
   scanf("%lf%lf", &m, &c);
   a1 = 1 + m*m;
   b1 = 2*m*c - 2*a - 2*m*b;
   c1 = a*a + c*c + b*b - 2*b*c - r*r;
   discr = b1*b1 - 4*a1*c1;
   if (discr > 0.0)
   {
      printf("\n\nExisten dos soluciones:\n");
      sol1x = (-b1 + sqrt(discr) )/(2*a1);
      sol2x = (-b1 - sqrt(discr) )/(2*a1);
      sol1y = m*sol1x + c;
      sol2y = m*sol2x + c;
      printf("primer punto:\n%lf %lf\n", sol1x, sol1y);
      printf("segundo punto:\n%lf %lf\n", sol2x, sol2y);
   }
   else if ( discr < 0.0 )
      printf("\n\nNo hay solución.\n");
   else
   {
      printf("\n\nSólo hay una solución:\n");
      sol1x = -b1/(2*a1);
      sol1y = m*sol1x + c;
      printf("%lf %lf\n", sol1x, sol1y);
   }
}


Comentario: Este Ejercicio es muy sencillo. Lo único que hay que hacer es formar la ecuación de segundo grado a partir de los datos leídos y resolverla, tenienndo en cuanta los tres casos que se pueden presentar.



Ejercicio 6: Dar la vuelta a cada una de las palabras de un texto.

PrevioSiguienteTop

Se trata de realizar un programa que lea una frase y, conservando el orden de las palabras que ha leído, escriba cada una de estas palabras al revés. Por ejemplo:

"La casa de la pradera"

se transformaría en:

"aL asac ed al aredarp"

Guarda este programa con el nombre de alreves1.c

Nota: Este Ejercicio se va a resolver junto con el siguiente.



Ejercicio 7: Escribir las palabras de un texto en orden inverso.

PrevioSiguienteTop

Se trata de realizar un programa que lea un fichero y cambie el orden de las palabras al escribirlo de tal manera que la primera palabra sea la última y viceversa. Por ejemplo:

"La casa de la pradera"

se transformaría en:

"pradera la de casa La"

Guarda este programa con el nombre de alreves2.c

Solución comentada del Ejercicio 7.


/* fichero alreves2.c */
/* Se lee cada palabra y se almacena de modo dinamico */
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
#define MAXFRA 10
void main(void)
{
   char c, temp[20];
   char **frase;
   int n, j=0, i, npal=0;
   
   /* reservar espacio para el vector de punteros a las palabras */
   frase = malloc(MAXFRA*sizeof(char*));
   printf("Introduzca una frase, y pulse \"Intro\" para finalizar.\n");
   /* Lectura de las palabras */
   do {
      c = getchar();
      if (c != ' ' && c != '\n')
         temp[j++]=c;
      else {
         temp[j++] = '\0';
         frase[npal] = malloc(j*sizeof(char));
         strcpy(frase[npal++], temp);
         j=0;
      }
   } while (c != '\n');
   
   printf("\n\nLas palabras de la frase son:");
   for (j=0; j<npal; j++)
      printf("\n%s ", frase[j]);
      
   printf("\n\nLa frase escrita al reves es:\n");
   n = npal-1;
   for (i=n; i>=0; i--) 
      printf("%s ", frase[i]);
   printf("\n\nLas palabras al reves son:\n");
   for (i=0; i<npal; i++) {
      for (j=strlen(frase[i])-1; j>=0; j--) 
         printf("%c", frase[i][j]);
      printf(" ");
   }
}


Comentario: La idea principal de este Ejercicio es ir leyendo cada una de las palabras y guardándolas en cadenas de caracteres. La variable frase es un puntero a un vector de punteros también llamado frase, que apuntan a las primeras letras de cada palabra. Con esta estructura es bastante fácil escribir las palabras en orden inverso y también escribir en orden inverso las letras de cada palabra. Se utiliza rererva dinámica de memoria.



Ejercicio 8: Descomposición de una matriz en suma de una matriz simétrica y otra antisimétrica.

PrevioSiguienteTop

En este ejercicio tienes que descomponer una matriz cualquiera en la suma de una matriz simétrica y otra antisimétrica. Ello sólo es posible para matrices cuadradas.

Para cada par de elementos de la misma posición (Simétrica [i] [j] y Antisimétrica [i] [j] ) tendrás que resolver un sistema de dos ecuaciones y dos incógnitas. Las ecuaciones son:

Matriz [i] [j] = Simétrica [i] [j] + Antisimétrica [i][j]

Matriz [j] [i] = Simétrica [i] [j] - Antisimétrica [i][j]

Recuerda que como comprobación de la solución te puede servir saber que los elementos de la diagonal principal de la matriz antisimétrica han de ser ceros.

El tamaño de la matriz a descomponer tiene que ser una variable introducida por el usuario durante la ejecución por lo que tienes que usar alocación dinámica de memoria.

Solución comentada del Ejercicio 8.


/* fichero antisim.c */
#include <stdio.h>
#include <malloc.h>
void main(void)
{
   double **a;
   double **sim;
   double **antisim;
   int    i, j, n;
   printf("Descomposición de una matriz cuadrada cualquiera en \n");
   printf("suma de simétrica y antisimétrica.\n");
   printf("\n");
   printf("Introduzca la dimensión de la matriz a descomponer:\n");
   printf("Dimensión: ");
   scanf("%d", &n);
   printf("Introduzca los valores de la matriz:\n");
   a = malloc(n*sizeof(double*));
   sim = malloc(n*sizeof(double*));
   antisim = malloc(n*sizeof(double*));
   for (i=0; i<n; i++) {
      a[i] = malloc(n*sizeof(double));
      sim[i] = malloc(n*sizeof(double));
      antisim[i] = malloc(n*sizeof(double));
   }
   for (i=0; i<n ; i++)
      for ( j = 0; j < n; j++) {
         printf("%d,%d: ", i+1, j+1);
         scanf("%lf", &a[i][j]);
      }
   for (i=0; i<n ; i++)
      for (j=0; j<n ; j++) {
         sim[i][j] = (a[i][j] + a[j][i])*0.5;
         antisim[i][j] = (a[i][j] - a[j][i])*0.5;
      }
   printf("La matriz simétrica es:\n");
   for (i=0; i<n; i++) {
      for (j=0; j<n; j++)
         printf("%lf  ", sim[i][j]);
      printf("\n");
   }
   printf("\n");
   printf("La matriz antisimétrica es:\n");
   for (i=0; i<n; i++) {
      for (j=0; j<n; j++)
         printf("%lf   ", antisim[i][j]);
      printf("\n");
   }
   printf("\n");
   printf("La matriz original debe ser:\n");
   for (i=0; i<n; i++) {
      for (j=0; j<n; j++)
         printf("%lf   ", sim[i][j] + antisim[i][j] );
      printf("\n");
   }
   printf("\n");
}


Comentario: Cualquier matriz cuadrada puede descomponerse de esta forma utilizando las fórmulas dadas en el enunciado del Ejercicio. En el programa presentado se utiliza reserva dinámica de memoria para estas tres matrices. Se lee la matriz inicial y se hallan las otras dos. Al final se suman para comprobar que se obtinne de nuevo la matriz original



Ejercicio 9: Calcular sen(x) por interpolación a partir de una tabla.

PrevioSiguienteTop

Realiza un programa principal que calcule el seno de un ángulo introducido por teclado en grados, interpolando entre los valores de una tabla. Esta tabla se encuentra en el fichero tabla.d del disco q\prac11, y está formada por una columna con los valores del seno de los ángulos comprendidos entre 0º y 90º, ambos inclusives. El ángulo cuyo seno se quiere calcular se introducirá por teclado con un formato tal y como 35.22.

Guarda el programa como interpol.c.

Solución comentada del Ejercicio 9.


/* fichero interpol.c */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define PI 3.14159265358979
void main(void)
{
   double vector[91];
   int    x1, x2;
   double y1, y2;
   double x, y, a, b, radianes;
   FILE   *fi;
   int    i;
   fi = fopen("tabla.d", "r");
   for (i=0; i <= 90; i++)
      fscanf(fi, " %lf", &vector[i]);
   printf("Este programa calcula el valor del seno de un ángulo\n");
   printf("por interpolación entre dos valores.\n");
   printf("\nIntroduzca el valor del ángulo en grados:\n");
   printf("valor: ");
   scanf("%lf", &x);
   printf("\n");
   /* Valores entre los que interpolar */
   x1 = (int)x;
   x2 = x1 + 1;
   y1 = vector[x1];
   y2 = vector[x2];
   /* Cálculo del valor interpolado */
   a = (y2 - y1)/(x2 - x1);
   b = (y1*x2 - y2*x1)/(x2 - x1);
   y = a*x + b;
   printf("El valor del seno de %6.2lf es %18.15lf.\n", x, y);
   radianes = x*PI/180.0;
   printf("%s%6.2lf es %18.15lf%s",
         "El valor del seno de ", x, 
         sin(radianes), ", calculado con math.h");
}


Comentario: La interpolación entre una serie de valores almacenados en memoria es una de las posibles formas de evaluar funciones con expresiones mateméticas complicadas o incluso que no responden a ninguna expresión concreta. En este ejemplo se interpola linealmente para hallar el valor de la función seno(x) y el resultado se compara con el que se obtiene con la función definida en math.h. Puede observarse que sólo se obtienen unas pocas cifras exactas.