universidad veracruzana

Anuncio
UNIVERSIDAD VERACRUZANA
FACULTAD DE INGENIERÍA MECÁNICA ELÉCTRICA
“MANUAL DE PROGRAMAS APLICADOS A MÉTODOS NUMÉRICOS”
TRABAJO PRÁCTICO EDUCATIVO
PARA ACREDITAR LA EXPERIENCIA EDUCATIV A DE:
TRABAJO RECEPCIONAL
P R E S E N T A:
J u an A rtu r o B ad i l l o R i o s
J os é L u i s Orti z d e l a lu z
DIRECTOR DE TRABAJO PRÁCTICO EDUCATIVO:
ISC. KARLOS REYES ORTEGA
POZA RICA DE HGO; VER.
2011
ÍNDICE
INTRODUCCIÓN
1
CAPITULO I
JUSTIFICACIÓN
3
TIPO Y NATURALEZA DEL TRABAJO
3
CARACTERÍSTICAS Y FUNCIONES ESCENCIALES
3
CAPITULO II
PROCESOS DEL TRABAJO
6
1.- ECUACIONES LINEALES
1.1.- Definición y clasificación
6
1.2.- Gauss Simple
6
1.2.1.- Ejemplo propuesto
9
1.2.2.- Diagrama de Flujo
11
1.2.3.- Pseudocódigo
13
1.2.4.- Programas en C
13
1.2.5.- Demostración del Programa
15
1.3.- Gauss Seidel
16
1.3.1.- Ejemplo propuesto
17
1.3.2.- Diagrama de Flujo
19
1.3.3.- Pseudocódigo
20
1.3.4.- Programas en C
21
1.3.5.- Demostración del Programa
23
1.4.- Gauss Jordan
25
1.4.1.- Ejemplo propuesto
25
1.4.2.- Diagrama de Flujo
28
1.4.3.- Pseudocódigo
30
1.4.4.- Programas en C
32
1.4.5.- Demostración del Programa
2.- ECUACIONES NO LINEALES
33
34
2.1.- Definición y tipos de ecuaciones no lineales
34
2.2.- Bisección
35
2.2.1.- Ejemplo propuesto
36
2.2.2.- Diagrama de Flujo
39
2.2.3.- Pseudocódigo
40
2.2.4.- Programas en C
41
2.2.5.- Demostración del Programa
42
2.3.- Falsa Posición
43
2.3.1.- Ejemplo propuesto
45
2.3.2.- Diagrama de Flujo
47
2.3.3.- Pseudocódigo
48
2.3.4.- Programas en C
49
2.3.5.- Demostración del Programa
51
2.4.- Newton-Raphson
52
2.4.1.- Ejemplo propuesto
53
2.4.2.- Diagrama de Flujo
55
2.4.3.- Pseudocódigo
56
2.4.4.- Programas en C
56
2.4.5.- Demostración del Programa
57
3.- ECUACIONES DIFERENCIALES ORDINARIAS
58
3.1.- Concepto y clasificación
58
3.2.- Euler
59
3.2.1.- Ejemplo propuesto
60
3.2.2.- Diagrama de Flujo
64
3.2.3.- Pseudocódigo
65
3.2.4.- Programas en C
66
3.2.5.- Demostración del Programa
67
3.3.-Runge-Kutta
68
3.3.1.- Ejemplo propuesto
69
3.3.2.- Diagrama de Flujo
71
3.3.3.- Pseudocódigo
72
3.3.4.- Programas en C
74
3.3.5.- Demostración del Programa
75
4.- INTEGRACIÓN
76
4.1.- Concepto y clasificación
76
4.2.- Simpson 1/3
76
4.2.1.- Ejemplo propuesto
77
4.2.2.- Diagrama de Flujo
80
4.2.3.- Pseudocódigo
81
4.2.4.- Programas en C
82
4.2.5.- Demostración del Programa
83
COSTOS
84
CAPITULO III
APORTACIONES Y CONTRIBUCIONES AL DESARROLLO
86
BIBLIOGRAFÍA
87
ANEXOS
88
INTRODUCCIÓN
Los métodos numéricos son técnicas mediante las cuales es posible resolver
problemas mediante el uso de operaciones aritméticas. Este manual tiene como
meta dejar plasmados los métodos más importantes que abarca la experiencia
educativa de métodos numéricos para la solución de sistemas de ecuaciones
lineales, no lineales y a su vez de derivadas e integrales. Cabe destacar que el
objetivo fundamental de este trabajo es ofrecer una guía práctica para que los
estudiantes puedan seguir paso a paso la elaboración de los programas desde el
desarrollo de los algoritmos a través de los diagramas de flujo hasta la realización
del código fuente pasando por las etapas de compilación y ejecución exitosa del
programa.
1
CAPITULO I
2
JUSTIFICACIÓN
Dada la importancia que tienen los métodos numéricos en ingeniería, el alumno
necesita entender y comprender la experiencia educativa no solo desde un enfoque
teórico sino también práctico, entendido esto como la generación de los programas
para aplicar los métodos aprendidos durante la experiencia educativa, brindándoles
así una herramienta que podrán utilizar en el ámbito laboral y les permitirá ahorrar
tiempo y esfuerzo, debido a que mediante la aplicación de los métodos numéricos
es posible manejar sistemas de ecuaciones grandes los cuales de otra forma serian
difíciles de resolver de forma analítica.
Sin embargo, ésta materia actualmente no cuenta con un manual para el desarrollo
de los programas que apliquen los diferentes métodos vistos en clase; por tal
motivo, éste trabajo pretende ser una guía al momento de cursar esta experiencia y
ahorrar tiempo al alumno en la investigación del programa, desarrollo y aplicación
de los mismos.
TIPO Y NATURALEZA DEL TRABAJO
El tipo de trabajo es práctico educativo donde la información será seleccionada y
estructurada con el fin de contribuir en el aprendizaje de los estudiantes y como
soporte académico para los docentes que imparten la experiencia educativa de
métodos numéricos dentro de la Universidad Veracruzana, así mismo permitirá
mejorar las bases para la aplicación de los métodos numéricos mediante programas
los cuales sirven como herramienta para la solución de problemas en ingeniería.
CARACTERÍSTICAS Y FUNCIONES ESCENCIALES
El manual incluirá específicamente los temas de mayor interés para los estudiantes
de la Facultad de Ingeniería Mecánica Eléctrica, los cuales servirán como apoyo al
cursar la experiencia educativa de métodos numéricos.
La función principal de este trabajo, será proporcionar al alumno las bases para
conocer y aplicar los fundamentos de los métodos numéricos para la solución de
problemas de ingeniería, implementando diversos algoritmos a través de un
lenguaje de programación como el Lenguaje C y de esta manera obtener por
consecuencia programas que permitan mejorar el entendimiento de los métodos
numéricos.
3
CAPITULO II
PROCESO
DEL
TRABAJO
1.- ECUACIONES LINEALES
1.1.- Definición y clasificación
En este capítulo estudiaremos las técnicas de solución de sistemas de ecuaciones
lineales cuadrados Ax = b.
Un sistema de ecuaciones lineales, también conocido como sistema lineal de
ecuaciones o simplemente sistema lineal, es un conjunto de ecuaciones lineales
sobre un cuerpo o un anillo conmutativo.
El problema consiste en encontrar los valores desconocidos de las variables x1, x2 y
x3 que satisfacen las tres ecuaciones.
Para establecer la velocidad de cálculo y el "trabajo computacional" en los métodos
directos, se analiza el número de operaciones de éstos y con base en ello se
determinan sus necesidades de memoria. Como consecuencia de lo anterior, se da
particular atención a los sistemas especiales: simétricos, bandeados y dispersos,
entre otros. Así, estudiaremos los métodos que aprovechan estas características
para lograr reducir con esto el número de operaciones y los requerimientos de
máquina.
Posteriormente se exponen y se desarrollan tres métodos numéricos aplicados a las
ecuaciones lineales como son: Gauss Simple, Gauss Seidel y Gauss Jordan.
Dado que el mundo real puede verse como un grupo de objetos o partes trabajando
en conjunto o bien conectadas de alguna manera formando un todo, brindar al
alumno una mejor comprensión de la extraordinaria cantidad de situaciones que
pueden representarse con los sistemas o grupos de ecuaciones donde cada una de
ellas corresponde a alguna de sus partes, por ejemplo: en circuitos, estructuras.
1.2.- Gauss Simple
Esta técnica básica puede extenderse a sistemas grandes de ecuaciones
desarrollando un esquema sistemático o algorítmico para eliminar incógnitas y
sustituir hacia atrás.
La eliminación de Gauss es el más básico de dichos esquemas.
Aquí se presentan las técnicas sistemáticas para la eliminación hacia adelante y la
sustitución hacia atrás que la eliminación gaussiana comprende. Dado que éstas
técnicas son muy adecuadas para utilizarse en computadoras, se requieren algunas
modificaciones para obtener un algoritmo confiable. En particular, el programa debe
evitar la división entre cero. Al siguiente método se le llama eliminación gaussiana
simple, ya que no evita este problema.
6
El método está ideado para resolver un sistema general de n ecuaciones:
a11x1+a12x2+a13x3+….+a1nxn=b1
(Ec. 1.1a)
a21x1+a22x2+a23x3+….+a2nxn=b2
(Ec. 1.1b)
.
.
.
.
an1x1+an2x2+an3x3+….+annxn=bn
(Ec. 1.1c)
Como en el caso de dos ecuaciones, la técnica para resolver ecuaciones consiste
en dos fases: la eliminación de las incógnitas y su solución mediante sustitución
hacia atrás.
La eliminación hacia adelante de incógnitas. La primera fase consiste en reducir el
conjunto de ecuaciones a un sistema triangular superior (figura 1.1). El paso inicial
será eliminar la primera incógnita, x1, desde la segunda hasta la n-ésima ecuación.
Para ello se multiplica la ecuación (Ec. 1.1) por
para obtener
(Ec. 1.2)
Ahora, esta ecuación se resta de la ecuación (Ec. 1.2) para dar
O
Donde el superíndice prima indica que los elementos han cambiado sus valores
originales
7
Eliminación
hacia adelante
Sustitución
hacia atrás
Figura 1.1
Las dos fases de la eliminación de Gauss: eliminación hacia adelante y sustitución
hacia atrás. Los superíndices prima indican el número de veces que se han
modificado los coeficientes y constantes.
El procedimiento se repite después con las ecuaciones restantes. Por ejemplo, la
ecuación (Ec. 1.1) se puede multiplicar por
y el resultado se resta de la
tercera ecuación. Se repite el procedimiento con las ecuaciones restantes y da
como resultado el siguiente sistema modificado:
a11x1+a12x2+a13x3+….+a1nxn=b1
(Ec. 1.3a)
a´22x2+a´23x3+….+a´2nxn=b´2
(Ec. 1.3b)
a´32x2+a´33x3+….+a´3nxn=b´3
(Ec. 1.3c)
a´n2x2+a´n3x3+….+a´nnxn=b´n
(Ec. 1.3d)
En los pasos anteriores, la ecuación (Ec. 1.1a) se llama la ecuación pivote, y
se
denomina el coeficiente o elemento pivote. Observe que el proceso de multiplicación
del primer renglón por
es equivalente a dividirla entre
y multiplicarla por
. Algunas veces la operación de división es referida a la normalización. Se hace
esta distinción porque un elemento pivote cero llega a interferir con la normalización
al causar una división entre cero. Más adelante se regresará a este punto
importante, una vez que se complete la descripción de la eliminación de Gauss
simple.
Ahora se repite el procedimiento antes descrito para eliminar la segunda incógnita
en las ecuaciones (Ec. 1.3c) hasta (Ec. 1.3d). Para realizar esto, multiplique la
ecuación (Ec. 1.3b) por
y reste el resultado de la ecuación (Ec. 1.3c). Se
realiza la eliminación en forma similar en las ecuaciones restantes para obtener.
a11x1+a12x2+a13x3+….+a1nxn=b1
a´22x2+a´23x3+….+a´2nxn=b´2
a´´33x3+….+a´´3nxn=b´´3
.
.
.
.
a´´n2x2+a´´n3x3+….+a´nnxn=b´n
8
Donde el superíndice biprima indica que los elementos se han modificado dos
veces.
El procedimiento puede continuar usando las ecuaciones pivote restantes. La última
manipulación en esta secuencia es el uso de la
ésima ecuación para eliminar
el término
de la -ésima ecuación. Aquí el sistema se habrá transformado en un
sistema triangular superior
(Ec. 1.4a)
(Ec. 1.4b)
(Ec. 1.4c)
.
.
.
.
.
(Ec. 1.4d)
1.2.1.- Ejemplo propuesto
Para resolver el siguiente conjunto de ecuaciones. Emplee la eliminación de Gauss
(Ec.1.5)
(Ec.1.6)
(Ec.1.7)
Las operaciones se efectuarán usando seis cifras significativas.
La primera parte del procedimiento es la eliminación hacia adelante. Se multiplica la
ecuación (Ec. 1.5) por (0.1)/3 y se resta el resultado de la ecuación (Ec. 1.6) para
obtener
Después, se multiplica la ecuación (Ec. 1.5) por (0.3)/3 y se resta de la ecuación
(Ec.1.7) para eliminar
ecuaciones es
. Luego de efectuar estas operaciones, el sistema de
(Ec.1.8)
(Ec.1.9)
(Ec.1.10)
9
Para completar la eliminación hacia adelante,
debe eliminarse de la ecuación
(Ec.1.10). Para llevar a cabo esto, se multiplica la ecuación (Ec.1.9) por 0.190000/7.00333 y se resta el resultado de la ecuación (Ec.1.10). Esto elimina
de la tercera ecuación y reduce el sistema a una forma triangular superior:
(Ec.1.11)
(Ec.1.12)
(Ec.1.13)
Ahora se pueden resolver estas ecuaciones por sustitución hacia atrás. En primer
lugar, de la ecuación (Ec.1.13) se despeja x3
(Ec.1.14)
Este resultado se sustituye en la ecuación (Ec.1.12):
7.003332
- 0.293333 (7.00003) = -19.5617
de la que se despeja
(Ec.1.15)
Por último, las ecuaciones (Ec.1.14) y (Ec.1.15) se sustituyen en la (Ec.1.8):
3
- 0.1 (-2.50000) - 0.2 (7.00003) = 7.85
de la que se despeja,
aunque hay un pequeño error de redondeo en la ecuación (Ec.1.14), los resultados
son muy cercanos a la solución exacta,
. Esto se verifica al
sustituir los resultados en el sistema de ecuaciones original
10
1.2.2.- Diagrama de flujo
Diagrama de Flujo Gauss Simple eliminación hacia adelante
11
Diagrama de Flujo Gauss Simple sustitución hacia atrás
12
1.2.3.- Pseudocódigo
a) DO k = 1, n – 1
DO i = k + 1, n
factor =
DO j = k + 1 to n
ai,j = ai,j – factor . ak,j
END DO
bi = bi - factor •
END DO
END DO
b) xn = bn / an,n
DO i=n-1, 1,-1
sum = O
DO j = i + 1, n
sum = sum + ai,j . xj
END DO
xi = (bi - sum) / ai, i
END DO
Pseudocódigo que realiza a) la eliminación hacia adelante y b) la sustitución hacia
atrás.
1.2.4.- Programas en C
Gauss Simple
#include <math.h>
#include <stdio.h> /*para printf(),scanf()*/
#include <conio.h> /*para getch(),clrscr()*/
//#include <stdlib.h>/*para exit()*/
//#include <dos.h>
#define NUMEL
20
#define INTERVALOS 0
float A[25][25], B[25], S[25],X[25];
MAIN
printf("\n METODO DE GAUSS SIMPLE");
printf("\n Numero de Ecuaciones = ");
scanf("%d",&n);
printf("\n Inserte cada uno de los coeficientes\n");
for(i=1;i<=n;i++)
{
printf("\n Fila %d \n",i);
for(j=1;j<=n;j++)
{
printf(" Ingrese A(%d,%d) = ",i,j);
scanf("%f",&A[i][j]);
}
}
printf("\n Inserte cada uno de los terminos independientes\n");
for(i=1;i<=n;i++){
{
printf(" Ingrese B(%d) = ",i);
scanf("%f",&B[i]);
}
}
13
printf("\n Tolerancia para el calculo = ");
scanf("%f",&tol);
Gauss( n,tol, &er );
printf("\n\n RAICES DEL SISTEMA\n ");
for(i=1;i<=n;i++){
printf("\n X(%d) = %6.4f",i,X[i]);
}
printf("\n\n Fin del programa");
getch();
}
void Gauss( int n, float tol, int *er){
int i,j;
/*
*/
// IMPRESION DE LOS COEFICIENTES RECIBIDOS
printf("\n IMPRESION DE COEFICIENTES\n");
for(i=1;i<=n;i++)
{
printf("\n Fila %d \n",i);
for(j=1;j<=n;j++)
{
printf(" A(%d,%d) = %f",i,j, A[i][j]);
}
printf("\n");
}
getch();
*er = 0;
for (i=1;i<=n;i++){
S[i] = abs(A[i][1]);
for(j=2;j<=n;j++)
if( abs(A[i][j]>S[i]))
S[i] = A[i][j];
}
14
1.2.5.- Demostración del programa
15
1.3.- Gauss Seidel
El método de Gauss-Seidel emplea valores iniciales y después itera para obtener
las mejores aproximaciones a la solución. El método de Gauss-Seidel es
particularmente adecuado cuando se tiene gran número de ecuaciones. Los
métodos de eliminación pueden estar sujetos a errores de redondeo. Debido a que
el error en el método de Gauss-Seidel es determinado por el número de iteraciones,
no es un tema que preocupe. Aunque existen ciertos ejercicios donde la técnica de
Gauss-Seidel no convergerá al resultado correcto, estudiaremos otras ventajas que
se tienen dicho método.
Una matriz bandeada es una matriz cuadrada en donde la diagonal principal son
valores diferentes a cero (valor=1) y los que se encuentran fuera de la diagonal son
ceros. Los sistemas bandeados se encuentran con frecuencia en la práctica
científica y de la ingeniería. Por ejemplo, tales sistemas aparecen en la solución de
ecuaciones diferenciales.
Aunque la eliminación de Gauss o la convencional se emplean para resolver
sistemas de ecuaciones bandeados, resultan ser ineficientes, debido a que si el
pivoteo no es necesario, ninguno de los elementos fuera de la banda cambiará su
valor original igual a cero. Así, será necesario utilizar tiempo y espacio en el
almacenamiento y en el manejo de estos ceros inútiles. Si se sabe de antemano
que el pivoteo no es necesario, se pueden desarrollar algoritmos muy eficientes en
los que no intervengan los ceros fuera de la banda. Como en muchos problemas
con sistemas bandeados, no se requiere el pivoteo; los algoritmos alternos, que se
describirán a continuación, son los métodos seleccionados para tal fin.
El método de Gauss-Seidel es el método iterativo más comúnmente usado.
Suponga que se da un sistema de n ecuaciones:
Suponga que se limita a un conjunto de ecuaciones de 3 x 3. Si los elementos de la
diagonal no son todos cero, la primera ecuación se puede resolver para
la
segunda para
y la tercera para , para obtener
(Ec.1.16a)
(Ec.1.16b)
(Ec.1.16c)
Ahora, se puede empezar el proceso de solución al escoger valores iniciales para
las . Una forma simple para obtener los valores iniciales es suponer que todos son
cero. Estos ceros se sustituyen en la ecuación (Ec.1.16a), la cual se utiliza para
calcular un nuevo valor
Después, se sustituye este nuevo valor de
junto con el valor previo cero de
en la ecuación (Ec.1.16b) y se calcula el
nuevo valor de . Este proceso se repite con la ecuación (Ec.1.16c) para calcular
un nuevo valor de . Después se regresa a la primera ecuación y se repite todo el
procedimiento hasta que la solución converja suficientemente cerca de los valores
verdaderos. La convergencia se verifica usando el criterio de la siguiente ecuación
de error relativo porcentual.
16
(Ec.1.17)
Para toda las
respectivamente.
donde
y
son las iteraciones actuales y previas
1.3.1.- Ejemplo propuesto
Planteamiento del problema.
Use el método de Gauss-Seidel para obtener la
solución del sistema usado en el ejemplo:
Recuerde que la verdadera solución es
.
Solución. Primero, despeje la incógnita sobre la diagonal para cada una de las
ecuaciones.
(Ec.1.18a)
(Ec.1.18b)
(Ec.1.18c)
Suponiendo que
son cero, se utiliza la ecuación (Ec.1.18a) para calcular
Este valor, junto con el valor de
calcular
= 0, se sustituye en la ecuación (Ec.1.18b) para
17
la primera iteración termina al sustituir los valores calculados para
ecuación (Ec.1.18c) para dar
en la
en la segunda iteración, se repite el mismo proceso para calcular
El método es, por lo tanto, convergente hacia la verdadera solución. Es posible
aplicar iteraciones adicionales para mejorar los resultados. Sin embargo, es un
problema real no se podría saber con anticipación el resultado correcto. En
consecuencia, la ecuación (Ec.1.17) nos da un medio para estimar el error relativo
porcentual. Por ejemplo, para ,
Para
los errores estimados son
. Observe
que, como cuando se determinaron las raíces de una sola ecuación, las
formulaciones como la ecuación (Ec.1.17) de Error relativo porcentual, usualmente
ofrecen una valoración conservativa de la convergencia. Así, cuando estas se
satisfacen aseguran que el resultado se conozca con al menos, la tolerancia
especificada por .
18
1.3.2.- Diagrama de flujo
Diagrama de Flujo Gauss Seidel
19
1.3.3.- Pseudocódigo
SUBROUTINE Gseid (a, b, n, x, imax, es, lambda)
DO i = 1,n
dummy = ai.i
DO j = 1,n
ai,j = ai,j / dummy
END DO
bi = ai / dummy
END DO
DO i = 1, n
sum = bi
DO j = 1, n
j THEN sum =sum – ai, j *xj
IF i
END DO
xi = sum
END DO
iter =1
DO
centinela = 1
DO i = 1, n
old = xi
sum = bi
DO j = 1, n
IF i
j THEN sum =sum – ai, j *xj
END DO
xi = lambda * sum +( 1 - lambda) * old
IF centinela = 1 AND x1
0 . THEN
ea=ABS (( xi –old) / xi )*100.
IF ea
es THEN centinela = 0
END IF
END DO
iter = iter + 1
IF centinela = 1 OR (iter
I max) EXIT
END DO
END Gseid
20
1.3.4. - Programas en C
GAUSS SEIDEL
#include <stdio.h>
#include <math.h>
#include <iostream.h>
#include <conio.h>
#define L 10
#define P L
MAIN
cout<<"\n\n METODO DE GAUSS SEIDEL ";
cout<<"\n\n Numero de incognitas Posibles en el sistema: ";
scanf("%d",&n);
Gauss_Seidel(n);
titulo(n);
resultados();
cout<<"\n\nLos resultado son ";
for(x=0;x<n;x++)
{
RESULTADOS[x]=X[x];
cout<<"\nX["<<x<<"]= "<<RESULTADOS[x];
}
getch();
}
void resultados()
{
int q=0,i=1,t=3,s=n,r=0;
int sw=0,w=0,ite=0,h=0;
while((sw==0)&&(w<20))
{
h=0;
while(h<n)
{
if(tabla[r]==tabla[r+s])
{
cont++;
}
if(cont==n)
sw=1;
r++;
s++;
h++;
}
ite++;
w++;
}
w=ite-1;
for(int j=0;j<w;j++)
{
t=t+2;
if((i%10==0))
{
textcolor(LIGHTRED+BLINK);
gotoxy(5,t-2);
cprintf("\n\n ---Presione una tecla para ingresar ala tabla!!! ");
getch();
textcolor(GREEN);
clrscr();
t=5;
titulo(n);
}
gotoxy(15,t);cout<<i<<"ø";
int y=20,z=0;
for(int r=0;r<n;r++)
21
{
}
}
}
i++;
gotoxy(y+z,t);cout<<tabla[q];
q++;
z=z+10;
void main()
{
22
1.3.5.- Demostración del programa
23
24
1.4.- Gauss Jordan
El método de Gauss-Jordan es una variación de la eliminación de Gauss. La
principal diferencia consiste en que cuando una incógnita se elimina en el método
de Gauss-Jordan. Ésta es eliminada de todas las otras ecuaciones, no sólo de las
subsecuentes. Además, todos los renglones se normalizan al dividirlos entre su
elemento pivote. De esta forma, el paso de eliminación genera una matriz identidad
en vez de una triangular (figura 1.2). En consecuencia, no es necesario usar la
sustitución hacia atrás para obtener la solución. El método se ilustra mejor con un
ejemplo.
Figura 1.2
Representación gráfica del método de Gauss-Jordan.
1.4.1.- Ejemplo propuesto
Método de Gauss-Jordan
Para resolver el siguiente conjunto de ecuaciones. Con la técnica de Gauss-Jordan
Solución.
mentada
Primero, exprese los coeficientes y el lado derecho como una matriz au-
3
-0.1 -0.2 7.85
0.1
7
0
-0.2
-0.3
-19.3
10
71.4
25
Luego normalice el primer renglón, dividiéndolo entre el elemento pivote, 3, para
obtener
1
-0.0333333 -0.066667 2.61667
0.1
7
0.3
-0.3
-0.2
-19.3
10
71.4
El término
se elimina del segundo renglón restando 0.1 veces al primer renglón del
segundo. En forma similar, restando 0.3 veces el primer renglón del tercero, se
eliminará el término del tercer renglón:
1
-0.0333333 -0.066667
0
7.00333
0
-0.190000
2.61667
-0.293333 -19.5617
10.0200
70.6150
En seguida, se normaliza el segundo renglón dividiéndolo entre 7.00333:
1
0
-0.0333333
1
-0.066667 2.61667
-0.0418848 - 2.79320
0
-0.190000
10.0200
70.6150
Al reducir los términos x2 de las ecuaciones primera y tercera se obtiene
1
0
0
0
1
0
-0.0680629 2.52356
-0.0418848 -2.79320
10.01200
70.0843
El tercer renglón se normaliza después al dividirlo entre 10.0120:
1
0 -0.0680629
0
1
0
0
2.52356
-0.0418848 -2.79320
1
7.00003
26
Por último los términos
obtener
se pueden eliminar de la primera y segunda ecuación para
1 0 0
3.00000
0 1 0
-2.50001
0 0 1
7.00003
De esta forma, como se muestra en la figura 1.2 la matriz de coeficientes se ha
transformado en la matriz identidad, y la solución se obtiene en el vector del lado
derecho. Observe que no se requiere la sustitución hacia atrás para llegar a la
solución.
27
1.4.2.- Diagrama de Flujo
N,M,I,A,J
Diagrama de Flujo Gauss Jordan
28
SUBROUTINE
GAUSS
(N,M,A)
I,PIVOTE,A,J,K,N,
M,CERO
Diagrama de Flujo principal Gauss Jordan subrutina
29
1.4.3.- Pseudocódigo
SUB Gauss (a, b, n, x, tol, er)
DIMENSION s (n)
er = 0
DO i =l, n
si = ABS (ai,1 )
DO j = 2, n
IF ABS (Ai,j)
si THEN si = abs (a i , j)
END DO
END DO
SUB Eliminate (a, s, n, b, tol, er)
IF er
-1 THEN
CALL Substitute (a , n , b, x)
END IF
END Gauss
CALL Eliminate (a, s, n, b, tol, er)
DO k = 1, n – 1
CALL Pivot (a, b, s, n, k)
IF ABS (ak, k/ sk)
tol THEN
er = -1
EXIT DO
END IF
DO i = k +1, n
factor = a i, k/ a k, k
DO j= k + 1, n
ai,j = a i, j - factor*ak,j
END DO
bi = bi – factor * bk
END DO
END DO
IF ABS (ak,k / sk) tol THEN er = -1
END Eliminate
30
SUB Pivot (a, b, s, n, k)
p=k
big = ABS(ak,k/sk)
DO i i= k + 1, n
dummy = ABS(aii,k/sii)
IF dummy > big THEN
big = dummy
p=ii
END IF
END DO
IF p
k THEN
DO jj = k, n
dummy = ap, jj
ap, jj = ak, jj
ak, jj = dummy
END DO
dummy = bp
bp = bk
bk = dummy
dummy = sp
sp = sk
sk = dummy
END IF
END pivot
SUB Substitute (a, n, b, x)
xn = bn / an,n
DO i = n – 1, 1, -1
sum = 0
DO j = i + 1, n
sum = sum + ai,j * xj
END DO
xi = (bi – sum) / ai,i
END DO
END Substitute
31
1.4.4.- Programas en C
GAUSS JORDAN
#include <math.h>
#include <stdio.h> /*para printf(),scanf()*/
#include <conio.h> /*para getch(),clrscr()*/
//#include <stdlib.h>/*para exit()*/
//#include <dos.h>
#define NUMEL
20
#define INTERVALOS 0
float A[25][25], B[25], S[25],X[25];
MAIN
printf("\n METODO DE GAUSS JORDAN");
printf("\n\n Ingrese el numero de incognitas \n\n Numero de Ecuaciones = ");
scanf("%d",&n);
printf("\n Inserte cada uno de los coeficientes\n");
for(i=1;i<=n;i++)
{
printf("\n Fila %d \n",i);
for(j=1;j<=n+1;j++)
{
printf(" Ingrese a(%d,%d) = ",i,j);
scanf("%f",&a[i][j]);
}
}
m=n+1;
do
{
if(a[1][1]==0)
{k=m-1;
for(i=2;i<=k;i++)
{if(a[i][1]!=0)
{for(j=1;j<=m;j++)
{
apoyo=a[i][j];
a[i][j]=a[1][j];
a[1][j]=apoyo;
}
}
}
}
else
{for(j=2;j<=m;j++)
{for(i=2;i<=n;i++)
{b[i-1][j-1]=a[i][j]-a[1][j]*a[i][1]/a[1][1];}
}
for(j=2;j<=m;j++)
{b[n][j-1]=a[1][j]/a[1][1];}
m=m-1;
for(j=1;j<=m;j++)
{for(i=1;i<=n;i++)
{a[i][j]=b[i][j];}
}
}
}
while(m>1);
printf("\n\n RAICES DEL SISTEMA\n ");
for(i=1;i<=n;i++)
{printf("\n X(%d) = %1.4f",i,a[i][1]);}
printf("\n\n Fin del programa");
getch();
}
}
32
1.4.5.- Demostración del programa
33
2.- ECUACIONES NO LINEALES
2.1.- Definición y tipos de ecuaciones no lineales
En este capítulo estudiaremos diversos métodos para resolver ecuaciones no lineales
en una incógnita
, aprovechando los conceptos básicos del cálculo y las
posibilidades gráficas y de cómputo de la tecnología moderna. Sistemáticamente a la
interpretación gráfica de los métodos, a fin de mostrar visualmente su funcionamiento y
de enriquecer las imágenes asociadas con ellos; de igual manera, se generan tablas
en la aplicación de cada técnica para analizar el comportamiento numérico y
eventualmente detener el proceso.
Se ha organizado el material como métodos de uno y dos puntos, de los segundos el
de posición falsa. Esto, junto con el concepto de orden de convergencia, nos permitirá
tener los elementos suficientes para seleccionar la técnica más adecuada para una
situación dada. Finalizamos el capítulo con las técnicas para resolver ecuaciones
polinomiales. El propósito de este capítulo es que el estudiante cuente con los
elementos básicos, computacionales y de criterio, apropiados para resolver el
problema algebraico clásico de encontrar las raíces reales y complejas de la ecuación
, en donde las técnicas algebraicas de "despejar" la incógnita no sean
aplicables, como es el caso de
, o bien resulten imprácticas.
Por último, es importante señalar lo difícil que resulta pensar en un tópico de
matemáticas o ingeniería que no involucre ecuaciones de esta naturaleza.
La solución de este sistema consta de un conjunto de valores
hacen que todas las ecuaciones sean iguales a cero.
que simultáneamente
Presentamos los métodos, para el caso en que las ecuaciones simultáneas son
lineales, es decir, que se puedan expresar en la forma general
donde la b y la a son constantes. A las ecuaciones algebraicas y trascendentales que
no se pueden expresar de esta forma se les llama ecuaciones no lineales. Por ejemplo,
Y
son dos ecuaciones simultáneas no lineales con dos incógnitas,
expresan en la forma de la ecuación como
y , las cuales se
Así, la solución serían los valores de y de y que hacen a las funciones
y
iguales a cero. La mayoría de los métodos para determinar tales soluciones son
extensiones de los métodos abiertos para resolver ecuaciones simples.
34
2.2.- Bisección
Al aplicar las técnicas gráficas se observa que
cambia de signo a ambos lados
de la raíz. En general, si
es real y continúa en el intervalo que va desde hasta
y
y
tienen signos opuestos, es decir,
(Ec.2.1)
entonces hay al menos una raíz real entre
y
.
Los métodos de búsqueda incremental aprovechan esta característica localizando
un intervalo en el que la función cambie de signo. Entonces, la localización del
cambio de signo (y, en consecuencia, de la raíz) se logra con más exactitud al dividir
el intervalo en varios subintervalos. Se investiga cada uno de estos subintervalos
para encontrar el cambio de signo. El proceso se repite y la aproximación a la raíz
mejora cada vez más en la medida que los subintervalos se dividen en intervalos
cada vez más pequeños.
Figura 2.1
35
El método de bisección, conocido también como de corte binario, de partición de
intervalos o de Bolzano, es un tipo de búsqueda incremental en el que el intervalo
se divide siempre a la mitad. Si la función cambia de signo sobre un intervalo, se
evalúa el valor de la función en el punto medio. La posición de la raíz se determina
situándola sobre el punto medio del subintervalo, dentro del cual ocurre un cambio
de signo. El proceso se repite hasta obtener una mejor aproximación. En la figura
2.1 se presenta un algoritmo sencillo para los cálculos de la bisección. En la figura
2.3 se muestra una representación gráfica del método. Los siguientes ejemplos se
harán a través de cálculos reales involucrados en el método.
2.2.1.- Ejemplo propuesto
Planteamiento del problema. Emplee el método de bisección para resolver el
siguiente problema.
Figura 2.2
El método gráfico para determinar las raíces de una ecuación
Solución. El primer paso del método de bisección consiste en asignar dos valores
iniciales a la incógnita (en este problema, c) que den valores de
con diferentes
signos. En la figura 2.2 se observa que la función cambia de signo entre los valores
12 y 16. Por lo tanto, la estimación inicial de la raíz
se encontrará en el punto
medio del intervalo
Dicha aproximación representa un error relativo porcentual verdadero de
(note que el valor verdadero de la raíz es 14.7802). A continuación calculamos el
producto de los valores en la función en un límite inferior y en el punto medio:
36
que es mayor a cero y, por lo tanto, no ocurre cambio de signo entre el límite
inferior y el punto medio. En consecuencia, la raíz debe estar localizada entre 14 y
16.
Entonces, se crea un nuevo intervalo redefiniendo el límite inferior como 14 y
determinando una nueva aproximación corregida de la raíz
Figura 2.3
Una representación gráfica del método de bisección. La gráfica presenta las
primeras tres iteraciones del ejemplo 2.2.1
la cual representa un error porcentual verdadero , = 1.5%. Este proceso se repite
para obtener una mejor aproximación. Por ejemplo,
37
Por lo tanto, la raíz está entre 14 y 15. El límite superior se redefine como 15 y la
raíz estimada para la tercera iteración se calcula así:
que representa un error relativo porcentual , = 1 .9%. Este método se repite
hasta que el resultado sea suficientemente exacto para satisfacer sus necesidades.
En el ejemplo anterior, se observa que el error verdadero no disminuye con cada
iteración. Sin embargo, el intervalo donde se localiza la raíz se divide a la mitad en
cada paso del proceso. Como se estudiará en la siguiente sección, el ancho del
intervalo proporciona una estimación exacta del límite superior del error en el
método de bisección.
38
2.2.2.- Diagrama de Flujo
Diagrama de Flujo Bisección
39
2.2.3.- Pseudocódigo
FUNCTION Bisect(xl, xu, es, imax, xr, iter, ea)
iter = 0
fl = f(xl)
DO
xrold = xr
xr = (xl + xu) / 2
fr = f( xr )
iter = iter + 1
IF xr
0 THEN
ea = ABS((xr – xrold) / xr) * 100
END IF
test = fl * fr
IF test
0 THEN
xu = xr
ELSE IF test
0 THEN
xl = xr
fl= fr
ELSE
ea = 0
END IF
IF ea
es OR iter
imax EXIT
END DO
Bisect = xr
END Bisect
40
2.2.4.- Programas en C
BISECCIÓN
#include <math.h>
#include <stdio.h> /*para printf(),scanf()*/
#include <conio.h> /*para getch(),clrscr()*/
//#include <stdlib.h>/*para exit()*/
//#include <dos.h>
#define NUMEL
20
#define INTERVALOS 0
float A[25][25], B[25], S[25],X[25];
MAIN
printf("\n METODO DE BISECCION\n");
printf( " Teclea valor minimo = ");
scanf("%f",&xmin);
printf("\n");
printf( " Teclea valor maximo = ");
scanf("%f",&xmax);
printf("\n");
printf( " Teclea valor de error esperado = ");
scanf("%f",&error);
printf("\n");
printf( " Teclea iteraciones maximas = ");
scanf("%f",&iteraciones);
printf("\n");
c1 = Bisect(xmin, xmax, error, iteraciones);
printf( " Resultado = %f", c1);
}
getch();
}
float Bisect( float x1,float xu, float es, int imax){
int iter;
float f1, fr, test;
float xold, xr, ea=0;
iter = 0;
f1 = F(x1, 10, 40, 68.1);
do{
xold = xr;
xr = (x1+xu)/2;
iter = iter +1;
if( xr != 0 ){
ea = abs((xr-xold)/xr)*100;
}
fr = F(xr, 10, 40, 68.1);
test = f1*fr;
if( test < 0 ){ // Se determina si hay una raiz entre x1 y xr, cambio de signo
xu = xr;
}
else if ( test > 0 ) {
x1 = xr;
f1 = fr;
}
else // Se encontro una raiz
ea = 0;
// imprimimos los resultados
printf( " No X1
Xu Xr
ea
et\n");
printf( " %d %6.3f
%6.3f %6.3f
%6.3f %6.3f \n", iter, x1, xu, xr, ea ) ;
}while( (ea >= es) || (iter<imax));
return xr;
}
41
2.2.5.- Demostración del programa
42
2.3.- Falsa Posición
Aun cuando la bisección es una técnica perfectamente válida para determinar
raíces, su método de aproximación por "fuerza bruta" es relativamente ineficiente.
La falsa posición es una alternativa basada en una visualización gráfica.
Un inconveniente del método de bisección es que al dividir el intervalo de , a
en mitades iguales, no se toman en consideración las magnitudes de
y
de
. Por ejemplo, si de
está mucho más cercana a cero que de
, es
lógico que la raíz se encuentre más cerca de que de
(figura 2.4). Un método
alternativo que aprovecha esta visualización gráfica consiste en unir
y
con una línea recta. La intersección de esta línea con el eje de las x representa una
mejor aproximación de la raíz. El hecho de que se reemplace la curva por una línea
recta da una "falsa posición" de la raíz; de aquí el nombre de método de la falsa
posición, o en latín, regula falsi. También se le conoce como método de
interpolación lineal.
Usando triángulos semejantes (fig.2.4) la intersección de la línea recta con el eje de
las se estima mediante
(Ec. 2.2)
en el cual se despeja
(Ec. 2.3)
Ésta es la fórmula de la falsa posición. El valor de
calculado con la ecuación
(Ec. 2.3), reemplazará, después, a cualquiera de los dos valores iniciales, o , y
da un valor de la función con el mismo signo de
. De esta manera, los valores
o
siempre encierran la verdadera raíz. El proceso se repite hasta que la
aproximación a la raíz sea adecuada. El algoritmo es idéntico al de la bisección
(figura 2.1), excepto en que la ecuación (Ec. 2.3).
43
Figura 2.4
Representación gráfica del método de la falsa posición con los triángulos
semejantes sombreados se obtiene la fórmula para el método.
Desarrollo del método de la falsa posición
Multiplicando en cruz la ecuación (2.2) obtenemos
Agrupando términos y reordenando:
Dividiendo entre
(Ec. 2.3a)
Ésta es una de las formas del método de la falsa posición. Observe que permite el
cálculo de la raíz
como una función de los valores iniciales inferior
y
superior . Ésta puede ponerse en una forma alternativa al separar los términos:
Sumando y restando
en el lado derecho:
44
Agrupando términos se obtiene
O
La cual es la misma ecuación (Ec. 2.3). Se utiliza esta forma porque implica una
evaluación de la función y una multiplicación menos que la ecuación (Ec. 2.3a).
2.3.1.- Ejemplo propuesto
Con el método de la falsa posición determine la raíz de la misma ecuación
analizada en el ejemplo 2.3 [ecuación (Ec. 2.3a)].
Solución. Se empieza el cálculo con los valores iniciales
=12 y
= 16.
Primera iteración:
= 12
=16
= 6.0699
= -2.2688
que tiene un error relativo verdadero de 0.89 por ciento.
Segunda iteración:
o
= -1.5426
Por lo tanto, la raíz se encuentra en el primer subintervalo y xr se vuelve ahora el
límite superior para la siguiente iteración, = 14.9113:
= 12
= 14.9113
f
45
el cual tiene errores relativos y verdadero y aproximado de 0.09 y 0.79 por ciento.
Es posible realizar iteraciones adicionales para hacer una mejor aproximación de
las raíces.
Se obtiene una idea más completa de la eficiencia de los métodos de bisección y
de falsa posición al observar la figura 3.5 donde se muestra el error relativo
porcentual verdadero de los ejemplos 3.3 y 3.3.1. Observe cómo el error decrece
mucho más rápidamente en el método de la falsa posición que en el de la
bisección, debido a un esquema más eficiente en el método de la falsa posición
para la localización de raíces.
Recuerde que en el método de bisección el intervalo entre
y
se va
haciendo más pequeño durante los cálculos. Por lo tanto, el intervalo, como se
define para la primera iteración, proporciona una medida del error en este método.
Éste no es el caso con el método de la falsa posición, ya que uno de los valores
iniciales puede permanecer fijo durante los cálculos, mientras que el otro converge
hacia la raíz. Como en el caso del ejemplo, el extremo inferior permanece en 12,
mientras que
converge a la raíz. En tales casos, el intervalo no se acorta, sino
que se aproxima a un valor constante.
Figura 2.5
Comparación de los errores relativos de los métodos de bisección y de falsa
posición
46
2.3.2.- Diagrama de Flujo
Diagrama de Flujo Falsa Posición
47
2.3.3.- Pseudocódigo
FUNCTION ModFalsepos (xl, xu, es, imax, xr, iter, ea)
iter = 0
fl = f(xl)
fu = f (xu)
DO
xrold = xr
xr = xu – fu * (xl – xu) / (fl – fu)
fr = f(xr)
iter= iter+1
IF xr
0 THEN
ea = Abs((xr – xrold) / xr) * 100
END IF
test = fl * fr
IF test
0 THEN
xu = xr
fu = f(xu)
iu= 0
il = il + 1
IF il
2 THEN fl = fl / 2
ELSE IF test
O THEN
xl=xr
fl = f (xl)
il=0
iu = iu + 1
IF iu
2 THEN fu =fu / 2
ELSE
ea = 0
END IF
IF ea
es OR iter
imax THEN EXIT
END DO
modFalsepos = xr
END Modfalsepos
48
2.3.4.- Programas en C
#include <math.h>
#include <stdio.h> /*para printf(),scanf()*/
#include <conio.h> /*para getch(),clrscr()*/
//#include <stdlib.h>/*para exit()*/
//#include <dos.h>
#define NUMEL
20
#define INTERVALOS 0
float A[25][25], B[25], S[25],X[25];
MAIN
printf("\n METODO DE FALSA POSICION\n");
printf( " Teclea valor minimo = ");
scanf("%f",&xmin);
printf("\n");
printf( " Teclea valor maximo = ");
scanf("%f",&xmax);
printf("\n");
printf( " Teclea valor de error esperado = ");
scanf("%f",&error);
printf("\n");
printf( " Teclea iteraciones maximas = ");
scanf("%f",&iteraciones);
printf("\n");
c1 = Bisect(xmin, xmax, error, iteraciones);
c1 = FalsePos( xmin, xmax, error, iteraciones);
printf( " Resultado = %f", c1);
getch();
}
}
49
float FalsePos( float x1,float xu, float es, int imax){
int iter=0, i1, iu;
float f1, fr, fu, test;
float xrold, xr, ea=0;
f1 = F(x1, 10, 40, 68.1);
fu = F(xu, 10, 40, 68.1);
do{
xrold = xr;
xr = xu-fu*(x1-xu)/(f1-fu);
fr = F(xr,10,40,68.1);
iter = iter +1;
if ( xr != 0 ) {
ea = abs( (xr - xrold)/xr )* 100;
}
test = f1 * fr;
if( test < 0 ) {
xu = xr;
fu = F(xu, 10, 40, 68.1);
iu = 0;
i1 = i1 + 1;
if ( i1 >= 2 )f1 = f1 / 2;
}
else if ( test > 0 ){
x1 = xr;
f1 = F(x1, 10, 40, 68.1);
i1 = 0;
iu = iu +1;
if( iu >= 2) fu = fu / 2;
}
else ea=0;
}
}while( (ea >= es) || (iter<imax));
return xr;
50
2.3.5.- Demostración del programa
51
2.4.- Newton-Raphson
Tal vez, de las fórmulas para localizar raíces, la fórmula de Newton-Raphson
(figura 2.6) sea la más ampliamente utilizada. Si el valor inicial para la raíz es
,
entonces se puede trazar una tangente desde el punto [ ,
] de la curva. Por lo
común, el punto donde esta tangente cruza al eje x representa una aproximación
mejorada de la raíz.
El método de Newton-Raphson se deduce a partir de esta interpretación
geométrica. De la figura 2.4, se tiene que la primera derivada en es equivalente a
la pendiente:
Figura 2.6
Representación gráfica del método de Newton Raphson. Se extrapola una tangente
a la función en [esto es,
] hasta el eje x para obtener una estimación de la
raíz en
.
f´
f
(Ec.2.4)
que se reordena para obtener
f
f´
(Ec.2.5)
La cual se conoce como fórmula de Newton-Raphson.
52
2.4.1.- Ejemplo propuesto
Planteamiento del problema. Utilice el método de Newton-Raphson para calcular
la raíz de
empleando como valor inicial
y se considere
como raíz real la iteración en donde exista una similitud de al menos 6 cifras
significativas con la iteración anterior.
Solución.
La primera derivada de la función es
que se sustituye, junto con la función original en la ecuación (2.5), para tener
53
Empezando con un valor inicial
0
0
1
0.500000000 11.8
2
3
0.566311003 0.147
0.567143165 0.0000220
4
0.567143290 <
, se obtuvieron los siguientes resultados:
100 '
Así, el método converge rápidamente a la raíz verdadera. Observe que el error
relativo porcentual verdadero en cada iteración disminuye mucho más rápido que
con la iteración simple de punto fijo.
54
2.4.2.- Diagrama de Flujo
INICIO
NGRAD,N,M,I,A,A
PROX,NRAIZ,X,N
MI,B,C,Y,REL
No convergen en
NMI iteraciones
Diagrama de Flujo Newton Raphson
55
2.4.3.- Pseudocódigo
function newtonIterationFunction(x) {
return x - (cos(x) - x^3) / (-sin(x) - 3*x^2)
}
var x := 0,5
for i from 0 to 99 {
print "Iteraciones: " i
print "Valor aproximado: " x
xold := x
x := newtonIterationFunction(x)
if x = xold {
print "Solución encontrada!"
break
}
}
2.4.4.- Programas en C
NEWTON RAPHSON
#include <conio.h>
#include <stdio.h>
#include <math.h>
MAIN
printf("\t\tMETODO DE NEWTON RAPHSON");
printf("\n\nintruce valor inicial");
printf("\n\nX=");
scanf("%f",&x1);
printf("\nIteraciones=");
scanf("%d",&I);
do
{
y=exp(-x1)-x1;
y2=-1*exp(-x1)-1;
xr=x1 - (y/y2);
a=((xr-x1)/xr)*100;
e=fabs(a);
x1=xr;
i++;
c++;
}
while(i<I);
printf("\n\n\n\nLa raiz aproximada es x%d = %.12f ",c,xr);
printf("\n\nError = %.3f",e);
getch();
}
56
2.4.3.- Demostración del programa
57
3.- ECUACIONES DIFERENCIALES ORDINARIAS
3.1.- Concepto y clasificación
En este capítulo se estudiarán, las técnicas numéricas de solución de ecuaciones
diferenciales con condiciones iniciales o de frontera, denominados problemas de valor
inicial o de frontera, respectivamente. Una ecuación diferencial ordinaria
(comúnmente abreviada "EDO") es una relación que contiene funciones de una sola
variable independiente, y una o más de sus derivadas con respecto a esa variable.
Para esto, se inicia formulando tales problemas, y luego, a partir de las ideas de
extrapolación, se plantean métodos como el de Euler. Más adelante, en un proceso
de ponderación de pendientes, se obtienen métodos con diferentes órdenes de
exactitud donde no se requiere de derivaciones complicadas de funciones, pagándose
como precio de ello un mayor número de cálculos. Tales métodos son conocidos
como métodos de Runge-Kutta. Basándose en el proceso de integración implicado en
la solución de las ecuaciones diferenciales y en la aproximación de funciones se
plantean familias de métodos denominados de predicción-corrección.
Dado que las ecuaciones diferenciales ordinarias permiten modelar procesos dinámicos tales como: vaciado de recipientes, reactores químicos, movimientos
amortiguados, desarrollos poblacionales e incluso situaciones estáticas como la
deflexión de vigas y problemas geométricos. Estas técnicas analíticas son necesarias
para ciertas ecuaciones muy particulares.
Muchas de las leyes generales de la naturaleza se expresan en el lenguaje de las
ecuaciones diferenciales; abundan también las aplicaciones en ingeniería, economía,
en las mismas matemáticas y en muchos otros campos de la ciencia aplicada.
Esta gran utilidad de las ecuaciones diferenciales es fácil de explicar; recuérdese que
si se tiene la función
su derivada
puede interpretarse como la
velocidad de cambio de y con respecto a . En cualquier proceso natural, las variables
incluidas y sus velocidades de cambio se relacionan entre sí mediante los principios
científicos que gobiernan el proceso. El resultado de expresar en símbolos
matemáticos estas relaciones, a menudo es una ecuación diferencial.
58
3.2.- Euler
Este tema se refiere a la solución de ecuaciones diferenciales ordinarias de la forma
Recuerde que el método fue de la forma general
Nuevo valor = valor anterior + pendiente x tamaño de paso
o, en términos matemáticos,
(Ec.3.2)
De acuerdo con esta ecuación, la pendiente estimada se usa para extrapolar desde
un valor anterior a un nuevo valor
en una distancia (figura 3.1). Esta fórmula
se aplica paso a paso para calcular un valor posterior y, por lo tanto, para trazar la
trayectoria de la solución.
En otras palabras, se toma la pendiente al inicio del intervalo como una aproximación
de la pendiente promedio sobre todo el intervalo.
Figura 3.1
Ilustración gráfica del método de un paso.
59
La primera derivada ofrece una estimación directa de la pendiente en
donde
es la ecuación diferencial evaluada en
sustituye la ecuación (Ec.3.1):
f
y
(figura 3.2):
. La estimación se
(Ec.3.2)
Esta fórmula se conoce como método de Euler (o de Euler-Cauchy o de punto
pendiente). Se predice un nuevo valor de y, usando la pendiente (igual a la primera
deriva en el valor original de x) para extrapolar linealmente sobre el tamaño de paso h
(figura 3.2).
Figura 3.2
Método de Euler.
3.2.1.- Ejemplo propuesto
Planteamiento del problema. Con el método de Euler integre numéricamente la
ecuación
desde
es
hasta
con un tamaño de paso 0.5. La condición inicial en
. Recuerde que la solución exacta está dada por la siguiente ecuación:
60
Solución.
Se utiliza la ecuación (Ec.3.2) para implementar el método de
Euler:
donde
y la pendiente estimada en
es
Por lo tanto,
La solución verdadera en
es
Así, el error es
Et = valor verdadero - valor aproximado
o , expresada como error relativo porcentual,
t
= -63.1%. En el segundo paso,
La solución verdadera en x = 1.0 es 3.0 y, entonces, el error relativo porcentual es 95.8%. El cálculo se repite y los resultados se dan en la tabla 3.2.1 y en la figura 3.3.
Comparación de los valores verdadero y aproximado de la integral de y'= -2X3+ 1
2.x2 - 20x + 8.5, con la condición inicial de que y = 1 en x = 0. Los valores
aproximados se calcularon empleando el método de Euler con un tamaño de paso de
0.5. El error local se refiere al error en que se incurre sobre un solo paso. Éste se
calcula con una expansión de la serie de Taylor como en el ejemplo 3.3. El error
global es la discrepancia total debida a los pasos anteriores y presentes.
61
Tabla 3.2.1
Error relativo
porcentual
X
Y verdadero
Y euler
Global
Local
0.0
1 .00000
1 .00000
0.5
3.21875
5.25000
-63.1
-63.1
1.0
3.00000
5.87500
-95.8
-28.0
1.5
2.21875
5.12500
131.0
-1.41
2.0
2.5
2.00000
2.71875
4.50000
4.75000
-125.0
-74.7
20.5
17.3
3.0
4.00000
5.87500
46.9
4.0
3.5
4.71875
7.12500
-51.0
-11.3
4.0
3.00000
7.00000
-133.3
-53.0
Figura 3.3
Comparación de la solución verdadera con una solución numérica usando el método
de Euler, para la integral de y' = -2X3 + 1 2X2 - 20x + 8.5 desde x = O hasta x = 4 con
un tamaño de paso de 0.5. La condición inicial en x = O es y = 1.
62
Observe que aunque el cálculo capta la tendencia general de la solución verdadera, el
error resulta considerable. Como se explica en la siguiente sección, es posible reducir
tal error usando un tamaño de paso menor.
El ejemplo anterior usa un polinomio simple como ecuación diferencial con el objetivo
de facilitar el siguiente análisis de error. De esta forma,
En efecto, un caso más general (y más común) implica EDO, donde aparece una
función que depende tanto de como de ,
Conforme avancemos en esta parte del texto, nuestros ejemplos comprenderán EDO
que dependen de variables independientes y dependientes.
63
3.2.2.- Diagrama de flujo
Diagrama de Flujo Euler
64
3.2.3.- Pseudocódigo
‘ nt rva o
nt grac ón
xi = 0
xf = 4
‘var ab s n c a s
x = xi
y=1
‘ stab c
tamaño
‘ núm ro
pasos
paso
t rm na
cá c o
dx = 0.5
nc= (xf – xi) /dx
‘con c on s
sa a
PRINT x,y
´ciclo para implementar el método de Euler
‘
sp
g
r s ta os
DO i = 1, nc
dydx = -2x3 +12x2 – 20x + 8.5
y = y + dydx . dx
x= x + dx
PRINT x, y
END DO
65
3.2.4.- Programas en C
EULER
#include <iostream.h>
#include <iomanip.h>
#include <conio.h>
#include <math.h>
#include <stdlib.h>
#include <dos.h>
MAIN
return (-2*pow(x,3))+(12*pow(x,2))-(20*x)+8.5;
case EULER: Lee(x0,xf,y0,h);
case SALIR: gotoxy(34,20);
default:
Euler(x0,xf,y0,h);
getch();
break;
cout<<"Fin de "<<__FILE__;
gotoxy(34,21);
cout<<"Espere un momento...";
sleep(1);
exit(0);
break;
gotoxy(34,15);
cout<<"Opcion no permitida";
getch();
break;
}
}while(op!=SALIR);
return 0;
}
cout<<" METODO DE EULER ";
gotoxy(10,4);
cout<<"Xo = "<<setw(10)<<setprecision(5)<<x0;
gotoxy(10,5);
cout<<"Xf = "<<setw(10)<<setprecision(5)<<xf;
gotoxy(10,6);
cout<<"Yo = "<<setw(10)<<setprecision(5)<<y0;
gotoxy(10,7);
cout<<"h = "<<setw(10)<<setprecision(5)<<h;
gotoxy(10,9);
cout<<" x
y";
gotoxy(9,10);
cout<<"----------------------";
cout<<endl;
cout<<setw(10) <<setprecision(5) << setiosflags(ios::fixed) << x;
cout<<setw(20) <<setprecision(5) << setiosflags(ios::fixed) << y << endl;
while(x<xf){
y+=h*f(x,y);
x+=h;
cout<<setw(10) <<setprecision(5) << setiosflags(ios::fixed) << x
<<setw(20) <<setprecision(5) << setiosflags(ios::fixed) << y <<
endl;
}
}
void Heun(float x0,float xf,float y0,float h)
{
66
3.2.5. - Demostración del programa
67
3.3.-Runge-Kutta
El método de Runge-Kutta (RK) logra la exactitud del procedimiento de la serie
Taylor sin necesitar el cálculo de derivadas de orden superior. Existen muchas
variantes, pero todas tienen la forma generalizada de la siguiente ecuación.
(Ec.3.3)
donde
se conoce como la función incremento, la cual puede interpretarse
como una pendiente representativa en el intervalo. La función incremento se escribe
en forma general como
(Ec.3.4)
donde las
son constantes y las
son
(Ec.3.4a)
(Ec.3.4b)
(Ec.3.4c)
.
.
.
(Ec.3.4d)
donde las
y
son constantes. Observe que las
son relaciones de recurrencia.
Es decir,
aparecen en la ecuación
, la cual aparece en la ecuación , etcétera.
Como cada es una evaluación funcional, esta recurrencia vuelve eficiente al método
de RK para cálculos en computadora.
Es posible tener varios tipos de métodos de Runge-Kutta empleando diferentes números de términos en la función incremento especificada por n. Observe que el
método de Runge-Kutta (RK) de primer orden con n=1 es, de hecho, el método de
Euler. Una vez que se elige n, se evalúan las a, p y q igualando la ecuación (Ec.3.3) a
los términos en la expansión de la serie de Taylor. Así, al menos para las versiones
de orden inferior, el número de términos, n, por lo común representa el orden de la
aproximación. Por ejemplo, en la siguiente sección, los métodos RK de segundo
orden usan la función incremento con dos términos (n – 2).
68
MÉTODO DE RUNGE KUTTA DE SEGUNDO ORDEN
La segunda versión de segundo orden de la ecuación (Ec.3.3) es
(Ec.3.5)
donde
(Ec.3.5a)
(Ec.3.5b)
Como se describe a continuación, los valores de
se evalúan al igualar
la ecuación (Ec.3.5) con la expansión de la serie de Taylor hasta el término de
segundo.
3.3.1.- Ejemplo propuesto
Comparación de varios esquemas RK de segundo orden
Planteamiento del problema. Utilice el método de Runge Kutta para integrar
numéricamente la siguiente ecuación:
Con la condición inicial de que y = 1 en x = 0. Los valores aproximados se calcularon
con el método RK de segundo orden, con un tamaño de paso de 0.5.
RUNGE KUTTA DE SEGUNDO ORDEN
0.0
0.5
1.0
1.5
2.0
2.5
3.0
3.5 .
4.0
1
.0000
3.21875
0
3.00000
2.21875
2.00000
2.71875
4.00000
4.71875
3.00000
1.00000
0
3.277344
3.101563
2.347656
2.140625
2.855469
4.117188
4.800781
3.031250
1.8
3.4..
5.8
7.0
5.0
2.9
1.7
1.0
69
En el primer intervalo
también es igual a 8.5 y
La pendiente promedio se calcula mediante
que se utiliza para predecir
Resolver la siguiente función
con la condición inicial
en
. Los valores aproximados se calcularon utilizando el método de runge kutta con un
tamaño de paso igual a 1.
0
2.0000000
2.0000000
0.00
2.0000000
0.00
1
6.1946314
6.7010819
8.18
6.3608655
2.68
2
14.8439219
16.3197819
9.94
15.3022367
3.09
3
33.6771718
37.1992489
10.46
34.7432761
3.17
4
75.3389626
83.3377674
10.62
77.7350962
3.18
70
3.3.2.-Diagrama de flujo
Diagrama de Flujo Runge Kutta
71
3.3.3.- Pseudocódigo
a) Programa pr nc pa o “man ja or”
Asigna valores para
n= número de ecuaciones
yi= valores iniciales de n variables dependientes
xi= valor inicial de la variable independiente
xf= valor final de la variable independiente
dx= cálculo del tamaño de paso
xout= intervalo de salida
x = xi
m= 0
xpm = x
DO I = 1, n
ypi,m = yi
END DO
DO
xend = x + xout
IF (xend
xf) THEN xend = xf
h= dx
CALL Integrador (x, y, n, h, xend)
m=m+1
xpm = x
DO i = 1, n
ypi,m = yi
END DO
IF (x
xf) EXIT
LOOP
DISPLAY RESULTS
END
72
b) Rutina para tomar un paso de salida
SUB integrator (x, y, n, h, xend)
DO
IF (xend – x < h) THEN h = xend – x
CALL RK4 (x, y, n, h)
IF (x
xend) EXIT
END DO
END SUB
c) Método RK de cuarto orden para un sistema de EDO
SUB RK4(x, y, n, h)
CALL Derivs (x, y, k1)
DO i = 1, n
ymi = yi + k1i * h / 2
END DO
CALL Derivs (x + h / 2, ym, k2)
DO i = 1, n
ymi = yi + k2i * h / 2
END DO
CALL Derivs (x + h / 2, ym, k3)
DO i = 1, n
yei = yi + k3i * h
END DO
CALL Derivs (x + h, ye, k4)
DO i = 1, n
slopei = (k1i + 2* (k2i+k3i)+k4i)/6
yi = yi + slopei * h
END DO
x=x+h
END SUB
d) Rutina para determinar derivadas
SUB Derivs (x, y, dy)
dy1 = …
dy2 = ….
END SUB
73
3.3.4.- Programas en C
RUNGE KUTTA
#include <iostream.h>
#include <iomanip.h>
#include <conio.h>
#include <math.h>
#include <stdlib.h>
#include <dos.h>
MAIN
return 4*exp(0.8*x)-(0.5*y);
case RK2: Lee(x0,xf,y0,h);
case SALIR: gotoxy(34,20);
default:
Heun(x0,xf,y0,h);
getch();
break;
cout<<"Fin de "<<__FILE__;
gotoxy(34,21);
cout<<"Espere un momento...";
sleep(1);
exit(0);
break;
gotoxy(34,15);
cout<<"Opcion no permitida";
getch();
break;
}
}while(op!=SALIR);
return 0;
}
cout<<"METODO DE HEUN (RK Orden 2)"<<endl;
gotoxy(10,4);
cout<<"Xo = "<< setw(10) << setprecision(5) <<x0;
gotoxy(10,5);
cout<<"Xf = "<< setw(10) << setprecision(5) <<xf;
gotoxy(10,6);
cout<<"Yo = "<< setw(10) << setprecision(5) <<y0;
gotoxy(10,7);
cout<<"h = "<< setw(10) << setprecision(5) <<h;
gotoxy(10,9);
cout<<"x
y";
gotoxy(9,10);
cout<<"----------------------";
cout<<endl;
cout<<setw(10) << setprecision(5) << setiosflags(ios::fixed) << x;
cout<<setw(20) << setprecision(5) << setiosflags(ios::fixed) << y <<endl;
while(x<xf){
k1=h*f(x,y);
k2=h*f(x+h,y+k1);
y+=(k1/2.0+k2/2.0);
x+=h;
cout<<setw(10) << setprecision(5) << setiosflags(ios::fixed) << x
<<setw(20) << setprecision(5) << setiosflags(ios::fixed) << y
<<endl;
}
}
{
void rk4(float x0,float xf,float y0,float h)
74
3.3.5.- Demostración del programa
75
4.- INTEGRACIÓN
4.1.- Concepto y clasificación
En este capítulo se abordará el tema clásico de integración definida. Para ello se
utilizarán procesos finitos, en los que a diferencia de los métodos analíticos, donde
el concepto de límite es central y por tanto los procesos infinitos se manejan
conjuntos de puntos discretos y haremos pasar por ellos o entre ellos un polinomio,
para después integrar o derivar dicho polinomio. Con esta técnica podremos
integrar y derivar funciones dadas tabularmente o bien funciones analíticas muy
complejas e, incluso, integrar aq é as c a nt gra “no
st ”, como s n caso
de
s n
cos
s n
tc
Además, dicha técnica se puede aproximar a la obtención de integrales dobles y
triples.
Siendo la derivada la medida de cambio puntual o instantáneo y la integral la suma
o acumulación de tales cambios, resulta fundamental en cualquier actividad de ingeniería o ciencias, conocer ambas técnicas, y no menos importante, darle sentido
físico a los resultados.
4.2.- Simpson 1/3
Una forma de obtener una estimación más exacta de una integral consiste en usar
polinomios de grado superior para unir los puntos. Por ejemplo, si hay otro punto a
la mitad entre
y
los tres puntos se pueden unir con una parábola (figura
4.1a). Si hay dos puntos igualmente espaciados entre f(a) y f b , los cuatro puntos
se pueden unir mediante un polinomio de tercer grado (figura 4.1b). Las fórmulas
que resultan de tomar las integrales bajo esos polinomios se conocen como reglas
de Simpson.
La regla de Simpson 1/3 resulta cuando un polinomio de interpolación de segundo
grado se sustituye en la siguiente ecuación:
Figura 4.1a
a) Descripción gráfica de la regla de Simpson 1/3, que consiste en tomar el
área bajo una parábola que une tres puntos.
76
Figura 4.1b
b) Descripción gráfica de la regla de Simpson 3/8 que consiste en tomar el área
bajo una ecuación cubica que une cuatro puntos.
4.2.1.- Ejemplo propuesto
Si se designan a y b como x0 y x2 respectivamente y f2(x) se representa por un
polinomio de Lagrange de segundo grado, la integral se transforma en
f
f
f
Después de la integración y de las manipulaciones algebraicas, se obtiene la
siguiente fórmula:
f
f
f
(Ec.4.1)
77
donde, en este caso,
. Esta ecuación se conoce como regla de
Simpson 1/3, y es la segunda fórmula de integración cerrada de Newton-Cotes. La
especificación "1/3" se origina del hecho de que está dividida entre 3 en la
ecuación (Ec.4.1). Una alternativa para obtenerla se integra el polinomio de NewtonGregory para llegar a la misma fórmula.
La regla de Simpson 1/3 también se puede expresar usando el formato de la
siguiente ecuación:
a
b
Ancho
f
f
f
(Ec.4.2)
Altura promedio
donde a
b
y
el punto a la mitad entre a y b , que está dado por
(b+a)/2. Observe que, de acuerdo con la ecuación (Ec.4.2), el punto medio está
ponderado por dos tercios; y los dos puntos extremos, por un sexto.
Se puede demostrar que la aplicación a un solo segmento de la regla de Simpson
1/3 tiene un error de truncamiento.
f
t
o como
t
b a
f
(Ec.4.3)
donde
está en algún lugar en el intervalo de y Así, la regla de Simpson 1/3
es más exacta que la regla del trapecio. No obstante, una comparación con la
ecuación (21.6) indica que es más exacta de lo esperado. En lugar de ser
proporcional a la tercera derivada, el error es proporcional a la cuarta derivada. Esto
es porque, como se muestra en el cuadro 21.3, el término del coeficiente de tercer
grado se hace cero durante la integración de la interpolación polinomial. En
consecuencia, la regla de Simpson 1/3 alcanza una precisión de tercer orden aun
cuando se base en sólo tres puntos.
78
Aplicación simple de la regla de Simpson 1/3
Planteamiento del problema. Con la ecuación (Ec.4.2) integre
desde
hasta
. Recuerde que la integral exacta es 1.640533
Solución.
Por lo tanto, la ecuación (Ec.4.2.2) se utiliza para calcular
Que representa un error exacto de
que es aproximadamente 5 veces más precisa que una sola aplicación de la regla
del trapecio
El error estimado es [(Ec.4.2.2)]
.
donde -2400 es el promedio de la cuarta derivada en el intervalo, , debido a que el
promedio de la cuarta derivada no es una estimación exacta de
. Sin
embargo, como este caso tiene que ver con un polinomio de quinto grado, el
resultado concuerda.
79
4.2.2.- Diagrama de flujo
INICIO
n_s, n_p, h, b,
a, x_j, j, k, i
Diagrama de Flujo Simpson 1/3
80
4.2.3. - Pseudocódigo
a)
FUNCTION Simp13 (h, f0, fl, f2)
simp13 = 2* h * (f0+4*f1+f2) /6
END Simp13
b)
FUNCTION Simp38 (h, f0, fl, f2)
simp38 = 3*h* (f0+3*(f1+f2)+f3) /8
END Simp38
c)
FUNCION Simp13m (h, n, f)
sum = f(0)
DO i = 1, n- 2, 2
sum = sum + 4 * fi + 2 * fi+1
END DO
sum = sum + 4 * fn-1 + 2 * fn
simp13m = h * sum / 3
END Simp13m
d)
FUNCTION SimpInt (a, b, n, f)
h = (b – a) / n
IF n = 1 THEN
sum = trap (h, fn-1,fn)
ELSE
m=n
odd = n / 2 – INT(n / 2)
IF odd > 0 AND > 1 THEN
sum = sum + simp38(h, fn-3, fn-2, fn-1, fn)
m=n–3
END IF
IF m > 1 THEN
sum = sum + simp13m(h,m,f)
END IF
END IF
SimpInt =sum
END SimpInt
81
4.2.4.- Programas en C
#include <stdio.h> /*para printf(),scanf()*/
#include <conio.h> /*para getch(),clrscr()*/
#include <stdlib.h>/*para exit()*/
#include <dos.h>
#include <math.h>
#define NUMEL
20
#define INTERVALOS 0
float f(float);
void _error(int n);
MAIN
}
h=(b-a)/(2*n);
S0=S1=0;
for(i=1;i<=(2*n-1);++i){
x=a+((double)i)*h;
if(!(i%2))
S0+=f(x);
else
S1+=f(x);
}
*Area=(h*(f(a)+4*S1+2*S0+f(b))/3.0);
printf("\n El area es -> %5.6f",*Area);
getch();
}
void LeeDatos(int opc)
{
if(opc==SIMPSON1_3){
clrscr();
printf("\n Metodo de Integracion por 1/3 de Simpson");
printf("\n ========================================");
putchar('\n');
printf("\n Numero de intervalos (PAR) -> ");
}
scanf("%d",&n);
if(n<1){
_error(INTERVALOS);
exit(1);
}
else
printf("\n Valor de a =>");
scanf("%f",&a);
printf("\n Valor de b =>");
scanf("%f",&b);
}
82
4.2.5.- Demostración del programa
83
COSTOS
Para la elaboración de este manual de programas aplicados a métodos numéricos
se recopiló información de diferentes fuentes: libros e internet pero como
herramienta principal, el programa borland c++ versión 5.0 y el costo de operación
de este software fue gratuito ya que la facultad cuenta con esta herramienta y está
instalado en el centro de cómputo en nuestra facultad de ingeniería mecánica
eléctrica.
84
CAPITULO III
APORTACIONES Y CONTRIBUCIONES AL DESARROLLO
Una de las razones por la que se elaboró este manual, es para que el estudiante
tenga como apoyo en la experiencia de métodos numéricos, programas para
aplicar los métodos, brindándoles así una herramienta que podrán utilizar y les
permitirá ahorrar tiempo y esfuerzo.
Con este trabajo práctico educativo se pretende que al estudiante le sea de mucha
utilidad y disponga de una herramienta más, principalmente a los estudiantes de
ingeniería que cursen la experiencia educativa de métodos numéricos con el fin de
que logre una mayor comprensión de los temas mencionados, utilizando como
estrategia de aprendizaje el uso de programas que ayuden a agilizar el
procedimiento de los métodos expuestos en este tema.
86
BIBLIOGRAFÍA
MÉTODOS NUMÉRICOS
APLICACIÓN
PARA
INGENIEROS
CON
PROGRAMAS
DE
STEVEN C. CHAPRA
RAYMOND P. CANALE
MÉTODOS NUMÉRICOS
LUTHE OLVERA SCHUTZ
MÉTODOS NUMÉRICOS APLICADOS A LA INGENIERÍA
ANTONIO NIEVES
87
ANEXOS
Anexo A
DIAGRAMAS DE FLUJO
TERMINAL:
Indica el inicio y el fin de un Diagrama de Flujo.
DEFINICIÓN DE VARIABLES:
Define todas y cada una de las variables que serán
utilizadas en el programa representado en el diagrama
de flujo.
PROCESO:
Cualquier tipo de operación que pueda originar cambio
de valor, formato o posición de la información
almacenada en memoria, operaciones aritméticas, de
transferencia, etc.
DECISIÓN:
Indica operaciones lógicas o de comparación de datos,
normalmente dos, y en función del resultado de la misma
determina cual de los distintos caminos alternativos del
programa se debe seguir; normalmente tiene dos salidas
respuestas: sí o no, pero puede tener más según los
casos.
ENTRADA:
Indica la asignación de un valor de una variable tomando
este valor desde un dispositivo de entrada.
SALIDA:
Indica que el resultado será presentado ya sea por
pantalla o por impresora.
88
Anexo B
CICLOS
Ciclos: Son procesos repetitivos los cuales se ejecutan una o más instrucciones
varias veces.
Valor Inicial: Se encuentra asignado a una variable la cual es la que controla el
ciclo y debe ser de tipo entero, además tiene el valor de inicio del ciclo.
Valor Final: Con este valor indicamos la última vez que el ciclo debe ejecutarse.
Incremento: Representa la frecuencia en que debe aumentarse la variable.
Entrada: Marca el principio o reinicio de las instrucciones que se encuentran
dentro del ciclo.
Regreso: Indica donde termina el ciclo, en qué momento el proceso se repite.
Salida: Indica la finalización de la ejecución de un ciclo.
89
Anexo C
PRINCIPIOS DE C
En este anexo se ofrece una breve historia del desarrollo del lenguaje C y se
consideran también sus características.
ORÍGENES DEL C
El proceso de desarrollo del lenguaje C se origina con la creación de un lenguaje
llamado BCPL, que fue desarrollado por Martin Richards. El BCPL tuvo influencia
en un lenguaje llamado B, el cual se usó en 1970 y fue inventado por Ken
Thompson y que permitió el desarrollo de C en 1971, el cual lo inventó e
implementó Dennis Ritchie. Para 1973 el sistema operativo UNIX estaba casi
totalmente escrito en C.
Durante muchos años el estándar para C fue la versión 5 del sistema operativo
UNIX, documentada en ``The C Programming Language'' escrito por Brian W.
Kernighan and Dennis M. Ritchie in 1978 comúnmente referido como K&R.
Posteriormente se hicieron varias implementaciones las cuales mostraban las
siguientes tendencias:
Nuevas características
Diferencias de máquinas
Diferencias de productos
Errores en los compiladores
Malas implementaciones
Esto originó que en el verano de 1983 se estableciera un comité para resolver
estas discrepancias, el cual empezó a trabajar en un estándar ANSI C, la cual fue
completada en 1988.
CARACTERÍSTICAS DE C
Algunas de las características más importantes que definen el lenguaje y que han
permitido que sea tan popular, como lenguaje de programación son:
Tamaño pequeño.
Uso extensivo de llamadas a funciones.
Comandos breves (poco tecleo).
Lenguaje estructurado.
Programación de bajo nivel (nivel bit)
Implementación de apuntadores - uso extensivo de apuntadores para la memoria,
arreglos, estructuras y funciones
Las diversas razones por la cual se ha convertido en un lenguaje de uso
profesional son:
El uso de constructores de alto nivel.
El poder manejar actividades de bajo-nivel.
El generar programas eficientes.
La posibilidad de poder ser compilado en una variedad de computadoras, con
90
pocos cambios (portabilidad).
Un punto en contra es que tiene una detección pobre de errores, lo cual en
ocasiones es problemático para los principiantes.
Estructura de un programa en C
Un programa de C tiene básicamente la siguiente forma:
Comandos del preprocesador.
Definiciones de tipos.
Prototipos de funciones - declara el tipo de función y las variables pasadas a la
misma.
Variables
Funciones
Para un programa se debe tener una función main().
Una función tiene la forma:
tipo nombre_de_la_funcion (parámetros)
{
variables locales
sentencias de C
}
Si la definición del tipo es omitida, C asume que la función regresa un tipo entero.
Nota: Lo anterior puede ser una fuente de problemas en un programa.
A continuación se muestra un primer programa:
/* Programa ejemplo */
main()
{
printf( "Me gusta C\n" );
exit (0);
}
NOTAS:
C requiere un punto y coma al final de cada sentencia.
printf es una función estándar de C, la cual es llamada en la función main().
\n significa salto de línea. Salida formateada.
exit() es también una función estándar que hace que el programa termine. En el
sentido estricto no es necesario ya que es la última línea de main() y de cualquier
forma terminará el programa.
En caso de que se hubiera llamado a la función printf de la siguiente forma:
printf(".\n.1\n..2\n...3\n");
La salida tendría la siguiente forma:
.1
..2
...3
91
VARIABLES
C tiene los siguientes tipos de datos simples:
Tabla: Tipos de C
Tipo
Tamaño (bytes) Límite inferior
Límite superior
Char
1
--
unsigned char
1
short int
2
--
unsigned short int 2
(long) int
4
Float
4
double
8
Los tipos de datos básicos tiene varios modificadores que les preceden. Se usa un
modificador para alterar el significado de un tipo base para que encaje con las
diversas necesidades o situaciones. Los modificadores son: signed, unsigned, long
y short.
En los sistemas UNIX todos los tipos int son long int, a menos que se especifique
explícitamente short int.
Nota: no hay un tipo booleano en C -- se deberá usar char, int o aún mejor
unsigned char.
signed, unsigned, long y short pueden ser usados con los tipos char e int. Aunque
es permitido el uso de signed en enteros, es redundante porque la declaración de
entero por defecto asume un número con signo.
Para declarar una variable en C, se debe seguir el siguiente formato:
tipo lista_variables;
tipo es un tipo válido de C y lista_variables puede consistir en uno o más
identificadores separados por una coma. Un identificador debe comenzar con una
letra o un guión bajo.
Ejemplo:
int i, j, k;
float x,y,z;
char ch;
92
DEFINICIÓN DE VARIABLES GLOBALES
Una variable global se declara fuera de todas las funciones, incluyendo a la función
main(). Una variable global puede ser utilizada en cualquier parte del programa.
Por ejemplo:
short numero, suma;
int numerogr, sumagr;
char letra;
main()
{
...
}
Es también posible pre inicializar variables globales usando el operador de
asignación =, por ejemplo:
float suma= 0.0;
int sumagr= 0;
char letra= 'A';
main()
{
...
}
Que es lo mismo que:
float suma;
int sumagr;
char letra;
main()
{
suma = 0.0;
sumagr= 0;
letra = 'A';
...
}
Dentro de C también se permite la asignación múltiple usando el operador =, por
ejemplo:
a = b = c = d = 3;
...que es lo mismo, pero más eficiente que:
a = 3;
b = 3;
c = 3;
93
d = 3;
La asignación múltiple se puede llevar a cabo, si todos los tipos de las variables
son iguales.
Se pueden redefinir los tipos de C usando typedef. Como un ejemplo de un simple
uso se considera como se crean dos nuevos tipos real y letra. Estos nuevos tipos
pueden ser usados de igual forma como los tipos predefinidos de C.
typedef float real;
typedef char letra;
/* Declaración de variables usando el nuevo tipo */
real suma=0.0;
letra sig_letra;
LECTURA Y ESCRITURA DE VARIABLES
El lenguaje C usa salida formateada. La función printf tiene un carácter especial
para formatear (%) -- un carácter enseguida define un cierto tipo de formato para
una variable.
%c caracteres
%s cadena de caracteres
%d enteros
%f flotantes
Por ejemplo:
printf("%c %d %f",ch,i,x);
La sentencia de formato se encierra entre " ", y enseguida las variables.
Asegurarse que el orden de formateo y los tipos de datos de las variables
coincidan.
scanf() es la función para entrar valores a variables. Su formato es similar a printf.
Por ejemplo:
scanf("%c %d %f %s",&ch, &i, &x, cad);
Observar que se antepone & a los nombres de las variables, excepto a la cadena
de caracteres.
CONSTANTES
ANSI C permite declarar constantes. Cuando se declara una constante es un poco
parecido a declarar una variable, excepto que el valor no puede ser cambiado.
La palabra clave const se usa para declarar una constante, como se muestra a
continuación:
const a = 1;
int a = 2;
Notas:
Se puede usar constantes o después del tipo.
94
Es usual inicializar una constante con un valor, ya que no puede ser cambiada de
alguna otra forma.
La directiva del preprocesador #define es un método más flexible para definir
constantes en un programa.
Frecuentemente se ve la declaración const en los parámetros de la función. Lo
anterior simplemente indica que la función no cambiara el valor del parámetro. Por
ejemplo, la siguiente función usa este concepto:
char *strcpy(char *dest, const char *orig);
El segundo argumento orig es una cadena de C que no será alterada, cuando se
use la función de la biblioteca para copiar cadenas.
OPERADORES ARITMÉTICOS
Lo mismo que en otros lenguajes de programación, en C se tienen los operadores
aritméticos más usuales (+ suma, - resta, * multiplicación, / división y % módulo).
El operador de asignación es =, por ejemplo: i=4; ch='y';
Incremento ++ y decremento -- unario. Los cuales son más eficientes que las
respectivas asignaciones. Por ejemplo: x++ es más rápido que x=x+1.
Los operadores ++ y -- pueden ser prefijos o postfijos. Cuando son prefijos, el valor
es calculado antes de que la expresión sea evaluada, y cuando es postfijo el valor
es calculado después que la expresión es evaluada.
En el siguiente ejemplo, ++z es prefijo y -- es postfijo:
int x,y,z;
main()
{
x=( ( ++z ) - ( y-- ) ) % 100;
}
Que es equivalente a:
int x,y,z;
main()
{
z++;
x = ( z-y ) % 100;
y--;
}
El operador % (módulo o residuo) solamente trabaja con enteros, aunque existe
una función para flotantes.
El operador división / es para división entera y flotantes. Por lo tanto hay que tener
cuidado. El resultado de x = 3 / 2; es uno, aún si x es declarado como float. La
regla es: si ambos argumentos en una división son enteros, entonces el resultado
es entero. Si se desea obtener la división con la fracción, entonces escribirlo como:
x = 3.0 / 2; o x = 3 / 2.0 y aún mejor x = 3.0 / 2.0.
Por otra parte, existe una forma más corta para expresar cálculos en C. Por
ejemplo, si se tienen expresiones como: i = i + 3; o x = x * (y + 2); , pueden ser
95
reescritas como:
Lo cual es equivalente, pero menos eficiente que:
Por lo que podemos reescribir las expresiones anteriores como: i += 3; y x *= y + 2;
respectivamente.
OPERADORES DE COMPARACIÓN
El operador para probar la igualdad es ==, por lo que se deberá tener cuidado de
no escribir accidentalmente sólo =, ya que:
if ( i = j ) ...
Es una sentencia legal de C (sintácticamente hablando aunque el compilador avisa
cuando se emplea), la cual copia el valor de ``j'' en ``i'', lo cual será interpretado
como VERDADERO, si j es diferente de cero.
Diferente es !=, otros operadores son: < menor que, > mayor que, <= menor que o
igual a y >= (mayor que o igual a).
OPERADORES LÓGICOS
Los operadores lógicos son usualmente usados con sentencias condicionales o
relacionales, los operadores básicos lógicos son:
&& Y lógico, || O lógico y! negación.
ORDEN DE PRECEDENCIA
Es necesario ser cuidadosos con el significado de expresiones tales como a + b *
c, dependiendo de lo que se desee hacer
(a + b) * c
o
a + (b * c)
Todos los operadores tienen una prioridad, los operadores de mayor prioridad son
evaluados antes que los que tienen menor prioridad. Los operadores que tienen la
misma prioridad son evaluados de izquierda a derecha, por lo que:
a-b-c
es evaluado como
(a - b) - c
Prioridad Operador(es)
Más alta ( ) [ ] ->
! ~ ++ -- - (tipo) * & sizeof
*/%
+96
<< >>
< <= > >=
== !=
&
^
|
&&
||
?
= += -= *= /=
Más baja ,
De acuerdo a lo anterior, la siguiente expresión:
a < 10 && 2 * b < c
Es interpretada como:
(a < 10) && ( (2 * b) < c )
y
a=
b=
10 / 5
+ 2;
como
a=
(b=
( 10 / 5 )
+ 2 );
ESTRUCTURAS CONDICIONALES
En este capítulo se revisan los distintos métodos con los que C controla el flujo
lógico de un programa.
Como se revisó en el capítulo anterior, los operadores relaciones binarios que se
usan son:
==, !=, <, <=, > y >=
además los operadores lógicos binarios:
||, &&
y el operador lógico unario de negación !, que sólo toma un argumento.
Los operadores anteriores son usados con las siguientes estructuras que se
muestran.
97
LA SENTENCIA IF
Las
tres
formas
como
se
puede
emplear
la
sentencia
if
son:
if (condición)
sentencia;
...o
if (condición)
sentencia1;
else
sentencia2;
...o
if (condicion1)
sentencia1;
else if (condicion2)
sentencia2;
...
else
sentencian;
El flujo lógico de esta estructura es de arriba hacia abajo. La primera sentencia se
ejecutará y se saldrá de la estructura if si la primera condición es verdadera. Si la
primera condición fue falsa, y existe otra condición, se evalúa, y si la condición es
verdadera, entonces se ejecuta la sentencia asociada. Si existen más condiciones
dentro de la estructura if, se van evaluando éstas, siempre y cuando las
condiciones que le precedan sean falsas.
La sentencia que está asociada a la palabra reservada else, se ejecuta si todas las
condiciones de la estructura if fueron falsas.
Por ejemplo:
main()
{
int x, y, w;
........
if (x>0)
{
z=w;
.......
}
else
{
z=y;
.......
}
}
98
EL OPERADOR “?”
El operador ternario condicional “?” es más eficiente que la sentencia if. El
operador ? tiene el siguiente formato:
expresión1 ? expresión 2 : expresion3;
Que es equivalente a la siguiente expresión:
if (expresión1) then expresión2 else expresión3;
Por ejemplo, para asignar el máximo de a y b a la variable z, usando ?,
tendríamos:
z = (a>b) ? a : b;
que es lo mismo que:
if (a > b)
z = a;
else
z = b;
El uso del operador “?” para reemplazar las sentencias if ... else no se restringe
sólo a asignaciones, como en el ejemplo anterior. Se pueden ejecutar una o más
llamadas de función usando el operador ? poniéndolas en las expresiones que
forman los operandos, como en el ejemplo siguiente:
f1(int n)
{
printf("%d ",n);
}
f2()
{
printf("introducido\n");
}
main()
{
int t;
printf(": ");
scanf("%d",&t);
/* imprime mensaje apropiado */
t ? f1(t) + f2() : printf("Se dio un cero\n");
}
99
LA SENTENCIA SWITCH
Aunque con la estructura if ... else if se pueden realizar comprobaciones múltiples,
en ocasiones no es muy elegante, ya que el código puede ser difícil de seguir y
puede confundir incluso al autor transcurrido un tiempo. Por lo anterior, C tiene
incorporada una sentencia de bifurcación múltiple llamada switch. Con esta
sentencia, la computadora comprueba una variable sucesivamente frente a una
lista de constantes enteras o de carácter. Después de encontrar una coincidencia,
la computadora ejecuta la sentencia o bloque de sentencias que se asocian con la
constante.
La
forma
general
de
la
sentencia
switch
es:
switch (variable) {
case constante1:
secuencia de sentencias
break;
case constante2:
secuencia de sentencias
break;
case constante3:
secuencia de sentencias
break;
...
default:
secuencia de sentencias
}
Donde la computadora ejecuta la sentencia default si no coincide ninguna
constante con la variable, esta última es opcional. Cuando se encuentra una
coincidencia, la computadora ejecuta las sentencias asociadas con el case hasta
encontrar la sentencia break con lo que sale de la estructura switch.
Las limitaciones que tiene la sentencia switch ... case respecto a la estructura if
son:
Sólo se tiene posibilidad de revisar una sola variable.
Con switch sólo se puede comprobar por igualdad, mientras que con if puede ser
con cualquier operador relacional.
No se puede probar más de una constante por case.
La forma como se puede simular el último punto, es no teniendo sentencias
asociados a un case, es decir, teniendo una sentencia nula donde sólo se pone el
caso, con lo que se permite que el flujo del programa caiga al omitir las sentencias,
como se muestra a continuación:
switch (letra)
{
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':
numvocales++;
break;
100
case ' ':
numesp++;
break;
default:
numotras++;
break;
}
ITERACIÓN
En este capítulo se revisan los mecanismos de C para repetir un conjunto de
instrucciones hasta que se cumple cierta condición.
LA SENTENCIA FOR
La sentencia for tiene el siguiente formato:
for ( expresión1; expresión2; expresión3)
sentencia;
o { bloque de sentencias }
En donde expresión1 se usa para realizar la inicialización de variables, usando una
o varias sentencias, si se usan varias sentencias deberá usarse el operador, para
separarlas. Por lo general, establece el valor de la variable de control del ciclo.
expresión2 se usa para la condición de terminación del ciclo y expresión3 es el
modificador a la variable de control del ciclo cada vez que la computadora lo repite,
pero también puede ser más que un incremento.
Por ejemplo:
int X;
main()
{
for( X=3; X>0; X--)
{
printf("X=%d\n",X);
}
}
genera la siguiente salida a pantalla ...
X=3
X=2
X=1
101
Todos las siguientes sentencias for son válidas en C. Las aplicaciones prácticas de
tales sentencias no son importantes aquí, ya que tan sólo se intenta ilustrar
algunas características que pueden ser de utilidad:
for ( x=0; ( (x>3) && (x<9) ); x++ )
for ( x=0, y=4; ( (x>3) && (x<9) ); x++, y+=2)
for ( x=0, y=4, z=4000; z; z/=10)
En el segundo ejemplo se muestra la forma como múltiples expresiones pueden
aparecer, siempre y cuando estén separadas por una coma ,
En el tercer ejemplo, el ciclo continuará iterando hasta que z se convierta en .
LA SENTENCIA WHILE
La sentencia while es otro ciclo o bucle disponible en C. Su formato es:
while ( expresión) sentencia;
donde sentencia puede ser una sentencia vacía, una sentencia única o un bloque
de sentencias que se repetirán. Cuando el flujo del programa llega a esta
instrucción, primero se revisa si la condición es verdad para ejecutar la(s)
sentencia(s), y después el ciclo while se repetirá mientras la condición sea
verdadera. Cuando llega a ser falsa, el control del programa pasa a la línea que
sigue al ciclo.
En el siguiente ejemplo se muestra una rutina de entrada desde el teclado, la cual
se cicla mientras no se pulse A:
main()
{
char carac;
carac = '\0';
while( carac != 'A') carac = getchar();
}
Antes de entrar al ciclo se inicializa la variable carac a nulo. Después pasa a la
sentencia while donde se comprueba si carac no es igual a 'A', como sea verdad
entonces se ejecuta la sentencia del bucle (carac = getchar();). La función getchar()
lee el siguiente carácter del flujo estándar (teclado) y lo devuelve, que en nuestro
ejemplo es el caracter que haya sido tecleado. Una vez que se ha pulsado una
tecla, se asigna a carac y se comprueba la condición nuevamente. Después de
pulsar A, la condición llega a ser falsa porque carac es igual a A, con lo que el ciclo
termina.
De lo anterior, se tiene que tanto el ciclo for, como el ciclo while comprueban la
condición en lo alto del ciclo, por lo que el código dentro del ciclo no se ejecuta
siempre.
102
A continuación mostramos otro ejemplo:
main()
{
int x=3;
while( x>0 )
{
printf("x = %d\n", x);
x--;
}
}
que genera la siguiente salida en pantalla:
x=3
x=2
x=1
Como se observa, dentro del ciclo tenemos más de una sentencia, por lo que se
requiere usar la llave abierta y la llave cerrada { ... } para que el grupo de
sentencias sean tratadas como una unidad.
Como el ciclo while pueda aceptar también expresiones, y no solamente
condiciones lo siguiente es válido:
while ( x-- );
while ( x = x + 1 );
while ( x += 5 );
Si se usan este tipo de expresiones, solamente cuando el resultado de x--, x=x+1 o
x+=5 sea cero, la condición fallará y se podrá salir del ciclo.
De acuerdo a lo anterior, podemos realizar una operación completa dentro de la
expresión. Por ejemplo:
main()
{
char carac;
carac = '\0';
while ( (carac = getchar()) != 'A' )
putchar(carac);
}
En este ejemplo se usan las funciones de la biblioteca estándar getchar() -- lee un
carácter del teclado y putchar() escribe un carácter dado en pantalla. El ciclo while
procederá a leer del teclado y lo mostrará hasta que el carácter A sea leído.
LA SENTENCIA DO-WHILE
Al contrario de los ciclos for y while que comprueban la condición en lo alto del
bucle, el bucle do ... while la examina en la parte baja del mismo. Esta
característica provoca que un ciclo do ... while siempre se ejecute al menos una
103
vez. La forma general del ciclo es:
do {
sentencia;
} while (condición);
Aunque no son necesarias las llaves cuando sólo está presente una sentencia, se
usan normalmente por legibilidad y para evitar confusión (respecto al lector, y no
del compilador) con la sentencia while.
En el siguiente programa se usa un ciclo do ... while para leer números desde el
teclado hasta que uno de ellos es menor que o igual a 100:
main()
{
int num;
do
{
scanf("%d", &num);
} while ( num>100 );
}
Otro uso común de la estructura do... while es una rutina de selección en un menú,
ya que siempre se requiere que se ejecute al menos una vez.
main()
{
int opc;
printf("1. Derivadas\n");
printf("2. Limites\n");
printf("3. Integrales\n");
do
{
printf(" Teclear una opcion: ");
scanf("%d", &opc);
switch(opc)
{
case 1:
printf("\tOpcion 1 seleccionada\n\n");
break;
case 2:
printf("\tOpcion 2 seleccionada\n\n");
break;
case 3:
printf("\tOpcion 3 seleccionada\n\n");
break;
default:
printf("\tOpcion no disponible\n\n");
break;
}
104
} while( opc != 1 && opc != 2 && opc != 3);
}
Se muestra un ejemplo donde se reescribe usando do... while uno de los ejemplos
ya mostrados.
main()
{
int x=3;
do
{
printf("x = %d\n", x--);
}
while( x>0 ) ;
}
USO DE BREAK Y CONTINUE
Como se comento uno de los usos de la sentencia break es terminar un case en la
sentencia switch. Otro uso es forzar la terminación inmediate de un ciclo, saltando
la prueba condicional del ciclo.
Cuando se encuentra la sentencia break en un bucle, la computadora termina
inmediatamente el ciclo y el control del programa pasa a la siguiente sentencia del
ciclo. Por ejemplo:
main()
{
int t;
for(t=0; t<100; t++)
{
printf("%d ", t);
if (t==10) break;
}
}
Este programa muestra en pantalla los números del 0 al 10, cuando alcanza el
valor 10 se cumple la condición de la sentencia if, se ejecuta la sentencia break y
sale del ciclo.
La sentencia continue funciona de manera similar a la sentencia break. Sin
embargo, en vez de forzar la salida, continue fuerza la siguiente iteración, por lo
que salta el código que falta para llegar a probar la condición. Por ejemplo, el
siguiente programa visualizará sólo los números pares:
main()
{
int x;
for( x=0; x<100; x++)
{
if (x%2)
continue;
printf("%d ",x);
105
}
}
Finalmente se considera el siguiente ejemplo donde se leen valores enteros y se
procesan de acuerdo a las siguientes condiciones. Si el valor que sea leído es
negativo, se desea imprimir un mensaje de error y se abandona el ciclo. Si el valor
es mayor que 100, se ignora y se continúa leyendo, y si el valor es cero, se desea
terminar el ciclo.
main()
{
int valor;
while( scanf("%d", &valor) == 1 && valor != 0)
{
if ( valor<0 )
{
printf("Valor no valido\n");
break;
/* Salir del ciclo */
}
if ( valor>100)
{
printf("Valor no valido\n");
continue;
/* Pasar al principio del ciclo nuevamente */
}
printf("Se garantiza que el valor leido esta entre 1 y 100");
}
}
ARREGLOS UNIDIMENSIONALES Y MULTIDIMENSIONALES
Los arreglos son una colección de variables del mismo tipo que se referencian
utilizando un nombre común. Un arreglo consta de posiciones de memoria
contigua. La dirección más baja corresponde al primer elemento y la más alta al
último. Un arreglo puede tener una o varias dimensiones. Para acceder a un
elemento en particular de un arreglo se usa un índice.
El formato para declarar un arreglo unidimensional es:
tipo nombre_arr [ tamaño ]
Por ejemplo, para declarar un arreglo de enteros llamado listanum con diez
elementos se hace de la siguiente forma:
int listanum[10];
En C, todos los arreglos usan cero como índice para el primer elemento. Por tanto,
el ejemplo anterior declara un arreglo de enteros con diez elementos desde
listanum[0] hasta listanum[9].
La forma como pueden ser accesados los elementos de un arreglo, es de la
siguiente forma:
106
listanum[2] = 15; /* Asigna 15 al 3er elemento del arreglo listanum*/
num = listanum[2]; /* Asigna el contenido del 3er elemento a la variable num */
El lenguaje C no realiza comprobación de contornos en los arreglos. En el caso de
que sobrepase el final durante una operación de asignación, entonces se
asignarán valores a otra variable o a un trozo del código, esto es, si se dimensiona
un arreglo de tamaño N, se puede referenciar el arreglo por encima de N sin
provocar ningún mensaje de error en tiempo de compilación o ejecución, incluso
aunque probablemente se provoque el fallo del programa. Como programador se
es responsable de asegurar que todos los arreglos sean lo suficientemente
grandes para guardar lo que pondrá en ellos el programa.
C permite arreglos con más de una dimensión, el formato general es:
tipo nombre_arr [ tam1 ][ tam2 ] ... [ tamN];
Por ejemplo un arreglo de enteros bidimensionales se escribirá como:
int tabladenums[50][50];
Observar que para declarar cada dimensión lleva sus propios paréntesis
cuadrados.
Para acceder los elementos se procede de forma similar al ejemplo del arreglo
unidimensional, esto es,
tabladenums[2][3] = 15; /* Asigna 15 al elemento de la 3ª fila y la 4ª columna*/
num = tabladenums[25][16];
A continuación se muestra un ejemplo que asigna al primer elemento de un arreglo
bidimensional cero, al siguiente 1, y así sucesivamente.
main()
{
int t,i,num[3][4];
for(t=0; t<3; ++t)
for(i=0; i<4; ++i)
num[t][i]=(t*4)+i*1;
for(t=0; t<3; ++t)
{
for(i=0; i<4; ++i)
printf("num[%d][%d]=%d ", t,i,num[t][i]);
printf("\n");
}
}
En C se permite la inicialización de arreglos, debiendo seguir el siguiente formato:
tipo nombre_arr[ tam1 ][ tam2 ] ... [ tamN] = {lista-valores};
Por ejemplo:
int i[10] = {1,2,3,4,5,6,7,8,9,10};
int num[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};
107
CADENAS
A diferencia de otros lenguajes de programación que emplean un tipo denominado
cadena string para manipular un conjunto de símbolos, en C, se debe simular
mediante un arreglo de caracteres, en donde la terminación de la cadena se debe
indicar con nulo. Un nulo se especifica como '\0'. Por lo anterior, cuando se declare
un arreglo de caracteres se debe considerar un carácter adicional a la cadena más
larga que se vaya a guardar. Por ejemplo, si se quiere declarar un arreglo cadena
que guarde una cadena de diez caracteres, se hará como:
char cadena[11];
Se pueden hacer también inicializaciones de arreglos de caracteres en donde
automáticamente C asigna el carácter nulo al final de la cadena, de la siguiente
forma:
char nombre_arr[ tam ]="cadena";
Por ejemplo, el siguiente fragmento inicializa cadena con ``hola'':
char cadena[5]="hola";
El código anterior es equivalente a:
char cadena[5]={'h','o','l','a','\0'};
Para asignar la entrada estándar a una cadena se puede usar la función scanf con
la opción %s (observar que no se requiere usar el operador &), de igual forma para
mostrarlo en la salida estándar.
Por ejemplo:
main()
{
char nombre[15], apellidos[30];
printf("Introduce tu nombre: ");
scanf("%s",nombre);
printf("Introduce tus apellidos: ");
scanf("%s",apellidos);
printf("Usted es %s %s\n",nombre,apellidos);
}
El lenguaje C no maneja cadenas de caracteres, como se hace con enteros o
flotantes, por lo que lo siguiente no es válido:
main()
{
char nombre[40], apellidos[40], completo[80];
nombre="José María";
/* Ilegal */
apellidos="Morelos y Pavón";
/* Ilegal */
completo="Gral."+nombre+apellidos; /* Ilegal */
}
Funciones
Una función es un conjunto de declaraciones, definiciones, expresiones y
sentencias que realizan una tarea específica.
108
El formato general de una función en C es:
especificador_de_tipo
nombre_de_función(
lista_de_parámetros
)
{
variables
locales
código de la función
}
El especificador_de_tipo indica el tipo del valor que la función devolverá mediante
el uso de return. El valor puede ser de cualquier tipo válido. Si no se específica un
valor, entonces la computadora asume por defecto que la función devolverá un
resultado entero. No se tienen siempre que incluir parámetros en una función. la
lista de parámetros puede estar vacía.
Las funciones terminan y regresan automáticamente al procedimiento que las
llamó cuando se encuentra la última llave}, o bien, se puede forzar el regreso antes
usando la sentencia return. Además del uso señalado la función return se usa para
devolver un valor.
Se examina a continuación un ejemplo que encuentra el promedio de dos enteros:
float encontprom(int num1, int num2)
{
float promedio;
promedio = (num1 + num2) / 2.0;
return(promedio);
}
main()
{
int a=7, b=10;
float resultado;
resultado = encontprom(a, b);
printf("Promedio=%f\n",resultado);
}
FUNCIONES VOID
Las funciones void dan una forma de emular, lo que en otros lenguajes se conocen
como procedimientos (por ejemplo, en PASCAL). Se usan cuando no requiere
regresar un valor. Se muestra un ejemplo que imprime los cuadrados de ciertos
números.
void cuadrados()
{
int contador;
for( contador=1; contador<10; contador++)
printf("%d\n",contador*contador);
}
main()
{
109
cuadrados();
}
En la función cuadrados no está definido ningún parámetro, y por otra parte
tampoco se emplea la sentencia return para regresar de la función.
FUNCIONES Y ARREGLOS
Cuando se usan un arreglo como un argumento a la función, se pasa sólo la
dirección del arreglo y no la copia del arreglo entero. Para fines prácticos podemos
considerar el nombre del arreglo sin ningún índice como la dirección del arreglo.
Considerar el siguiente ejemplo en donde se pasa un arreglo a la función imp_rev,
observar que no es necesario especificar la dimensión del arreglo cuando es un
parámetro de la función.
void imp_rev(char s[])
{
int t;
for( t=strlen(s)-1; t>=0; t--)
printf("%c",s[t]);
}
main()
{
char nombre[]="Facultad";
imp_rev(nombre);
}
Observar que en la función imp_rev se usa la función strlen para calcular la
longitud de la cadena sin incluir el terminador nulo. Por otra parte, la función
imp_rev no usa la sentencia return ni para terminar de usar la función, ni para
regresar algún valor.
Se muestra otro ejemplo,
float enconprom(int tam, float lista[])
{
int i;
float suma = 0.0;
for ( i=0; i<tam; i++)
suma += lista[i];
return(suma/tam);
}
main()
{
float numeros[]={2.3, 8.0, 15.0, 20.2, 44.01, -3.0, -2.9};
printf("El promedio de la lista es %f\n", enconprom(7,numeros) );
}
110
Para el caso de que se tenga que pasar un arreglo con más de una dimensión, no
se indica la primera dimensión pero, el resto de las dimensiones deben señalarse.
Se muestra a continuación un ejemplo:
void imprtabla(int tamx,int tamy, float tabla[][5])
{
int x,y;
for ( x=0; x<tamx; x++ )
{
for ( y=0; y<tamy; y++ )
printf("t[%d][%d]=%f",x,y,tabla[x][y]);
printf("\n");
}
}
PROTOTIPOS DE FUNCIONES
Antes de usar una función C debe tener conocimiento acerca del tipo de dato que
regresará y el tipo de los parámetros que la función espera.
El estándar ANSI de C introdujo una nueva (mejor) forma de hacer lo anterior
respecto a las versiones previas de C.
La importancia de usar prototipos de funciones es la siguiente:
Se hace el código más estructurado y por lo tanto, más fácil de leer.
Se permite al compilador de C revisar la sintaxis de las funciones llamadas.
Lo anterior es hecho, dependiendo del alcance de la función. Básicamente si una
función ha sido definida antes de que sea usada (o llamada), entonces se puede
usar la función sin problemas.
Si no es así, entonces la función se debe declarar. La declaración simplemente
maneja el tipo de dato que la función regresa y el tipo de parámetros usados por la
función.
Es una práctica usual y conveniente escribir el prototipo de todas las funciones al
principio del programa, sin embargo esto no es estrictamente necesario.
Para declarar un prototipo de una función se indicará el tipo de dato que regresará
la función, el nombre de la función y entre paréntesis la lista del tipo de los
parámetros de acuerdo al orden que aparecen en la definición de la función. Por
ejemplo:
int longcad(char []);
Lo anterior declara una función llamada longcad que regresa un valor entero y
acepta una cadena como parámetro.
111
Descargar