commit 2b5b44f49cf2cc66e2658641c03adacc91d6372f
parent df63ed1f5a884b2e9545145f9705e66892cddfa6
Author: Martin Kloeckner <mjkloeckner@gmail.com>
Date: Wed, 23 Jul 2025 00:42:51 -0300
updated `notas/complejidad_algoritmica/*`
generated PDF with custom `style.tex` template
Diffstat:
5 files changed, 534 insertions(+), 58 deletions(-)
diff --git a/notas/complejidad_algoritmica/complejidad_algoritmica.md b/notas/complejidad_algoritmica/complejidad_algoritmica.md
@@ -1,13 +1,13 @@
-# Complejidad Algorítmica
+# Complejidad algorítmica
Algoritmos y Estructuras de Datos (CB100) - FIUBA
-Martin Klöckner - [mklockner@fi.uba.ar](mailto:mklockner@fi.uba.ar)
+Martin Klöckner - [mklockner@fi.uba.ar](mailto:mklockner@fi.uba.ar)
-Se dice que un algorítmico es mas eficiente que otro si consume menos recursos.
-La eficiencia se puede medir en términos espaciales (cantidad de memoria
-estática y dinámica que utiliza al ejecutarse) o en términos temporales (el
-tiempo que tarda en ejecutarse) en general se busca una relación de compromiso
-que comprende ambos factores.
+Se dice que un algorítmico es mas eficiente que otro si consume menos recursos,
+o se ejecuta mas rápido. La eficiencia se puede medir en términos espaciales
+(cantidad de memoria estática y dinámica que utiliza al ejecutarse) o en
+términos temporales (el tiempo que tarda en ejecutarse) en general se busca una
+relación de compromiso que comprende ambos factores.
Cuando se hace un análisis de la complejidad temporal de un algoritmo, se hace
referencia al tamaño de entrada del problema, o tamaño del problema, este tamaño
@@ -18,18 +18,19 @@ se quiere calcular el factorial, ya que cuanto mayor sea este número mayor ser
el tiempo de ejecución del algoritmo, otro ejemplo es el caso de una búsqueda
binaria en la cual el tamaño del problema será el tamaño del vector a ordenar.
-Se denota entonces la complejidad algorítmica para una operación de `n` entradas
-como `T(n)`, esta complejidad algorítmica mide el número de operaciones
-elementales, y si bien al analizar un algoritmo existe un mejor caso, un caso
-promedio y un peor caso, en la practica se suele definir a `T(n)` en términos
-del peor caso, ya que determina cual sería el número de operaciones elementales
-requeridas con la peor entrada posible. Por ejemplo en el siguiente caso, la
-cantidad de ciclos ejecutados depende directamente de la posición del dato en el
-vector, en el mejor caso es `1` y es cuando el dato está en el primer elemento,
-el caso promedio, es un promedio ponderado entre las probabilidades de todas las
-posibles entradas, en este caso resulta `n/2`, el peor caso es cuando está al
-final, y en ese caso la cantidad de operaciones elementales requeridas es `n`,
-por esto ultimo la complejidad algorítmica `T(n)` resulta `n`.
+Se denota entonces el coste real para una operación de $n$ entradas como $T(n)$,
+esta coste mide el número de operaciones elementales requeridas para ejecutar el
+algoritmo que se describe, y si bien al analizar un algoritmo existe un mejor
+caso, un caso promedio y un peor caso, en la practica se suele definir a $T(n)$
+en términos del peor caso, ya que determina cual sería el número de operaciones
+elementales requeridas con la peor entrada posible. Por ejemplo en el siguiente
+caso, la cantidad de ciclos ejecutados depende directamente de la posición del
+dato en el vector, en el mejor caso es $1$ y es cuando el dato está en el primer
+elemento, el caso promedio, es un promedio ponderado entre las probabilidades de
+todas las posibles entradas, en este caso resulta $n/2$, el peor caso es cuando
+está al final, y en ese caso la cantidad de operaciones elementales requeridas
+es $n$, por esto ultimo la complejidad algorítmica $T(n)$ resulta $n$,
+denotándose $T(n) = n$.
```c++
int get_pos(int* vec, int len, int data) {
@@ -47,9 +48,10 @@ int get_pos(int* vec, int len, int data) {
Las operaciones elementales son aquellas operaciones básicas de bajo nivel que
un algoritmo ejecuta y que tienen un costo constante (es decir, toman el mismo
tiempo, independientemente del tamaño de la entrada). Se considera operaciones
-elementales a las operaciones aritméticas básicas (`+`, `-`, `*`, etc), comparaciones lógicas (`==`,
-`!=`, `>`, etc), transferencias de control, asignaciones a variables de tipos
-básicos (`x = 5`, `a = b`, etc), acceso a memoria (`a[i]`, `x = b`, etc).
+elementales a las operaciones aritméticas básicas (`+`, `-`, `*`, etc),
+comparaciones lógicas (`==`, `!=`, `>`, etc), transferencias de control,
+asignaciones a variables de tipos básicos (`x = 5`, `a = b`, etc), acceso a
+memoria (`a[i]`, `x = b`, etc).
Las operaciones elementales sirven para independizar la definición de la
complejidad de un algoritmo de la maquina en la cual se ejecuta, ya que la
@@ -57,47 +59,45 @@ diferencia será una constante relacionada a la rapidez con la cual la maquina e
la cual se ejecuta el algoritmo puede realizar dichas operaciones elementales.
```c++
-int a; // 1 operacion elemental
-a = 5; // 1 operacion elemental
-a = a + 5; // 2 operaciones elementales (acceso a memoria y suma)
+int a; // 1 operación elemental
+a = 5; // 1 operación elemental
+a = a + 5; // 2 OE (acceso a memoria y suma)
```
-En el ejemplo anterior la complejidad algorítmica resulta `T(n) = 4` y es
+En el ejemplo anterior la complejidad algorítmica resulta $T(n) = 4$ y es
constante independiente de la entrada (no tiene entrada). En el ejemplo
-siguiente la entrada es `n`.
+siguiente la entrada es $n$.
```c++
-int n; // 1 operacion elemental
-std::cin >> n; // se considera 1 operacion elemental aunque no lo sea
+int n; // 1 operación elemental
+std::cin >> n; // se considera 1 OE
-// n > 0
while(n > 0) {
- std::cout << n; // tambien se considera 1 operacion elemental
- n--; // 2 operaciones elementales
+ std::cout << n; // se considera 1 OE
+ n--; // 2 OE
}
```
-El número total de operaciones elementales en el mejor de los casos es `2` y es
-cuando la entrada es `n <= 0`, en el peor de los caso se puede ver que el ciclo
-`while` se ejecuta `n` veces, resultando la complejidad algorítmica `T(n) = 2 +
-n*3`.
+El número total de operaciones elementales en el mejor de los casos es $2$ y es
+cuando la entrada es $n <= 0$, en el peor de los caso se puede ver que el ciclo
+`while` se ejecuta $n$ veces, resultando la complejidad algorítmica $T(n) = 2 +
+n*3$.
-En el siguiente ejemplo hay una condición y en una de las ramas un ciclo while,
-en la rama verdadera de la condición se debe ejecutar `1` operación elemental,
-mientras que la rama falsa se deben ejecutar `3*n` operaciones elementales, por
-lo tanto el coste total, considerando el pero caso resulta `T(n) = 4 + 3*n`.
+En el siguiente ejemplo hay una condición y en una de las ramas un ciclo
+`while`, ante estos casos se toma el peor caso, por lo tanto el coste total
+resulta $T(n) = 4 + 3*n$, ya que es el coste del ciclo.
```c++
-int n; // 1 operacion elemental
-std::cin >> n; // se considera 1 operacion elemental
+int n; // 1 operacion elemental
+std::cin >> n; // 1 OE
-if(n % 2 == 0) { // 2 operaciones elementales
- std::cout << n; // se considera 1 operacion
+if(n % 2 == 0) { // 2 OE
+ std::cout << n; // 1 OE
} else {
while(n > 0) {
- std::cout << n; // 1 oepracion elemental
- n--; // 2 operaciones elementales
+ std::cout << n; // 1 OE
+ n--; // 2 OE
}
}
```
@@ -165,32 +165,167 @@ Los ordenes mas comunes entre diferentes algoritmos se pueden ordenar en forma
creciente en cuanto a complejidad algorítmica, esto permite comparar la
eficiencia entre los algoritmos:
-$$ \mathcal{O}(1) \subset \mathcal{O}(log(n)) \subset \mathcal{O}(n) \subset
-\mathcal{O}(n*log(n)) \subset \mathcal{O}(n^2) \subset \mathcal{O}(n^3) \subset ...
-\subset \mathcal{O}(2^n) \subset \mathcal{O}(n!)$$
+\begin{align*}
+\mathcal{O}(1) &\subset \mathcal{O}(\log n) \subset \mathcal{O}(n) \subset \mathcal{O}(n \log n) \\
+&\subset \mathcal{O}(n^2) \subset \mathcal{O}(n^3) \subset \cdots \subset \mathcal{O}(2^n) \subset \mathcal{O}(n!)
+\end{align*}
-
+
#### Propiedades del orden
+A continuación se muestran propiedades de la cota superior $\mathcal{O}(f)$
+
1. $f$ es $\mathcal{O}(f)$ entonces $f$ esta acotada por su orden
-2. $\mathcal{O}(f)$ es $\mathcal{O}(g) \Rightarrow \mathcal{O}(f)$ esta incluido en
- $\mathcal{O}(g)$ y viceversa
+2. $\mathcal{O}(f)$ es $\mathcal{O}(g) \Rightarrow \mathcal{O}(f) \subset \mathcal{O}(g)$ y $\mathcal{O}(g) \subset \mathcal{O}(f)$
3. $\mathcal{O}(f) = \mathcal{O}(g)$ $\Leftrightarrow$ $f$ es $\mathcal{O}(g)$ y $g$ es
$\mathcal{O}(f)$
4. Si $f$ es $\mathcal{O}(g)$ y $g$ es $\mathcal{O}(h)$ $\Rightarrow$ $f$ es $\mathcal{O}(h)$
5. Si $f$ es $\mathcal{O}(g)$ y $f$ es $\mathcal{O}(h)$ $\Rightarrow$ $f$ es $\mathcal{O}(min(g, h))$
-6. [Regla de la suma] Si $f_{1}$ es $\mathcal{O}(g)$ y $f_{2}$ es $\mathcal{O}(h)$
+6. Si $f_{1}$ es $\mathcal{O}(g)$ y $f_{2}$ es $\mathcal{O}(h)$
$\Rightarrow$ $f_{1} + f_{2}$ es $\mathcal{O}(max(g, h))$
-7. [Regla del producto] Si $f_{1}$ es $\mathcal{O}(g)$ y $f_{2}$ es $\mathcal{O}(h)$
+7. Si $f_{1}$ es $\mathcal{O}(g)$ y $f_{2}$ es $\mathcal{O}(h)$
$\Rightarrow$ $f_{1} * f_{2}$ es $\mathcal{O}(g*h)$
-## Complejidad algorítmica de algoritmos recursivos
+## Algoritmos recursivos
-<!-- ### Método gráfico -->
-
-<!-- ### Método de recurrencia -->
+Dado una función de costo real con recurrencia (es decir que depende de la misma
+función para términos anteriors) por ejemplo $T(n) = T(n-1) + 1$ se puede hallar
+la complejidad mediante dos métodos gentales, el primero el método de expansion,
+en el cual se trata de un método iterativo evaluando como depende la función con
+las iteraciones, y en el segundo método de resolución, aplicando el teorema
+maestro, el cual es una formula general.
### Método de expansión
+El método de expansion se trata de ir hallando los términos recursivos mediante
+la formula del coste real $T(n)$ y reemplazando en si misma, es un proceso
+iterativo. Por ejemplo se tiene una expresión de $T(n)$:
+
+$$\begin{align}
+T(n) = 2T\left(\frac{n}{2}\right) + \mathcal{O}(1)
+\end{align}$$
+
+De la ecuación anterior se puede obtener $T\left(\frac{n}{2}\right)$
+reemplazando $n$ con $n/2$:
+
+$$\begin{align}
+T\left(\frac{n}{2}\right) = 2T\left(\frac{n}{4}\right) +
+\mathcal{O}(1)
+\end{align}$$
+
+Por lo tanto reemplazando $(2)$ en $(1)$ resulta:
+
+$$\begin{align}
+T(n) &= 2\cdotp \left[2T\left(\frac{n}{4}\right) + \mathcal{O}(1)\right] + \mathcal{O}(1) \nonumber\\
+ &= 4T\left(\frac{n}{4}\right) + 2\mathcal{O}(1) + \mathcal{O}(1) \nonumber\\
+ &= 4T\left(\frac{n}{4}\right) + 3\mathcal{O}(1)
+\end{align}$$
+
+Pero de $(1)$ también se puede obtener $T\left(\frac{n}{4}\right)$:
+
+$$\begin{align}
+T\left(\frac{n}{4}\right) = 2T\left(\frac{n}{8}\right) + \mathcal{O}(1)
+\end{align}$$
+
+Y reemplazando $(4)$ en la ecuación $(3)$:
+
+$$\begin{align}
+T(n) &= 4\cdotp \left[2T\left(\frac{n}{8}\right) + \mathcal{O}(1)\right] + 2\mathcal{O}(1) + \mathcal{O}(1) \nonumber\\
+ &= 8T\left(\frac{n}{8}\right) + 4\mathcal{O}(1) + 2\mathcal{O}(1) + \mathcal{O}(1)\nonumber\\
+ &= 8T\left(\frac{n}{8}\right) + 7\mathcal{O}(1)
+\end{align}$$
+
+Se puede ver que luego de realizar $k$ veces el mismo procedimiento resulta:
+
+$$\begin{align}
+T(n) = 2^{k}\cdotp T\left(\frac{n}{2^k}\right) + (2^{k}-1)\cdotp \mathcal{O}(1)
+\end{align}$$
+
+Pero las iteraciones se terminan cuando el numero de entradas es $1$, entonces
+en $(6)$:
+
+$$\begin{align}
+\frac{n}{2^k} = 1 \Rightarrow n = 2^k \Rightarrow \boxed{log_{2}(n) = k}
+\end{align}$$
+
+Con $(7)$ en $(6)$ resulta
+
+$$T(n) = n\cdotp T(1) + (n - 1)\cdotp \mathcal{O}(1)$$
+
+De la expresión anterior, suponiendo $T(1) = \mathcal{O}(1)$ y aproximando, resulta
+
+$$\begin{align*}
+T(n) &= n\cdotp \mathcal{O}(1) + (n - 1)\cdotp \mathcal{O}(1) \\
+ &= \mathcal{O}(n) + \mathcal{O}(n) \Rightarrow \boxed{T(n) = \mathcal{O}(n)}
+\end{align*}$$
+
+Es decir la complejidad resulta $\mathcal{O}(n)$. El mismo problema se podría haber
+
### Teorema maestro
+
+El teorema maestro se trata de una solución general para hallar la complejidad
+dependiendo de como evolucionan los términos, en general se tienen dos forums,
+la forma lineal, o por sustracción y la forma por division, las diferencias o
+cuando aplicar cada una se muestran a continuación.
+
+#### Reducción por sustracción
+
+Dado una función de costo real $T(n)$ de la forma
+
+$$T(n) = a\cdotp T(n-b)+\mathcal{O}(n^{k})$$
+
+Entonces la complejidad algorítmica, o la solución de la ecuación de recurrencia
+$T(n)$ resulta:
+
+$$T(n) =
+\begin{cases}
+\hspace{0.75em} \mathcal{O}(n^{(n/b)}\cdotp n^{k}) & \text{si } a > 1 \\
+\hspace{0.75em} \mathcal{O}(n^{k} & \text{si } a = 1 \\
+\hspace{0.75em} \mathcal{O}(n^k) & \text{si } a < 1
+\end{cases}$$
+
+#### Reducción por division
+
+Dado una función de costo real $T(n)$ de la forma
+
+$$T(n) =
+\begin{cases}
+\hspace{0.75em} c\cdotp n^{k} & \text{si } 1 \leq n < b \\
+\hspace{0.75em} a\cdotp T\left(\frac{n}{b}\right) + c\cdotp n^{k} & \text{si } n \geq b
+\end{cases}$$
+
+Entonces la complejidad algorítmica, o la solución de la ecuación de recurrencia
+$T(n)$ resulta:
+
+$$T(n) =
+\begin{cases}
+\hspace{0.75em} \mathcal{O}(n^k) & \text{si } a < b^{k} \\
+\hspace{0.75em} \mathcal{O}(n^{k}\cdotp log(n)) & \text{si } a = b^{k} \\
+\hspace{0.75em} \mathcal{O}(n^{log_{b}(a)}) & \text{si } a > b^{k}
+\end{cases}$$
+
+\vspace{-0.5em}
+
+## Complejidad amortizada
+
+Cuando se mide la complejidad de un algoritmo mediante el peor caso
+($\mathcal{O}$) puede haber casos en los que no sea representativo, es decir, el
+peor caso dista mucho de la media de ejecución del algoritmo, por lo que se usa
+la **complejidad amortizada** la cual es una especie de promedio por operación
+de un algoritmo en el peor de los casos a lo largo de una serie de operaciones.
+
+Por definición la complejidad amortizada es una técnica de análisis que se
+utiliza para determinar el tiempo promedio por operación de un algoritmo en el
+peor de los casos a lo largo de una secuencia de operaciones. En lugar de
+analizar el tiempo proporciona una estimación del costo total de una serie de
+operaciones, dividiendo este costo total por el numero de operaciones. Este tipo
+de análisis es especialmente útil cuando ciertas operaciones pueden ser muy
+costosas individualmente, pero esas operaciones costosas ocurren con poca
+frecuencia.
+
+Existen varios métodos para realizar un análisis amortizado, pero el mas común
+es calcular el costo total de una secuencia de operaciones y dividir por el
+numero total de operaciones:
+
+$$\boxed{\text{Costo amortizado } = \frac{T(n)}{n}}$$
diff --git a/notas/complejidad_algoritmica/complejidad_algoritmica.pdf b/notas/complejidad_algoritmica/complejidad_algoritmica.pdf
Binary files differ.
diff --git a/notas/complejidad_algoritmica/fix-math.lua b/notas/complejidad_algoritmica/fix-math.lua
@@ -0,0 +1,74 @@
+-- removes all `$$` surrounding \begin{align} and \begin{align*} contexts; also
+-- modifies the space after and before equations by adding \vspace{...}
+--
+-- \begin{align} .. \end{align} adds more vertical space before and after than
+-- $$ .. $$ (or \[ \] in latex notation)
+
+local eq_before_space = "-1em"
+local eq_after_space = "0em"
+local eq_align_before_space = "-0.75em"
+local eq_align_after_space = "-0.75em"
+
+local function vspace(amount)
+ return pandoc.RawBlock('latex', '\\vspace{' .. amount .. '}')
+end
+
+function walk_blocks(blocks)
+ local new_blocks = {}
+ for _, blk in ipairs(blocks) do
+ if blk.t == 'Para' then
+ -- Paras can contain inline math elements, so check and replace those as well
+ local new_inlines = {}
+ for _, inline in ipairs(blk.content) do
+ if inline.t == 'Math' and inline.mathtype == 'DisplayMath' then
+ table.insert(new_blocks, vspace(eq_before_space))
+ if inline.text:match('^\\begin{align%*?}') then
+ -- replace inline Math with RawBlock (convert Para to RawBlock)
+ table.insert(new_blocks, vspace(eq_align_before_space))
+ table.insert(new_blocks, pandoc.RawBlock('latex', inline.text))
+ table.insert(new_blocks, vspace(eq_align_after_space))
+ else
+ table.insert(new_blocks, pandoc.RawBlock('latex', '\\[' .. inline.text .. '\\]'))
+ end
+ table.insert(new_blocks, vspace(eq_after_space))
+ else
+ table.insert(new_inlines, inline)
+ end
+ end
+ -- Only add the Para if there is still content
+ if #new_inlines > 0 then
+ table.insert(new_blocks, pandoc.Para(new_inlines))
+ end
+
+ elseif blk.t == 'CodeBlock' or blk.t == 'RawBlock' then
+ -- Just keep them as is
+ table.insert(new_blocks, blk)
+
+ elseif blk.t == 'BlockQuote' or blk.t == 'Div' then
+ -- Recurse on nested blocks
+ table.insert(new_blocks, vspace("0.5em"))
+ blk.content = walk_blocks(blk.content)
+ table.insert(new_blocks, blk)
+ table.insert(new_blocks, vspace("0.5em"))
+
+ elseif blk.t == 'BulletList' or blk.t == 'OrderedList' then
+ -- process each item recursively
+ for i, item in ipairs(blk.content) do
+ blk.content[i] = walk_blocks(item)
+ end
+ table.insert(new_blocks, blk)
+
+ elseif blk.t == 'Item' then
+ blk.content = walk_blocks(blk.content)
+ table.insert(new_blocks, blk)
+ else
+ table.insert(new_blocks, blk)
+ end
+ end
+ return new_blocks
+end
+
+function Pandoc(doc)
+ doc.blocks = walk_blocks(doc.blocks)
+ return doc
+end
diff --git a/notas/complejidad_algoritmica/func_cmp.png b/notas/complejidad_algoritmica/func_cmp.png
Binary files differ.
diff --git a/notas/complejidad_algoritmica/style.tex b/notas/complejidad_algoritmica/style.tex
@@ -0,0 +1,267 @@
+% \documentclass[14pt]{extarticle}
+
+% page setup
+% \usepackage[a4paper,
+% top=2.5cm,
+% bottom=2.5cm,
+% left=2.00cm,
+% right=2.00cm,
+% bmargin=2.50cm]{geometry}
+\usepackage[a4paper,
+ top=2.00cm,
+ left=1.75cm,
+ right=1.75cm,
+ bottom=2.00cm,
+ bmargin=2.00cm]{geometry}
+
+\setlength{\columnsep}{16pt} % Default is usually 10pt
+
+\usepackage{titlesec}
+\usepackage{fontspec}
+\setmainfont{Helvetica}
+
+% right pointing hand
+\usepackage{utfsym}
+
+% make pictures caption font bold and small
+\usepackage[font={footnotesize,bf}]{caption}
+\usepackage{subcaption}
+
+% inline code (backticks in md)
+\linespread{1.10}
+\definecolor{bgcolor}{HTML}{e0e0e0}
+\let\oldtexttt\texttt
+
+% \renewcommand{\texttt}[1]{
+% \colorbox{bgcolor}{\oldtexttt{#1}}
+% }
+
+% change boldfont bold to extrabold
+% \setmainfont[
+% BoldFont={Inter-ExtraBold}
+% ]{Inter}
+
+% change regular font to light font
+% \setmainfont{Inter light}
+
+\newfontfamily\titlefont{Inter}[
+UprightFont = *-Regular,
+BoldFont = *-ExtraBold,
+Scale = 0.90
+]
+
+\newfontfamily\sectionsfont{Inter}[
+UprightFont = *-Regular,
+BoldFont = *-Bold,
+]
+
+\setmathfont[Scale=1.05]{Fira Math}
+
+\usepackage{xcolor}
+\definecolor{ugrey}{HTML}{333333}
+
+\titleformat{\section}
+{\color{ugrey}\titlefont\Large\bfseries}
+{\color{ugrey}}
+{0em}
+{}
+
+\titleformat{\subsection}
+{\color{ugrey}\sectionsfont\large\bfseries}
+{\color{ugrey}}
+{0em}
+{}
+
+\titleformat{\subsubsection}
+{\color{ugrey}\sectionsfont\bfseries}
+{\color{ugrey}}
+{0em}
+{}
+
+\titleformat{\paragraph}
+{\color{ugrey}\sectionsfont\bfseries}
+{\color{ugrey}\theparagraph}
+{0em}
+{}
+
+\titleformat{\subparagraph}
+{\color{ugrey}\normalfont\bfseries}
+{\color{ugrey}\theparagraph}
+{0em}
+{}
+
+% spacing: how to read {12pt plus 4pt minus 2pt}
+% 12pt is what we would like the spacing to be
+% plus 4pt means that TeX can stretch it by at most 4pt
+% minus 2pt means that TeX can shrink it by at most 2pt
+%
+% \titlespacing{command}{left spacing}{before spacing}{after spacing}[right]
+
+\titlespacing*{\section}
+{0pt}{1.5ex plus 0.25ex minus .2ex}{1.25ex plus .2ex}
+
+\titlespacing*{\subsection}
+{0pt}{1.5ex plus 0.25ex minus .2ex}{1.25ex plus .2ex}
+
+\titlespacing*{\subsubsection}
+{0pt}{1.25ex plus 0.25ex minus .2ex}{1.25ex plus .2ex}
+
+\titlespacing*{\paragraph}
+{0pt}{1.25ex plus 0.25ex minus .2ex}{1.0ex plus .2ex}
+
+\titlespacing*{\subparagraph}
+{0pt}{1.00ex plus 0.25ex minus .2ex}{1.0ex plus .2ex}
+
+% spacing between formulas and text
+% \usepackage[nodisplayskipstretch]{setspace}
+% \setstretch{1.20}
+
+\setlength{\abovedisplayskip}{0pt}
+\setlength{\belowdisplayskip}{0pt}
+
+\setlength{\abovedisplayshortskip}{0pt}
+\setlength{\belowdisplayshortskip}{0pt}
+
+\setlength{\belowdisplayshortskip}{\belowdisplayskip}
+
+\setlength{\baselineskip}{0pt}
+
+% \usepackage{setspace}
+% \setstretch{1.25}
+
+% \renewcommand{\figurename}{Fig.}
+
+\usepackage{caption}
+\captionsetup{font=normalsize, font=bf, labelfont=bf}
+\captionsetup[sub]{font=small,labelfont=md}
+
+\renewcommand\thesubfigure{\arabic{subfigure}}
+
+\renewcommand{\figurename}{Figura}
+\renewcommand{\tablename}{Tabla}
+
+% \renewcommand{\contentsname}{Índice}
+\renewcommand\contentsname{\vspace*{-45pt}}
+
+% TOC dots separation
+% \renewcommand{\cftdotsep}{10}
+
+% \setlength{\cftsecindent}{0pt}% Remove indent for \section
+% \setlength{\cftsubsecindent}{5pt}% Remove indent for \subsection
+% \setlength{\cftsubsubsecindent}{0pt}% Remove indent for \subsubsec
+
+\setcounter{tocdepth}{4}
+
+\usepackage{titling}
+\renewcommand{\maketitle}{
+ \begin{flushleft}
+ {\bfseries\Huge\thetitle}
+ \vspace{1mm}
+ \end{flushleft}
+ % \thispagestyle{empty}
+}
+
+% remove the page number from all the pages that the TOC occupies
+% \addtocontents{toc}{\protect\thispagestyle{empty}}
+
+% add page break after TOC set it to page number 1
+\let\oldtableofcontents\tableofcontents % remember the definition
+\renewcommand\tableofcontents{
+ \oldtableofcontents % use the standard toc
+ \thispagestyle{empty}
+ \pagebreak
+ \setcounter{page}{1}
+}
+
+% Set text color for all document
+% \color{ugrey}
+
+\usepackage[titles]{tocloft}
+\renewcommand{\cftdotsep}{1.5}
+\renewcommand{\cftsetpnumwidth}{1.5}
+\renewcommand{\cftsetrmarg}{1.5}
+
+\usepackage{float}
+\makeatletter
+\def\fps@figure{H}
+\makeatother
+
+% nicer chemical figures
+\usepackage{chemfig}
+
+% change style of quote, see also https://tex.stackexchange.com/a/436253/114857
+\usepackage[most]{tcolorbox}
+
+
+\definecolor{linequote}{RGB}{224,215,188}
+\definecolor{bordercolor}{RGB}{221,221,221}
+% \definecolor{backquote}{RGB}{249,245,233}
+\definecolor{backquote}{RGB}{245,245,245}
+
+% change left border: https://tex.stackexchange.com/a/475716/114857
+% change left margin: https://tex.stackexchange.com/a/457936/114857
+\newtcolorbox{myquote}[1][]{%
+ enhanced,
+ breakable,
+ size=minimal,
+ left=8pt,
+ top=8pt,
+ bottom=8pt,
+ right=8pt,
+ boxrule=1pt,
+ sharp corners=all,
+ colback=backquote,
+ colframe=black,
+ #1}
+
+% redefine quote environment to use the myquote environment, see
+% https://tex.stackexchange.com/a/337587/114857
+\renewenvironment{quote}{\begin{myquote}}{\end{myquote}}
+
+% better fractions
+\usepackage{nicefrac,xfrac}
+
+% surround footnotes number with square brackets and always use numbers (even
+% inside quoted text)
+% https://www.overleaf.com/learn/latex/Footnotes
+\renewcommand*{\thefootnote}{\ [\arabic{footnote}]\ }
+\renewcommand*{\thempfootnote}{\ [\arabic{mpfootnote}]\ }
+
+% space between text and footer
+\setlength\footskip{32pt}
+\setlength{\skip\footins}{12pt}
+
+% align first letter of all the lines in the footnotes
+\usepackage[bottomfloats,belowfloats,hang]{footmisc}
+\setlength{\footnotemargin}{1em}
+
+\newcommand{\unit}[2]{\nicefrac{#1}{#2}}
+
+\usepackage{enumitem}
+\usepackage{amsfonts}
+
+\setlist[itemize,1]{label=$\bullet$}
+\setlist[itemize,2]{label=$\textopenbullet$}
+
+\usepackage{tabularx}
+\usepackage{multirow} % Required for multirows
+\usepackage{colortbl}
+
+% Reduce space around displayed equations safely
+\makeatletter
+\setlength{\jot}{8pt} % valor por defecto es 3pt aproximadamente
+\makeatother
+
+\usepackage{enumitem}
+
+\setlist[enumerate]{
+ leftmargin=0pt, % no global indentation
+ labelindent=0pt, % label aligned with margin
+ itemindent=5pt, % no extra indent
+ labelwidth=0pt, % label takes no reserved space
+ align=left % make wrapped lines align with the left edge, not number
+}
+
+\usepackage[spanish]{babel}
+\binoppenalty=10000
+\relpenalty=10000