X-Git-Url: https://git.llucax.com/z.facultad/75.42/euler.git/blobdiff_plain/1cb6e00fd81f6ef48f5c45168b7dc211d8ce5ec3..b578c6e6beb9d5b1f2957f0b0367b91413b49d82:/enunciado.c diff --git a/enunciado.c b/enunciado.c index 364f1ed..3d877a1 100644 --- a/enunciado.c +++ b/enunciado.c @@ -1,60 +1,61 @@ -/* vim: set et ts=4 sw=4 : */ -/** \mainpage Taller de Programación I (75.42) - Trabajo Práctico I +/* vim: set et ts=4 sw=4 fdm=indent fdl=1 fdn=1 fo+=t: + * + * Taller de Programación (75.42). + * + * Ejercicio Número 1: + * Graficador de la solución de una ecuación diferencial por el método + * de Euler (explícito). + * + * Copyleft 2003 - Leandro Lucarella + * Puede copiar, modificar y distribuir este programa bajo los términos de + * la licencia GPL (http://www.gnu.org/). + * + * Creado: sáb ago 23 16:59:01 ART 2003 + * + * $Id$ + */ -\section objetivo Objetivo. - - Desarrollar un programa que resuelva, utilizando un método iterativo, - una función diferencial en un intervalo de tiempo dado. +/** \mainpage Taller de Programación I (75.42) +\section objetivo Objetivo. + Desarrollar un \ref main "programa" que resuelva, utilizando un + \ref metodo "método iterativo", una \ref funcion "función diferencial" + en un intervalo de tiempo dado. \note Utilizar sólo funciones ANSI C. - - \section desarrollo Desarrollo. - \subsection parte1 Parte 1. - - Dada una función diferencial \f$df_{(t)}\f$, desarrolle un - programa que calcule la integral de la misma utilizando el - método iterativo (Euler explícito), tal como se explica a - continuación. Almacene estos resultados en un vector. - + Dada una \ref funcion "función diferencial" \f$df_{(t)}\f$, + desarrolle un \ref main "programa" que calcule la integral de la + misma utilizando el método iterativo (\ref metodo "Euler explícito"), + tal como se explica a continuación. Almacene estos resultados en un + \ref Resultados "vector". \subsubsection funcion Función diferencial. - \f[ df_{(t)} = \frac{500 - f_{(t)}}{30} \f] - \subsubsection variables Variables. - - - \f$t_i\f$: Tiempo inicial de la iteración = 0. + - \f$t_i\f$: \ref T0 "Tiempo inicial de la iteración" = 0. Solicitar al usuario el ingreso de las restantes variables por línea de comandos: - - \f$f_{(t)}\f$: Estado inicial del sistema. - \f$t_f\f$: Tiempo final de la iteración (segundos). - - step: Paso (en segundos). - + - \f$step\f$: Paso (en segundos). - \subsubsection Método de iteración (Euler explícito). - + \subsubsection metodo Método de iteración (Euler explícito). \f[ - f_{(t + step)} = f_{(t)} + df_{(t)} * step + f_{(t + step)} = f_{(t)} + df_{(t)} \cdot step \f] - - \subsection parte2 Parte 2. - - Grafique los primeros 70 pasos (si los hay) de la función f en - formato texto, entre los valores máximos y mínimos que tome en + Grafique los primeros 70 pasos (si los hay) de la \ref funcion + "función" en formato texto, entre los valores máximos y mínimos que tome en este intervalo, utilizando 20 líneas horizontales. Por ejemplo: - \verbatim t0 = 0 @@ -67,13 +68,312 @@ \endverbatim +\section resolucion Resolución. + El \ref main "programa principal" se divide en FIXME N tareas principales, + cada una realizada por una función particular. + + \subsection obtencion Obtención y validación de parámetros del usuario. + Antes de comenzar a hacer cálculos es necesario obtener los valores de + las \ref variables "variables" de la entrada del usuario (en este caso + a través de parámetros de línea de comandos). Esto es realizado + por la función cargar_datos(). + + \subsection integracion Integración de la ecuación diferencial. + Reemplazando la \ref "ecuación diferencial" \f$ df_{(t)} \f$ + en la solución por el \ref metodo "método de Euler", obtenemos + la siguiente función: + \f[ + f_{(t + step)} = f_{(t)} + \frac{500 - f_{(t)}}{30} \cdot step + \f] + Resultando, en realidad, una función númerica de dos variables + (\f$ step \f$ y \f$ f_t \f$): + \f[ + f_{t + step} = f_{(step, f_t)} = f_t + \frac{500 - f_t}{30} \cdot step + \f] + Este paso es realizado por el macro FUNCION() para que sea más + veloz. + + Todo lo que resta es iterar, paso a paso, calculando los valores de la + función y almacenándolos en el \ref Resultados "vector de resultados", + tarea realizada por la función calcular(). + */ + + +/************************ BIBLIOTECAS USADAS *******************************/ + +/* Para utilizar printf() */ +#include + +/* Para utilizar strtod(), EXIT_SUCCES y EXIT_FAILURE */ +#include + + + +/***************************** CONSTANTES **********************************/ + +/** Indica un valor verdadero */ +#define TRUE 1 + +/** Indica un valor falso */ +#define FALSE 0 + +/** Máxima cantidad de pasos a calcular. */ +#define MAX_PASOS 70 + +/** Altura (en líneas) utilizadas para dibujar el gráfico. */ +#define ALTO 20 + +/** Tiempo inicial. */ +#define T0 0.0 + +/** Tiempo final por omisión. */ +#define DEFAULT_TF 70.0 + +/** Valor inicial de la función por omisión. */ +#define DEFAULT_F0 0.0 + +/** Valor del pas de iteración por omisión. */ +#define DEFAULT_PASO 1.0 + + + +/******************************* MACROS ************************************/ + /** - -Descripción breve de la función Test. + * \ref integracion "Calcula" el siguiente paso de la función numérica. + * Obtiene el siguiente valor de \f$ f \f$, integrando numéricamente la \ref + * funcion "ecuación diferencial" por el \ref metodo "método de euler". + * \f[ + * f_{t + step} = f_{(step, f_t)} = f_t + \frac{500 - f_t}{30} \cdot step + * \f] + * + * \note Se pone en un macro para poder reemplazar fácilmente la función sin la + * pérdida de velocidad que agrega la indirección de un llamado a una + * función. + */ +#define FUNCION(paso, ft) ((ft) + (500.0 - (ft)) / 30.0 * paso) -En la descripción completa tengo un link a la \ref parte1 "página principal". -*/ -void Test(void); \ No newline at end of file + +/************************ TIPOS DE DATOS UTILIZADOS ************************/ + +/** + * Tipo de dato utilizado para medir el tiempo y el valor de la función. + * Se define como un tipo de dato propio para cambiar fácilmente la precisión. + */ +typedef float Real; + +/** + * Vector que representa los resultados. + * El índice del vector representa el número de iteración (que multiplicado por + * el paso da el tiempo, o eje X). El contenido es el valor de la + * \ref funcion "función" en ese instante. + */ +typedef Real Resultados[MAX_PASOS]; + + + +/********************************* FUNCIONES *******************************/ + +/** + * Imprime una explicación de como usar el programa. + * + * \param fh Archivo en donde imprimir el mensaje (ej: stdout o stderr). + */ +void imprimir_uso(FILE* fh) { + fprintf(fh, "\n"); + fprintf(fh, "Modo de uso:\n"); + fprintf(fh, " tp1 [paso [tf [f0]]]\n"); + fprintf(fh, "\n"); + fprintf(fh, "Donde:\n"); + fprintf(fh, " paso: Paso a utilizar (%.2f por omisión).\n", DEFAULT_PASO); + fprintf(fh, " tf: Tiempo final (%.2f por omisión).\n", DEFAULT_TF); + fprintf(fh, " f0: Valor inicial de la función (%.2f por omisión).\n", + DEFAULT_F0); +} + +/** + * Carga (validando) un dato real en una variable. + * Si al validar hay algún error, muestra un mensaje por la salida de error y + * devuelve false. + * + * \param arg Argumento a cargar (y validar). + * \param var Variable en donde cargar el real. + * \param nom Nombre de la variable que se quiere cargar (para el mensaje de + * error, en caso de haberlo). + * + * \return TRUE si se cargó bien, FALSE si no. + */ +int argtod(const char* arg, Real* var, const char* nom) { + /* Puntero al último caracter bien interpretado por strtod. */ + char* err = NULL; + /* Realiza la conversión de un string a un Real */ + *var = strtod(arg, &err); + /* Si el caracter donde apunta endptr es 0 es porque se interpretó toda la + * cadena bien */ + if ((char)*err == '\0') { + return TRUE; + /* Si no, es que hubo error. */ + } else { + fprintf(stderr, "Error: El parámetro '%s' debe ser un número ", nom); + fprintf(stderr, "real. Usted ingresó '%s' pero '%s' no ", arg, err); + fprintf(stderr, "pudo ser interpretado.\n"); + return FALSE; + } +} + +/** + * Carga los datos necesarios por el programai. + * Obtiene los datos desde los parámetros de la línea de comandos, validándolos + * y mostrando un mensaje de error en caso de haberlo. + * + * \param argc Cantidad de parámetros de línea de comandos ingresados. + * \param argv Parámetros de línea de comandos. + * \param paso Paso utilizado para las iteraciones. + * \param tf Tiempo final. + * \param f0 Valor inicial de la función. + * + * \return TRUE si se cargaron bien, FALSE si no. + * \todo Verificar que el paso no sea cero y que tf > ti. + */ +int cargar_datos(int argc, const char** argv, Real* paso, Real* tf, Real* f0) { + switch (argc) { + /* Si no tiene parámetros usa los valores por omisión. */ + case 1: + break; + /* Si tiene de 1 a 3 parámetros, los lee y valida, saliendo con un + * mensaje de error en caso de haberlo. */ + case 4: + /* Hay 3 parámetros, lee el 3er parámetro. */ + if (!argtod(argv[3], f0, "f0")) { + return FALSE; + } + /* Continúa con el resto de los parámetros. */ + case 3: + /* Hay al menos 2 parámetros, lee el 2do parámetro. */ + if (!argtod(argv[2], tf, "tf")) { + return FALSE; + } + /* Continúa con el resto de los parámetros. */ + case 2: + /* Hay al menos 1 parámetro, lee el 1er parámetro. */ + if (!argtod(argv[1], paso, "paso")) { + return FALSE; + } + break; /* Finaliza el switch (no lee más parámetros). */ + /* Hay demasiados parámetros, sale con mensaje de error. */ + default: + fprintf(stderr, "Error: Demasiados parámetros.\n"); + return FALSE; + } + /* TODO Verificar que el paso no sea cero y que tf > ti. */ + /* XXX - sacar */ + printf("paso = %f, tf = %f, f0 = %f\n", *paso, *tf, *f0); + return TRUE; +} + +/** + * \ref integracion "Calcula" todos los valores de la función. + * + * \param res Vector donde se guardan los resultados. + * \param paso Paso de iteración. + * \param ti Tiempo de inicio de la iteración. + * \param tf Tiempo final de la iteración. + * \param f0 Valor inicial de la función. + * + * \return Cantidad de pasos realizados. + */ +size_t calcular(Resultados* res, Real paso, Real ti, Real tf, Real f0) { + /* Índice para iterar. */ + size_t i; + /* Calculo la cantidad de pasos necesarios según el tiempo inicial, el + * tiempo final y el "tamaño" del paso. */ + size_t pasos = (size_t)((tf - ti) / paso); + /* Respeto la cantidad máxima de pasos admitida. */ + if (pasos > MAX_PASOS) { + pasos = MAX_PASOS; + } + /* Agrego el valor inicial de la función (para empezar a iterar). */ + (*res)[0] = f0; + /* Itero paso a paso calculando el valor de la función. */ + for (i = 1; i < pasos; i++) { + /* f(t+paso) = FUNCION(paso, f(t)) */ + (*res)[i] = FUNCION(paso, (*res)[i-1]); + /* FIXME sacar!!!! */ + printf("i = %i, t = %.2f, f(t) = %.2f\n", i, (Real)i * paso, (*res)[i]); + } + return pasos; +} + +/** + * Devuelve el valor máximo de los resultados. + * + * \param res Vector de resultados. + * \param pasos Cantidad de pasos a iterar. + * + * \return Máximo resultado. + */ +Real max(const Resultados* res, size_t pasos) { + size_t i; + Real max = (*res)[0]; + for (i = 1; i < pasos; i++) { + if ((*res)[i] > max) { + max = (*res)[i]; + } + } + return max; +} + +/** + * Devuelve el valor mínimo de los resultados. + * + * \param res Vector de resultados. + * \param pasos Cantidad de pasos a iterar. + * + * \return Mínimo resultado. + */ +Real min(const Resultados* res, size_t pasos) { + size_t i; + Real min = (*res)[0]; + for (i = 1; i < pasos; i++) { + if ((*res)[i] < min) { + min = (*res)[i]; + } + } + return min; +} + +/** + * Programa principal. + * Este es el programa que se encarga de resolver el trabajo práctico. + * + * \param argc Cantidad de parámetros de línea de comandos ingresados. + * \param argv Parámetros de línea de comandos. + * + * \return EXIT_FAILURE si hubo un error, si no EXIT_SUCCESS. + */ +int main(int argc, const char** argv) { + /* TODO: Declaración de variables. */ + Resultados resultados; + size_t pasos; + Real paso = DEFAULT_PASO; + Real tf = DEFAULT_TF; + Real f0 = DEFAULT_F0; + + /* Obtención de datos. */ + if (!cargar_datos(argc, argv, &paso, &tf, &f0)) { + imprimir_uso(stderr); + return EXIT_FAILURE; + } + + /* Cálculo de la solución, dejando en un array los valores de cada paso. */ + pasos = calcular(&resultados, paso, T0, tf, f0); + + /* TODO: Barrido de pantalla de arriba hacia abajo consultando el array + * y dibujando los resultados pertinentes. */ + /* TODO: Liberación de memoria y otras limpiezas, en caso de ser necesario. */ + return EXIT_SUCCESS; +} +