]> git.llucax.com Git - z.facultad/75.42/euler.git/commitdiff
Se completa el 80% del TP aproximadamente.
authorLeandro Lucarella <llucax@gmail.com>
Sun, 24 Aug 2003 06:22:08 +0000 (06:22 +0000)
committerLeandro Lucarella <llucax@gmail.com>
Sun, 24 Aug 2003 06:22:08 +0000 (06:22 +0000)
Falta dibujar los resultados nada más...

enunciado.c

index 364f1ed190803193fb0470e28c5e97b507e66273..3d877a166465f37f03dc1105f3f9f0c7cb8279b5 100644 (file)
@@ -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 <llucare@fi.uba.ar>
+ * 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
 
         \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 <stdio.h>
+
+/* Para utilizar strtod(), EXIT_SUCCES y EXIT_FAILURE */
+#include <stdlib.h>
+
+
+
+/***************************** 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;
+}
+