CB100

Notas, resueltos y tps de la materia Algoritmos y Estructuras de Datos
Index Commits Files Refs README
commit 0e6d2918b84297c32a629dd5bbdd44b7b2ea7aad
parent e6ba123e97f8494520e042fe5601581abb9ffbd5
Author: Martin Kloeckner <mjkloeckner@gmail.com>
Date:   Wed, 27 Mar 2024 03:00:30 -0300

Update text content

Remove `Modularización` section

Diffstat:
Mtps/1/informe/main.tex | 259++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
1 file changed, 151 insertions(+), 108 deletions(-)
diff --git a/tps/1/informe/main.tex b/tps/1/informe/main.tex
@@ -69,6 +69,8 @@ fontupper={\ttfamily\mystrut}}
 \lhead{\thetitle}
 \cfoot{\thepage}
 
+\def\fileName{primos.cpp}
+
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 \begin{document}
 
@@ -147,7 +149,7 @@ por \code{4}.
 \subsection{Criba de Eratóstenes}
 
 La criba de Eratóstenes es un algoritmo sofisticado y eficiente para descartar
-numeros no primos de una lista de numeros. En la Criba de Eratóstenes se itera
+números no primos de una lista de números. En la Criba de Eratóstenes se itera
 número a número sobre una lista ordenada y finita de números naturales,
 comenzando por el primero, si se trata de un número primo, se buscan todos los
 múltiplos en la lista de números y se descartan, luego se avanza al siguiente
@@ -155,7 +157,7 @@ número que no haya sido descartado, el cual siempre resulta un número primo, y
 se descartan sus múltiplos, así sucesivamente hasta el final de la lista. 
 
 La criba de Eratóstenes se puede optimizar basándose en la propiedad de que los
-números no primos (numeros compuestos), se pueden expresar como el producto de
+números no primos (números compuestos), se pueden expresar como el producto de
 números primos, por ejemplo \code{4 = 2*2} o \code{21 = 3*7}, por lo tanto, si
 un número no es primo, debe tener al menos un factor primo que sea menor o igual
 que su raíz cuadrada, ya que de lo contrario, el producto de los factores
@@ -163,9 +165,9 @@ resultaria en un numero mayor.
 
 De la propiedad anterior se deduce que se puede detener la iteración en la lista
 de números cuando se llega a la raiz cuadrado del numero máximo en la lista, ya
-que los numeros no primos por encima de éste tendran algun factor menor que la
+que los números no primos por encima de éste tendran algun factor menor que la
 raiz cuadrada del numero máximo, de este modo se reduce drasticamente el numero
-de iteraciones necesarias para descartar los numeros no primos de la lista.
+de iteraciones necesarias para descartar los números no primos de la lista.
 
 \section{Desarrollo}
 
@@ -174,105 +176,102 @@ introducción, se utilizó puramente el lenguaje C++, en particular en su versi
 estándar C98. Para organizar el proceso de compilación se utilizó la herramienta
 \code{make}.
 
-% Si bien se mencionaron dos algoritmos para hallar números primos, se utiliza
-% la Criba de Eratóstenes ya que resulta mucho más eficiente que el algoritmo
-% primitivo.
-
 \subsection{Implementación en C++}
 
-Para representar el máximo de números en la lista se define la constante
-\code{MAXIMO}.
+El codigo fuente de la aplicacion esta contenido en un solo archivo:
+\code{\fileName}.
+
+En la primera parte del archivo se definen dos constantes globales
+\code{OUTPUT\_FILE\_PATH}, que representa el nombre del archivo de salida, y
+\code{MAXIMO} que representa el largo de la lista de números a analizar.
 
 \begin{fullgrayverb}[\mbox{}]
-// Se define la constante `MAXIMO` que representa la cantidad
-// de números en la lista
+#define OUTPUT_FILE_PATH "primos.txt"
 const unsigned int MAXIMO = 100000000;
 \end{fullgrayverb}
 
-La Criba de Eratóstenes opera sobre una lista de números naturales ordenada.
-Para representarla se utiliza la librería estándar \code{vector} de la cual
-se define un vector de tipo \code{bool} inicialmente con valores \code{true}
-indicando que son todos números primos, luego al aplicar el algoritmo éste
-cambiará el contenido del vector a \code{false} en las posiciones en que se
-ubiquen números no primos.
+Luego se declara la función \code{vectorDiscardNonPrimes}, esta función asigna
+el valor \code{false} a los casilleros del vector, que recibe como argumento, en
+los cuales el índice es un numero no primo.
 
 \begin{fullgrayverb}[\mbox{}]
-// Se define un vector de tipo `bool` de largo `MAXIMO`
-// con valores iniciales `true`
-std::vector<bool> numeros(MAXIMO, true);
+// Se declara la función `vectorDiscardNonPrimes`
+void vectorDiscardNonPrimes(std::vector<bool>& v);
 \end{fullgrayverb}
 
-Comenzando por los casos particulares, \code{0} y \code{1} no son números
-primos, por lo que se puede cambiar su valor a \code{false} directamente.
+Se declara la función \code{vectorExportToFilePath} la cual recibe como
+argumento un vector, un archivo abierto y una referencia a una variable. Esta
+función imprime sobre el archivo las posiciones del vector en donde el
+contenido es \code{true}, la cantidad de números impresos los asigna a la
+variable \code{primesWritten}.
 
 \begin{fullgrayverb}[\mbox{}]
-// 0 y 1 no son primos
-numeros[0] = numeros[1] = false;
+// Se declara la función `vectorExportToFilePath`
+void vectorExportToFilePath(
+         const std::vector<bool> v,
+         std::ofstream& fp,
+         unsigned int &primesWritten);
 \end{fullgrayverb}
 
-Para determinar los números no primos y descartarlos de la lista de números, se
-itera sobre el vector descartando todos los múltiplos de los números primos,
-hasta llegar a la raíz cuadrada del numero máximo de la lista. Para determinar
-la raíz cuadrada del numero máximo se utiliza la función
-\code{std::sqrt} de la librería estándar \code{cmath} 
+\subsubsection{Funcion \code{main}}
+
+La Criba de Eratóstenes opera sobre una lista de números naturales ordenada,
+para representarla se utiliza la librería estándar \code{vector}, de la cual
+se define un vector de tipo \code{bool} inicialmente con valores \code{true}
+indicando que son todos números primos.
 
 \begin{fullgrayverb}[\mbox{}]
-for (i = 2; i < std::sqrt(MAXIMO); ++i) {
-    if(numeros[i] == true) {
-        for (j = i; j <= (MAXIMO/i); ++j) {
-            numeros[i*j] = false;
-        }
-    }
-}
+// Se define un vector de tipo `bool` de largo `MAXIMO`
+// con valores iniciales `true`
+std::vector<bool> numeros(MAXIMO, true);
 \end{fullgrayverb}
 
 \pagebreak
-Para la impresión en el archivo se define una constante para representar el
-nombre del archivo de salida.
+Para la manipulación del archivo de salida, se utiliza la clase \code{ofstream}
+de la librería estándar \code{fstream}.
 
 \begin{fullgrayverb}[\mbox{}]
-// Se define el nombre del archivo a imprimir los números primos
-#define OUTPUT_FILE_PATH "primos.txt"
+// Se define el objeto fp para representar el archivo de salida
+std::ofstream fp;
 \end{fullgrayverb}
 
-Para la manipulación del archivo se utiliza la clase \code{ofstream} de la
-librería estándar \code{fstream}.
+Además se define la variable \code{primesFound} la cual luego contendrá la
+cantidad de números primos hallados.
 
 \begin{fullgrayverb}[\mbox{}]
-// Se define el objeto fp para representar el archivo de salida
-std::ofstream fp;
+unsigned int primesFound;
 \end{fullgrayverb}
 
-Utilizando los métodos \code{open} e \code{is\_open} del objeto \code{fp} se abre
-el archivo, y se comprueba si hubo algún error, en caso afirmativo, se informa
-al usuario haciendo uso de métodos de la librería estándar \code{iostream} y se
-termina la ejecución con un código de error \code{-1}.
+Para descartar los números no primos del vector \code{numeros} se invoca la
+función \linebreak\code{vectorDiscardNonPrimes}.
+
+\begin{fullgrayverb}[\mbox{}]
+vectorDiscardNonPrimes(numeros);
+\end{fullgrayverb}
+
+Luego de finalizada la función, se imprime en el archivo de salida los índices
+del vector correspondientes a los casilleros que permanecen con un valor
+\code{true}, ya que éstos representan los números primos.
+
+Utilizando los métodos \code{open} e \code{is\_open} del objeto \code{fp} se
+abre el archivo \code{primos.txt} y se comprueba si hubo algún error, en caso
+afirmativo, se informa al usuario haciendo uso de métodos de la librería
+estándar \code{iostream} y se termina la ejecución inmediatamente con un código
+de error \code{-1}.
 
 \begin{fullgrayverb}[\mbox{}]
-// Se abre el archivo en modo escritura, de haber un error lo reporta
-// al usuario y se termina la ejecución inmediatamente
 fp.open(OUTPUT_FILE_PATH);
 if (fp.is_open() == false) {
-    std::cerr << "ERROR: No se pudo abrir `" OUTPUT_FILE_PATH << "`\n";
+    std::cerr << "ERROR: No se pudo abrir `" OUTPUT_FILE_PATH "`\n";
     return -1;
 }
 \end{fullgrayverb}
 
-Para imprimir los números primos hallados en el archivo, se recorre la lista en
-búsqueda de aquellos casilleros que contengan \code{true}, ya que resultan
-números primos, en caso de encontrarse se imprime la posición al archivo, los
-casilleros que contienen \code{false} son ignorados. 
-
-La variable \code{j} se utiliza para contar la cantidad de números primos
-encontrados, ya que luego será informada al usuario por la consola.
+Para exportar los números primos del vector se invoca la función
+\linebreak\code{vectorExportToFilePath}
 
 \begin{fullgrayverb}[\mbox{}]
-for (i = 2, j = 0; i < numeros.size(); ++i) {
-    if(numeros[i] == true) {
-        fp << i << std::endl;
-        j++;
-    }
-}
+vectorExportToFilePath(numeros, fp, primesFound);
 \end{fullgrayverb}
 
 Luego de finalizada la impresión en el archivo, se cierra utilizando el método
@@ -282,58 +281,93 @@ Luego de finalizada la impresión en el archivo, se cierra utilizando el método
 fp.close();
 \end{fullgrayverb}
 
-\pagebreak
 Finalmente se imprime un mensaje al usuario indicando que terminó la impresión,
 y se informa la cantidad de números primos hallados, esto se hace utilizando
 metodos de la librería estándar \code{iostream}.
 
 \begin{fullgrayverb}[\mbox{}]
-std::cout << "Se encontraron `" << j << "` números primos\n";
+std::cout << "Se encontraron `" << primesFound << "` números primos\n";
 \end{fullgrayverb}
 
-\subsubsection{Modularización}
+Por ultimo se termina la ejecución con un codigo \code{0} indicando que el
+programa se ejecutó exitosamente.
+
+\begin{fullgrayverb}[\mbox{}]
+return 0;
+\end{fullgrayverb}
 
-A medida que se va agregando mas funcionalidad a la aplicacion, el codigo se
-extiende resultando mas dificil de leer, es por esto que resulta conveniente
-extraer cierta funcionalidad del punto de entrada \code{main} a sus propias
-funcinones. En el archvio \code{main.cpp} de la aplicacion se extraer dos
-funciones. \code{vectorDiscardNonPrimes} y \code{vectorExportToFilePath}.
+\subsubsection{Función \code{vectorDiscardNonPrimes}}
 
-\code{vectorDiscardNonPrimes} pone en falso los casilleros del vector que recibe
-como parametro en los cuales el indice resulta un numero no primo.
+La función \code{vectorDiscardNonPrimes} recibe una referencia a un vector de
+tipo \code{bool} como argumento y asigna \code{false} a los casilleros en los
+cuales el índice del mismo es un número no primo. 
+
+La función comienza descartando los casos particulares, \code{0} y \code{1}, por
+lo que asigna \code{false} a los índices respectivos directamente.
 
 \begin{fullgrayverb}[\mbox{}]
-void vectorDiscardNonPrimes(std::vector<bool>& v);
+// 0 y 1 no son primos
+numeros[0] = numeros[1] = false;
 \end{fullgrayverb}
 
-\code{vectorExportToFilePath} imprime el indice de los casillero correspondiente
-al vector que recibe como parametro en los cuales el casillero contiene una
-valor \code{true}, la impresion se realiza sobre un archivo que recibe como
-parametro, este parámetro debe ser un puntero a un archivo abierto, además esta
-funcion recibe una referencia a una variable, en esta variable se almacena la
-cantidad de numeros impresos sobre el archivo.
+Luego itera sobre el vector asignando \code{false} a todos los múltiplos de los
+números primos, hasta llegar a la raíz cuadrada del numero máximo de la lista.
+Para determinar la raíz cuadrada del numero máximo se utiliza la función
+\code{sqrt} de la librería estándar \code{cmath} 
 
 \begin{fullgrayverb}[\mbox{}]
-void vectorExportToFilePath(
-         const std::vector<bool> v,
-         std::ofstream& fp,
-         unsigned int &primesWritten);
+for (size_t i = 2; i < std::sqrt(MAXIMO); ++i) {
+    if(vector[i] == true) {
+        for (size_t j = i; j <= (MAXIMO/i); ++j) {
+            vector[i*j] = false;
+        }
+    }
+}
+\end{fullgrayverb}
+
+\subsubsection{Función \code{vectorExportToFilePath}}
+
+La función \code{vectorExportToFilePath} recibe como argumento un vector, una
+archivo abierto y una referencia a una variable. Luego de invocada la función
+imprime sobre el archivo los índices del vector en los cuales el contenido es
+\code{true}, a la variable que recibe le asigna la cantidad de índices impresos.
+
+Al comienzo de la función se asigna a la variable \code{primesWritten} cero.
+
+\begin{fullgrayverb}[\mbox{}]
+primesWritten = 0;
+\end{fullgrayverb}
+
+Para la impresion en el archivo, la función recorre el vector en búsqueda de
+aquellos casilleros que contengan \code{true}, en los casos afirmativos imprime
+el índice del vector al archivo e incrementa el contenido de la variable
+\code{primesWritten}, los casilleros que contienen \code{false} son ignorados.
+
+\begin{fullgrayverb}[\mbox{}]
+for (size_t i = 2; i < v.size(); ++i) {
+    if(v[i]) {
+        fp << i << std::endl;
+        primesWritten++;
+    }
+}
 \end{fullgrayverb}
 
 \subsection{Makefile}
 
-Si bien es importante la utilización de buenas practicas en el código fuente de
-la aplicación, muchas veces el proceso compilación no se tiene en cuenta,
-pero es también muy importante para generar una aplicación robusta y fácil de
-diagnosticar en caso de fallas.
+% Si bien es importante la utilización de buenas practicas en el código fuente de
+% la aplicación, muchas veces el proceso de compilación no se tiene en cuenta,
+% pero es también muy importante para generar una aplicación robusta y fácil de
+% diagnosticar en caso de fallas.
 
-Para el desarrollo de la aplicación se utiliza la herramienta \code{GNU make},
-la cual lee un archivo de nombre exclusivo \code{Makefile} que posee su propia
-sintaxis. El archivo \code{Makefile} utilizado intenta ser reutilizable y de
-fácil modificación, es por esto que al comienzo del mismo se utilizan variables
-para almacenar las principales opciones que el usuario podría querer modificar,
-por ejemplo el nombre del compilador utilizado, el nombre final del archivo,
-entre otras.
+Para organizar el proceso de compilacion de la aplicación se utiliza la
+herramienta \code{GNU make}, la cual lee un archivo de nombre exclusivo
+\code{Makefile} que posee su propia sintaxis.
+
+El archivo \code{Makefile} utilizado intenta ser reutilizable y de fácil
+modificación, es por esto que al comienzo del mismo se utilizan variables para
+almacenar las principales opciones que el usuario podría querer modificar, por
+ejemplo el nombre del compilador utilizado, el nombre final del archivo, entre
+otras.
 
 \begin{fullgrayverb}[\mbox{}]
 CC := g++
@@ -343,8 +377,8 @@ OBJS := $(SRCS:.cpp=.o)
 TARGET := primos
 \end{fullgrayverb}
 
-Para compilar y enlazar las cabeceras y el programa final, se utilizan las
-siguientes etiquetas:
+Para compilar el programa y enlazar cabeceras, en caso de que haya, se utilizan
+las siguientes reglas:
 
 \begin{fullgrayverb}[\mbox{}]
 $(TARGET): $(OBJS)
@@ -354,11 +388,11 @@ $(TARGET): $(OBJS)
     $(CC) $(CFLAGS) -c $< -o $@
 \end{fullgrayverb}
 
-La etiqueta que contiene \code{\$(TARGET)} se expande al nombre final de la
+La regla que contiene \code{\$(TARGET)} se expande al nombre final de la
 aplicación, luego de los dos puntos se indican las dependencias, las cuales se
 expanden a los archivos objeto (los que terminan en \code{.o})
 
-Para compilar los archivos objetos se utiliza la segunda etiqueta, la cual
+Para compilar los archivos objetos se utiliza la segunda regla, la cual
 corresponde a una \code{pattern rule}, una extension exclusiva de \code{GNU}, 
 esta \code{pattern rule} compila todos los archivos terminados en \code{.cpp} a
 archivos objeto terminaods en \code{.o}.
@@ -397,10 +431,14 @@ nuevamente la aplicación, se obtiene el siguiente mensaje en la consola:
 Se encontraron `5761455` números primos en `46.21` segundos
 \end{fullgrayverb}
 
-Como se puede observar en el segundo caso, el tiempo de ejecución se incrementa
-en un \code{406\%}.
+Comparando los dos resultados, se puede observar que al utilizar las
+optimizaciones de compilador el tiempo de ejecución se reduce en
+aproximadamente \code{75\%}.
+
+% Como se puede observar en el segundo caso, el tiempo de ejecución se incrementa
+% en aproximadamente \code{306\%}, dicho de otro modo, utilizando las
+% optimizaciones el tiempo de ejecucion se reduce en aproximadamente \code{75\%}.
 
-\pagebreak
 \section{Instalación}
 
 Para instalar la aplicación usted debe de disponer de una copia del código
@@ -422,15 +460,16 @@ Compruebe que este en el mismo directorio que el archivo \code{main.cpp}. Luego,
 compile el código con el programa \code{make}
 
 \begin{fullgrayverb}
- $ make
+$ make
 \end{fullgrayverb}$
 
 Luego puede ejecutar la aplicación de la siguiente manera:
 
 \begin{fullgrayverb}
- $ ./primos
+$ ./primos
 \end{fullgrayverb}$
 
+\pagebreak
 \subsection{Windows}
 
 En el caso de utilizar Windows como sistema operativo, usted puede configurar
@@ -444,17 +483,21 @@ De manera análoga si usted dispone de un compilador de C++ puede compilar la
 aplicación directamente desde la consola, sin la necesidad de utilizar la
 herramienta \code{make}, para eso ejecute el siguiente comando:
 
+% Define your variable
+\newcommand{\myvariable}{\textbf{Hello}}
+
 \begin{fullgrayverb}
- $ g++ -Wall -Wshadow -ansi -std=c++98 -O3 main.cpp -o primos
+$ g++ -Wall -Wshadow -ansi -std=c++98 -O3 $\fileName$ -o primos
 \end{fullgrayverb}$
 
+
 Tenga en cuenta la sintaxis de su compilador ya que puede variar, el comando
 anterior está previsto para \href{https://www.mingw-w64.org/}{MinGW-w64}
 
 Luego de compilado la aplicación usted la puede ejecutar de la siguiente manera 
 
 \begin{fullgrayverb}
- $ ./primos
+$ ./primos
 \end{fullgrayverb}$
 
 \section{Conclusión}