CB100

Notas, resueltos y tps de la materia Algoritmos y Estructuras de Datos
Index Commits Files Refs README
tps/1/informe/informe.tex (18826B)
   1 \documentclass[12pt]{article}
   2 \usepackage[spanish]{babel}
   3 \usepackage{natbib}
   4 \usepackage{url}
   5 \usepackage[utf8]{inputenc}
   6 \usepackage{amsmath}
   7 \usepackage{graphicx}
   8 \usepackage{parskip}
   9 \usepackage{fancyhdr}
  10 \usepackage{vmargin}
  11 \usepackage[ddmmyy]{datetime}
  12 \usepackage{anyfontsize}
  13 \usepackage{helvet}
  14 \renewcommand{\familydefault}{phv}
  15 \usepackage{xcolor}
  16 \usepackage[T1]{fontenc}
  17 
  18 % \setmarginsrb{leftmargin}{topmargin}{rightmargin}{bottommargin}%
  19 %          {headheight}{headsep}{footheight}{footskip}
  20 \setmarginsrb{2.5 cm}{2.25 cm}{2.5 cm}{1.50 cm}
  21            {1 cm}{1.25 cm}{1 cm}{1.50 cm}
  22 
  23 \usepackage{color}
  24 \usepackage{hyperref}
  25 \hypersetup{
  26     colorlinks=true, % set true if you want colored links
  27     linktoc=all,     % set to all if you want sections and subsections linked
  28     linkcolor=blue,  % choose some color if you want links to stand out
  29     urlcolor=blue,
  30 }
  31 
  32 \usepackage{subfig}
  33 \usepackage[labelformat=empty]{caption}
  34 
  35 \usepackage{fancyvrb}
  36 \fvset{xleftmargin=\mathindent}
  37 
  38 % code blocks
  39 \usepackage{verbatimbox}
  40 \newenvironment{fullgrayverb}
  41 {\verbbox}
  42 {\endverbbox\par\colorbox{gray!25}{\parbox{\textwidth}{\theverbbox}}\par}
  43 
  44 % inline code blocks
  45 \usepackage{tcolorbox}
  46 \newcommand\mystrut{\rule[-3pt]{0pt}{12pt}}
  47 \newtcbox{\code}{on line, boxrule=0pt, boxsep=0pt, top=0pt,
  48 left=0pt, bottom=0pt, right=0pt, colback=gray!25, colframe=white,
  49 fontupper={\ttfamily\mystrut}}
  50 
  51 \title{Números Primos}          % Titulo del trabajo.
  52 \author{Martin J. Klöckner}     % Nombre y apellido
  53 \newcommand{\padron}{123456}    % Padrón
  54 \newcommand{\tpnumber}{1}       % Número de trabajo práctico
  55 \date{\today}                   % Fecha (automática)
  56 
  57 \makeatletter
  58 \let\thetitle\@title
  59 \let\theauthor\@author
  60 \let\thedate\@date
  61 \makeatother
  62 
  63 \pagestyle{fancy}
  64 \fancyhf{}
  65 \rhead{\theauthor}
  66 \lhead{\thetitle}
  67 \cfoot{\thepage}
  68 
  69 \begin{document}
  70 \begin{titlepage}
  71     \vspace*{-2.5cm}
  72     {\centering
  73     \includegraphics[width=1.00\textwidth]{img/logofiuba.png}\\[2.25 cm]}
  74     \centering
  75     \textsc{\Large CB100}\\[0.2 cm]
  76     \textsc{\large Algoritmos y Estructuras de Datos}\\[4 cm]
  77     \textcolor{cyan}{{\fontsize{40}{60}\selectfont \bfseries \thetitle}}\\[0.5cm]
  78     {\Large \bfseries Trabajo Práctico N$^\circ$\tpnumber}\\[5cm]
  79 
  80 
  81     \vfill
  82     \noindent\makebox[\linewidth]{\rule{\textwidth}{0.4pt}}\\[0.5cm]
  83     \begin{minipage}{.46\textwidth}
  84     \textbf{Autor}\\
  85     \theauthor
  86     \end{minipage}%
  87     \begin{minipage}{.34\textwidth}
  88     \textbf{Legajo}\\
  89     \padron
  90     \end{minipage}%
  91     \begin{minipage}{.2\textwidth}
  92      \begin{flushright}
  93         \textbf{Fecha}\\
  94         \thedate
  95     \end{flushright}
  96     \end{minipage}
  97 \end{titlepage}
  98 
  99 {
 100     \hypersetup{linkcolor=black} % colorlinks=true option is used
 101     \tableofcontents
 102     \pagebreak
 103 }
 104 
 105 \section{Introducción}
 106 
 107 En este trabajo práctico se desarrolla una aplicación de consola que busca
 108 números primos hasta un valor máximo determinado por el usuario, cuando
 109 finaliza, los imprime en un archivo de texto plano en el directorio de ejecución
 110 de la aplicación.
 111 
 112 El lenguaje de programación utilizado para el desarrollo de la aplicación es C++
 113 y el algoritmo utilizado para hallar los números primos es la Criba de
 114 Eratóstenes.
 115 
 116 \subsection{Números Primos}
 117 
 118 Los números primos por definición son aquellos números positivos que solo son
 119 divisibles por \code{1} y por si mismos, divisibles en términos de que el resto
 120 de la división es nulo. El \code{0} y el \code{1} son dos casos particulares en
 121 los cuales ambos son considerados no primos, el \code{0} por razones obvias, no
 122 está definida la división por \code{0}, y el \code{1} por que solo es divisible
 123 por si mismo, lo cual hace que solo sea divisible por un número.
 124 
 125 El número \code{7}, por ejemplo, es un número primo, ya que solo es divisible
 126 por \code{1} y por \code{7}. El número \code{8} no es un número primo, ya que
 127 ademas de ser divisible por si mismo y por \code{1}, es divisible por \code{2} y
 128 por \code{4}.
 129 
 130 \subsection{Criba de Eratóstenes}
 131 
 132 La criba de Eratóstenes es un algoritmo sofisticado y eficiente para descartar
 133 números no primos de una lista de números. En la Criba de Eratóstenes se itera
 134 número a número sobre una lista ordenada y finita de números naturales,
 135 comenzando por el primero, si se trata de un número primo, se buscan todos los
 136 múltiplos en la lista de números y se descartan, luego se avanza al siguiente
 137 número que no haya sido descartado, el cual siempre resulta un número primo, y
 138 se descartan sus múltiplos, así sucesivamente hasta el final de la lista. 
 139 
 140 La criba de Eratóstenes se puede optimizar basándose en la propiedad de que los
 141 números no primos (números compuestos), se pueden expresar como el producto de
 142 números primos, por ejemplo \code{4 = 2*2} o \code{21 = 3*7}, por lo tanto, si
 143 un número no es primo, debe tener al menos un factor primo que sea menor o igual
 144 que su raíz cuadrada, ya que de lo contrario, el producto de los factores
 145 resultaría en un numero mayor.
 146 
 147 De la propiedad anterior se deduce que se puede detener la iteración en la lista
 148 de números cuando se llega a la raíz cuadrado del numero máximo en la lista, ya
 149 que los números no primos por encima de éste tendrán algún factor menor que la
 150 raíz cuadrada del numero máximo, de este modo se reduce drásticamente el numero
 151 de iteraciones necesarias para descartar los números no primos de la lista.
 152 
 153 \section{Desarrollo}
 154 
 155 Para la implementación de la aplicación, como bien se mencionó en la
 156 introducción, se utilizó puramente el lenguaje C++, en particular en su versión
 157 estándar C98. Para organizar el proceso de compilación se utilizó la herramienta
 158 \code{make}.
 159 
 160 \subsection{Implementación en C++}
 161 
 162 El código fuente de la aplicación esta contenido en un solo archivo:
 163 \code{primos.cpp}.
 164 
 165 En la primera parte del archivo se definen dos constantes globales
 166 \code{OUTPUT\_FILE\_PATH}, que representa el nombre del archivo de salida, y
 167 \code{MAXIMO} que representa el largo de la lista de números a analizar.
 168 
 169 \begin{fullgrayverb}[\mbox{}]
 170 #define OUTPUT_FILE_PATH "primos.txt"
 171 const unsigned int MAXIMO = 100000000;
 172 \end{fullgrayverb}
 173 
 174 Luego se declara la función \code{vectorDiscardNonPrimes}, esta función asigna
 175 el valor \code{false} a los casilleros del vector, que recibe como argumento, en
 176 los cuales el índice es un numero no primo.
 177 
 178 \begin{fullgrayverb}[\mbox{}]
 179 // Se declara la función `vectorDiscardNonPrimes`
 180 void vectorDiscardNonPrimes(std::vector<bool> &v);
 181 \end{fullgrayverb}
 182 
 183 Se declara la función \code{vectorPrintToOpenFile} la cual recibe como
 184 argumento un vector, un archivo abierto y una referencia a una variable. Esta
 185 función imprime sobre el archivo las posiciones del vector en donde el
 186 contenido es \code{true}, la cantidad de números impresos los asigna a la
 187 variable \code{primesWritten}.
 188 
 189 \begin{fullgrayverb}[\mbox{}]
 190 // Se declara la función `vectorPrintToOpenFile`
 191 void vectorPrintToOpenFile(
 192          const std::vector<bool> v,
 193          std::ofstream &outputFile,
 194          unsigned int &primesWritten);
 195 \end{fullgrayverb}
 196 
 197 \subsubsection{Función \code{main}}
 198 
 199 La Criba de Eratóstenes opera sobre una lista de números naturales ordenada,
 200 para representarla se utiliza la librería estándar \code{vector}, de la cual
 201 se define un vector de tipo \code{bool} inicialmente con valores \code{true}
 202 indicando que son todos números primos.
 203 
 204 \begin{fullgrayverb}[\mbox{}]
 205 // Se define un vector de tipo `bool` de largo `MAXIMO`
 206 // con valores iniciales `true`
 207 std::vector<bool> numeros(MAXIMO, true);
 208 \end{fullgrayverb}
 209 
 210 \pagebreak
 211 Para la manipulación del archivo de salida, se utiliza la clase \code{ofstream}
 212 de la librería estándar \code{fstream}.
 213 
 214 \begin{fullgrayverb}[\mbox{}]
 215 // Se define el objeto `outputFile` para representar el archivo de salida
 216 std::ofstream outputFile;
 217 \end{fullgrayverb}
 218 
 219 Además se define la variable \code{primesFound} la cual luego contendrá la
 220 cantidad de números primos hallados.
 221 
 222 \begin{fullgrayverb}[\mbox{}]
 223 unsigned int primesFound;
 224 \end{fullgrayverb}
 225 
 226 Para descartar los números no primos del vector \code{numeros} se invoca a la
 227 función \linebreak\code{vectorDiscardNonPrimes}.
 228 
 229 \begin{fullgrayverb}[\mbox{}]
 230 vectorDiscardNonPrimes(numeros);
 231 \end{fullgrayverb}
 232 
 233 Luego de finalizada la función, se imprime en el archivo de salida los índices
 234 del vector correspondientes a los casilleros que permanecen con un valor
 235 \code{true}, ya que éstos representan los números primos.
 236 
 237 Utilizando los métodos \code{open} e \code{is\_open} del objeto
 238 \code{outputFile} se abre el archivo \code{primos.txt} y se comprueba si hubo
 239 algún error, en caso afirmativo, se informa al usuario haciendo uso de métodos
 240 de la librería estándar \code{iostream} y se termina la ejecución inmediatamente
 241 con un código de error \code{-1}.
 242 
 243 \begin{fullgrayverb}[\mbox{}]
 244 outputFile.open(OUTPUT_FILE_PATH);
 245 if (outputFile.is_open() == false) {
 246     std::cerr << "ERROR: No se pudo abrir `" OUTPUT_FILE_PATH "`\n";
 247     return -1;
 248 }
 249 \end{fullgrayverb}
 250 
 251 Para exportar los números primos del vector se invoca a la función
 252 \linebreak\code{vectorPrintToOpenFile}
 253 
 254 \begin{fullgrayverb}[\mbox{}]
 255 vectorPrintToOpenFile(numeros, outputFile, primesFound);
 256 \end{fullgrayverb}
 257 
 258 Luego de finalizada la impresión en el archivo, se cierra utilizando el método
 259 \code{close}.
 260 
 261 \begin{fullgrayverb}[\mbox{}]
 262 outputFile.close();
 263 \end{fullgrayverb}
 264 
 265 Finalmente se imprime un mensaje al usuario indicando que terminó la impresión,
 266 y se informa la cantidad de números primos hallados, esto se hace utilizando
 267 métodos de la librería estándar \code{iostream}.
 268 
 269 \begin{fullgrayverb}[\mbox{}]
 270 std::cout << "Se encontraron `" << primesFound << "` números primos\n";
 271 \end{fullgrayverb}
 272 
 273 Por ultimo se termina la ejecución con un código \code{0} indicando que el
 274 programa se ejecutó exitosamente.
 275 
 276 \begin{fullgrayverb}[\mbox{}]
 277 return 0;
 278 \end{fullgrayverb}
 279 
 280 \subsubsection{Función \code{vectorDiscardNonPrimes}}
 281 
 282 La función \code{vectorDiscardNonPrimes} recibe una referencia a un vector de
 283 tipo \code{bool} como argumento y asigna \code{false} a los casilleros en los
 284 cuales el índice del mismo es un número no primo. 
 285 
 286 La función comienza descartando los casos particulares, \code{0} y \code{1}, por
 287 lo que asigna \code{false} a los índices respectivos directamente.
 288 
 289 \begin{fullgrayverb}[\mbox{}]
 290 // 0 y 1 no son primos
 291 vector[0] = vector[1] = false;
 292 \end{fullgrayverb}
 293 
 294 Luego itera sobre el vector asignando \code{false} a todos los múltiplos de los
 295 números primos, hasta llegar a la raíz cuadrada del numero máximo de la lista, o
 296 lo que es equivalente, hasta que el indice al cuadrado sea menor al numero
 297 máximo.
 298 
 299 \begin{fullgrayverb}[\mbox{}]
 300 for (size_t i = 2; i*i < MAXIMO; ++i) {
 301     if(vector[i] == true) {
 302         for (size_t j = i; j <= (MAXIMO/i); ++j) {
 303             vector[i*j] = false;
 304         }
 305     }
 306 }
 307 \end{fullgrayverb}
 308 
 309 \subsubsection{Función \code{vectorPrintToOpenFile}}
 310 
 311 La función \code{vectorPrintToOpenFile} recibe como argumento un vector, una
 312 archivo abierto y una referencia a una variable. Luego de invocada la función
 313 imprime sobre el archivo los índices del vector en los cuales el contenido es
 314 \code{true}, a la variable que recibe le asigna la cantidad de índices impresos.
 315 
 316 Al comienzo de la función se asigna a la variable \code{primesWritten} cero.
 317 
 318 \begin{fullgrayverb}[\mbox{}]
 319 primesWritten = 0;
 320 \end{fullgrayverb}
 321 
 322 Para la impresión en el archivo, la función recorre el vector en búsqueda de
 323 aquellos casilleros que contengan \code{true}, en los casos afirmativos imprime
 324 el índice del vector al archivo e incrementa el contenido de la variable
 325 \code{primesWritten}, los casilleros que contienen \code{false} son ignorados.
 326 
 327 \begin{fullgrayverb}[\mbox{}]
 328 for (size_t i = 2; i < vector.size(); ++i) {
 329     if(vector[i] == true) {
 330         outputFile << i << std::endl;
 331         primesWritten++;
 332     }
 333 }
 334 \end{fullgrayverb}
 335 
 336 \subsection{Makefile}
 337 
 338 Para organizar el proceso de compilación de la aplicación se utiliza la
 339 herramienta \code{GNU make}, la cual lee un archivo de nombre exclusivo
 340 \code{Makefile} que posee su propia sintaxis.
 341 
 342 El archivo \code{Makefile} utilizado intenta ser reutilizable y de fácil
 343 modificación, es por esto que al comienzo del mismo se utilizan variables para
 344 almacenar las principales opciones que el usuario podría querer modificar, por
 345 ejemplo el nombre del compilador utilizado, el nombre final del archivo, entre
 346 otras.
 347 
 348 \begin{fullgrayverb}[\mbox{}]
 349 CC := g++
 350 CFLAGS := -Wall -Wshadow -pedantic -ansi -std=c++98 -O3
 351 SRCS := $(wildcard *.cpp)
 352 OBJS := $(SRCS:.cpp=.o)
 353 TARGET := primos
 354 \end{fullgrayverb}
 355 
 356 Para compilar el programa y enlazar cabeceras, en caso de que haya, se utilizan
 357 las siguientes reglas:
 358 
 359 \begin{fullgrayverb}[\mbox{}]
 360 $(TARGET): $(OBJS)
 361     $(CC) $(CFLAGS) -o $@ $^
 362 
 363 %.o: %.cpp
 364     $(CC) $(CFLAGS) -c $< -o $@
 365 \end{fullgrayverb}
 366 
 367 La regla que contiene \code{\$(TARGET)} se expande al nombre final de la
 368 aplicación, luego de los dos puntos se indican las dependencias, las cuales se
 369 expanden a los archivos objeto (los que terminan en \code{.o})
 370 
 371 Para compilar los archivos objetos se utiliza la segunda regla, la cual
 372 corresponde a una \code{pattern rule}, una extensión exclusiva de \code{GNU}, 
 373 esta \code{pattern rule} compila todos los archivos terminados en \code{.cpp} a
 374 archivos objeto terminados en \code{.o}.
 375 
 376 \subsection{Optimizaciones del Compilador}
 377 
 378 La mayoría de los compiladores de C++ modernos proporcionan una opción para
 379 activar optimizaciones. Al utilizar estas optimizaciones el compilador
 380 intenta eliminar código redundante y optimizar mediante técnicas avanzadas el
 381 archivo binario final, de este modo se logra reducir el uso de memoria, el
 382 tiempo de ejecución y por consiguiente el consumo de energía, entre otras cosas.
 383 
 384 En el archivo \code{Makefile} de la aplicación por defecto se utiliza el máximo
 385 de optimizaciones posibles \code{-O3}, de ésta manera se obtiene un archivo
 386 binario superior en rendimiento que el que se obtendría con las optimizaciones
 387 desactivadas.
 388 
 389 \subsubsection{Tiempo de Ejecución}
 390 
 391 En el siguiente bloque se muestra la salida del programa utilizando el máximo de
 392 optimizaciones posible del compilador \code{-O3}, se puede apreciar en la salida
 393 del programa el tiempo de ejecución del mismo.
 394 
 395 \begin{fullgrayverb}
 396 Se encontraron `5761455` números primos en `11.36` segundos
 397 \end{fullgrayverb}
 398 
 399 Desactivando las optimizaciones del compilador, compilando y ejecutando
 400 nuevamente la aplicación, se obtiene el siguiente mensaje en la consola:
 401 
 402 \begin{fullgrayverb}
 403 Se encontraron `5761455` números primos en `46.21` segundos
 404 \end{fullgrayverb}
 405 
 406 Comparando los dos resultados, se puede observar que al utilizar las
 407 optimizaciones de compilador el tiempo de ejecución se reduce en
 408 aproximadamente \code{75\%}.
 409 
 410 \section{Instalación}
 411 
 412 Para instalar la aplicación usted debe de disponer de una copia del código
 413 fuente, si no posee una, puede obtenerla ingresando al
 414 \href{https://github.com/mjkloeckner/CB100}{Repositorio de Github}, de allí
 415 podrá descargar una copia, o bien puede clonar el repositorio utilizando
 416 \code{git} con el siguiente comando:
 417 
 418 \begin{fullgrayverb}
 419 $ git clone https://github.com/mjkloeckner/CB100.git
 420 \end{fullgrayverb}$
 421 
 422 Tenga en cuenta que si clona el Repositorio, o lo descarga del repositorio de
 423 Github, tendrá que navegar al directorio \code{tps/1}
 424 
 425 \subsection{Sistemas basados en UNIX}
 426 
 427 Compruebe que este en el mismo directorio que el archivo \code{primos.cpp}. Luego,
 428 compile el código con el programa \code{make}
 429 
 430 \begin{fullgrayverb}
 431 $ make
 432 \end{fullgrayverb}$
 433 
 434 Luego puede ejecutar la aplicación de la siguiente manera:
 435 
 436 \begin{fullgrayverb}
 437 $ ./primos
 438 \end{fullgrayverb}$
 439 
 440 \pagebreak
 441 \subsection{Windows}
 442 
 443 En el caso de utilizar Windows como sistema operativo, usted puede configurar
 444 \href{https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux}{WSL}, el cual
 445 virtualiza un sistema basado en UNIX, para hacerlo puede seguir la
 446 \href{https://learn.microsoft.com/en-us/windows/wsl/install}{guia oficial de
 447 Microsoft}. Una vez configurado WSL siga los pasos para los sistemas basados en
 448 UNIX
 449 
 450 De manera análoga si usted dispone de un compilador de C++ puede compilar la
 451 aplicación directamente desde la consola, sin la necesidad de utilizar la
 452 herramienta \code{make}, para eso ejecute el siguiente comando:
 453 
 454 \begin{fullgrayverb}
 455 $ g++ -Wall -Wshadow -ansi -std=c++98 -O3 primos.cpp -o primos
 456 \end{fullgrayverb}$
 457 
 458 Tenga en cuenta la sintaxis de su compilador ya que puede variar, el comando
 459 anterior está previsto para \href{https://www.mingw-w64.org/}{MinGW-w64}
 460 
 461 Luego de compilado la aplicación usted la puede ejecutar de la siguiente manera 
 462 
 463 \begin{fullgrayverb}
 464 $ ./primos
 465 \end{fullgrayverb}$
 466 
 467 \section{Conclusión}
 468 
 469 Luego de finalizar el desarrollo de la aplicación, se puede concluir que la
 470 combinación de C++ con \code{GNU Make}, resulta muy versátil, y en este caso en
 471 un alto rendimiento.
 472 
 473 El lenguaje C++ es uno de los lenguajes de mayor rendimiento en la lista de
 474 lenguajes de programación de alto nivel actuales, junto con C, aunque a
 475 diferencia de éste, C++ ofrece una amplia variedad de librerías estándar, lo que
 476 mejora su robustez y permite al usuario abstraerse de ciertas implementaciones
 477 que resultan irrelevantes en ciertos casos, por ejemplo, en el desarrollo de
 478 esta aplicación, el objecto \code{vector} utilizado para representar la lista de
 479 números naturales. De no existir la librería estándar \code{vector} uno tendría
 480 que ensuciarse las manos e implementar un tipo de dato para representar los
 481 números naturales, ya que no alcanza con los tipos de datos primitivos ofrecidos
 482 por el lenguaje.
 483 
 484 Ademas de ahorrar tiempo al programador, las librerías estándar ofrecen mayor
 485 seguridad que si se tratase de un tipo de dato creado por el usuario, ya que las
 486 principales están respaldadas por organizaciones y grupos de estándares como
 487 ISO, IEC, entre otros.
 488 
 489 \pagebreak
 490 \section{Referencias}
 491 
 492 \begin{itemize}
 493     \item Deitel H., Deitel P. - C how to Program: With an Introduction to C++
 494         (2015)
 495     \item
 496         \href{https://github.com/mjkloeckner/CB100}{Repositorio de Github}
 497     \item
 498         \href{https://en.cppreference.com/w/}{C++ Reference}
 499     \item
 500         \href{https://cplusplus.com/reference/}{Standard C++ Library reference}
 501     \item
 502         \href{https://en.cppreference.com/w/cpp/container/vector_bool}{
 503             std::vector\textless bool\textgreater}
 504     \item
 505         \href{https://cplusplus.com/reference/fstream/ofstream/}{std::ofstream}
 506     \item
 507         \href{https://www.mingw-w64.org/}{MinGW-w64}
 508     \item
 509         \href{https://www.gnu.org/software/make/manual/html_node/index.html}{GNU
 510         Make Manual}
 511 \end{itemize}
 512 
 513 \end{document}
 514