CB100

Notas, resueltos y tps de la materia Algoritmos y Estructuras de Datos
Index Commits Files Refs README
guias/complejidad_algoritmica/guia_complejidad_algoritmica.md (9381B)
   1 # Guía complejidad algorítmica
   2 
   3 Algoritmos y Estructuras de Datos (CB100) - FIUBA  
   4 Martin Klöckner - [mklockner@fi.uba.ar](mailto:mklockner@fi.uba.ar)
   5 
   6 > 1. Un algoritmo divide un problema de tamaño $n$ en dos subproblemas de tamaño
   7 > $n/2$ con un costo constante en cada paso adicional, obtener la complejidad
   8 > sabiendo que el costo real $T(n)$ es: 
   9 > \vspace{-0.65em}
  10 > $$T(n) = 2T\left(\frac{n}{2}\right) + \mathcal{O}(1)$$
  11 
  12 Como la función de costo real $T(n)$ depende de la misma función $T(n)$ pero con
  13 una entrada menor $n/2$ se trata de un algoritmo recursivo o iterativo. Por el
  14 método de expansion, el orden se calcula siguiendo los procedimientos a
  15 continuación. De la expresión de $T(n)$:
  16 
  17 $$\begin{align}
  18 T(n) = 2T\left(\frac{n}{2}\right) + \mathcal{O}(1)
  19 \end{align}$$
  20 
  21 De la ecuación anterior se puede obtener $T\left(\frac{n}{2}\right)$
  22 reemplazando $n$ con $n/2$:
  23 
  24 $$\begin{align}
  25 T\left(\frac{n}{2}\right) = 2T\left(\frac{n}{4}\right) +
  26 \mathcal{O}(1)
  27 \end{align}$$
  28 
  29 Por lo tanto reemplazando $(2)$ en $(1)$ resulta:
  30 
  31 $$\begin{align}
  32 T(n) &= 2\cdotp \left[2T\left(\frac{n}{4}\right) + \mathcal{O}(1)\right] + \mathcal{O}(1) \nonumber\\
  33      &= 4T\left(\frac{n}{4}\right) + 2\mathcal{O}(1) + \mathcal{O}(1) \nonumber\\
  34      &= 4T\left(\frac{n}{4}\right) + 3\mathcal{O}(1)
  35 \end{align}$$
  36 
  37 Pero de $(1)$ también se puede obtener $T\left(\frac{n}{4}\right)$:
  38 
  39 $$\begin{align}
  40 T\left(\frac{n}{4}\right) = 2T\left(\frac{n}{8}\right) + \mathcal{O}(1)
  41 \end{align}$$
  42 
  43 Y reemplazando $(4)$ en la ecuación $(3)$:
  44 
  45 $$\begin{align}
  46 T(n) &= 4\cdotp \left[2T\left(\frac{n}{8}\right) + \mathcal{O}(1)\right]  + 2\mathcal{O}(1) + \mathcal{O}(1) \nonumber\\
  47      &= 8T\left(\frac{n}{8}\right) + 4\mathcal{O}(1) + 2\mathcal{O}(1) + \mathcal{O}(1)\nonumber\\
  48      &= 8T\left(\frac{n}{8}\right) + 7\mathcal{O}(1)
  49 \end{align}$$
  50 
  51 Se puede ver que luego de realizar $k$ veces el mismo procedimiento resulta:
  52 
  53 $$\begin{align}
  54 T(n) = 2^{k}\cdotp T\left(\frac{n}{2^k}\right) + (2^{k}-1)\cdotp \mathcal{O}(1)
  55 \end{align}$$
  56 
  57 Pero las iteraciones se terminan cuando el numero de entradas es $1$, entonces
  58 en $(6)$:
  59 
  60 $$\begin{align}
  61 \frac{n}{2^k} = 1 \Rightarrow n = 2^k \Rightarrow \boxed{log_{2}(n) = k}
  62 \end{align}$$
  63 
  64 Con $(7)$ en $(6)$ resulta
  65 
  66 $$T(n) = n\cdotp T(1) + (n - 1)\cdotp \mathcal{O}(1)$$
  67 
  68 De la expresión anterior, suponiendo $T(1) = \mathcal{O}(1)$ y aproximando, resulta
  69 
  70 $$\begin{align*}
  71 T(n) &= n\cdotp \mathcal{O}(1) + (n - 1)\cdotp \mathcal{O}(1) \\
  72      &= \mathcal{O}(n) + \mathcal{O}(n) \Rightarrow \boxed{T(n)     = \mathcal{O}(n)}
  73 \end{align*}$$
  74 
  75 Es decir la complejidad resulta $\mathcal{O}(n)$. El mismo problema se podría haber
  76 resuelto utilizando el teorema maestro para la reducción por division, el cual
  77 en primer lugar dice que dado un algoritmo con función de costo real $T(n)$ de
  78 la siguiente forma: 
  79 
  80 $$T(n) = 
  81 \begin{cases}
  82 \hspace{0.75em} c\cdotp n^{k}                                     & \text{si } 1\leq n < b \\
  83 \hspace{0.75em} a\cdotp T\left(\frac{n}{b}\right) + c\cdotp n^{k} & \text{si } n \geq b
  84 \end{cases}$$
  85 
  86 La complejidad algorítmica, o la solución de la ecuación de recurrencia $T(n)$
  87 resulta:
  88 
  89 $$T(n) = 
  90 \begin{cases}
  91 \hspace{0.75em} \mathcal{O}(n^k)                & \text{si } a < b^{k} \\
  92 \hspace{0.75em} \mathcal{O}(n^{k}\cdotp log(n)) & \text{si } a = b^{k} \\
  93 \hspace{0.75em} \mathcal{O}(n^{log_{b}(a)})     & \text{si } a > b^{k}
  94 \end{cases}$$
  95 
  96 En este caso se tiene que $T(n)$ es
  97 
  98 $$T(n) = 2T\left(\frac{n}{2}\right) + \mathcal{O}(1)$$
  99 
 100 Y es de la forma
 101 
 102 $$a\cdotp T\left(\frac{n}{b}\right) + c\cdotp n^{k}$$
 103 
 104 Con $a=2$, $b=2$, $c=1$ y $k=0$. Por lo tanto, la solución de la ecuación de
 105 recurrencia, o la complejidad sale directamente, siendo esta:
 106 
 107 $$T(n) = \mathcal{O}(n^{log_{b}(a)}) = \mathcal{O}(n^{log_{2}(2)}) \Rightarrow \boxed{T(n) = \mathcal{O}(n)}$$
 108 
 109 Resultando de igual forma que para la complejidad hallada mediante el método de
 110 expansión.
 111 
 112 > 2. Un algoritmo busca un valor en un array ordenado reduciendo el problema a
 113 >    la mitad en cada paso, con un costo constante para la comparación
 114 >    \vspace{-0.65em}
 115 >    $$T(n) = T\left(\frac{n}{2}\right) + \mathcal{O}(1)$$
 116 
 117 Para obtener la complejidad algorítmica, utilizo el método de expansión:
 118 
 119 $$\begin{align}
 120 T(n) = T\left(\frac{n}{2}\right) + \mathcal{O}(1)
 121 \end{align}$$
 122 
 123 Pero $T\left(\frac{n}{2}\right)$ resulta de reemplazar $n$ por $\frac{n}{2}$ en
 124 $T(n)$, entonces:
 125 
 126 $$\begin{align}
 127 T\left(\frac{n}{2}\right) = T\left(\frac{n}{4}\right) + \mathcal{O}(1)
 128 \end{align}$$
 129 
 130 Reemplazando $(9)$ en $(8)$ resulta
 131 
 132 $$\begin{align}
 133 T(n) &= T\left(\frac{n}{4}\right) + \mathcal{O}(1) + \mathcal{O}(1) \nonumber\\
 134      &= T\left(\frac{n}{4}\right) + 2\mathcal{O}(1)
 135 \end{align}$$
 136 
 137 Realizando una iteración mas reemplazando $n = \frac{n}{4}$ en $(8)$ resulta
 138 
 139 $$\begin{align}
 140 T\left(\frac{n}{4}\right) = T\left(\frac{n}{8}\right) + \mathcal{O}(1)
 141 \end{align}$$
 142 
 143 Y con $(11)$ en $(10)$:
 144 
 145 $$\begin{align}
 146 T(n) &= T\left(\frac{n}{8}\right) + \mathcal{O}(1) + \mathcal{O}(1) + \mathcal{O}(1) \nonumber\\
 147      &= T\left(\frac{n}{8}\right) + 3\mathcal{O}(1)
 148 \end{align}$$
 149 
 150 Se puede ver que luego de $k$ veces (o iteraciones) de repetir el procedimiento
 151 resulta
 152 
 153 $$\begin{align}
 154 T(n) &= T\left(\frac{n}{2^k}\right) + k\cdotp \mathcal{O}(1)
 155 \end{align}$$
 156 
 157 Pero las iteraciones se terminan cuando se llega al caso base, esto es, cuando
 158 $\frac{n}{2^k} = 1$ entonces:
 159 
 160 $$\begin{align}
 161 \frac{n}{2^k} &= 1 \Rightarrow 2^k = n \Rightarrow \boxed{k = log_{2}(n)}
 162 \end{align}$$
 163 
 164 Reemplazando $(14)$ en $(13)$
 165 
 166 $$\begin{align}
 167 T(n) &= T(1) + log_{2}(n)\cdotp \mathcal{O}(1)
 168 \end{align}$$
 169 
 170 Suponiendo $T(1) = \mathcal{O}(1)$ resulta que
 171 
 172 $$\begin{align}
 173 T(n) &= \mathcal{O}(1) + log_{2}(n)\cdotp \mathcal{O}(1)
 174 \end{align}$$
 175 
 176 Por lo tanto la complejidad algorítmica resulta:
 177 
 178 $$\boxed{T(n) = \mathcal{O}(log(n))}$$
 179 
 180 De manera análoga utilizando el teorema maestro, la expresión de $T(n)$ es de la
 181 forma
 182 
 183 $$a\cdotp T\left(\frac{n}{b}\right) + c\cdotp n^{k}$$
 184 
 185 Con $a = 1$, $b = 2$, $c=1$ y $k=0$, por lo tanto aplicando el teorema maestro
 186 para la reducción por division resulta que la complejidad es:
 187 
 188 $$T(n) = \mathcal{O}(n^{k}\cdotp log(n)) = \mathcal{O}(n^{0}\cdotp log(n))$$
 189 
 190 $$\Rightarrow\boxed{T(n) = \mathcal{O}(log(n))}$$
 191 
 192 > 3. Un algoritmo suma los elementos de una lista de $n$ números dividiendo la
 193 >    lista en dos partes de igual tamaño. 
 194 >    \vspace{-0.25em}
 195 >    $$T(n) = 2T\left(\frac{n}{2}\right) + \mathcal{O}(n)$$
 196 
 197 En este caso aplico el teorema maestro en caso de poder aplicarse primero y
 198 luego comparo con el método de expansión. Se puede ver que $T(n)$ es de la forma
 199 
 200 $$a\cdotp T\left(\frac{n}{b}\right) + c\cdotp n^{k}$$
 201 
 202 Con $a=2$, $b=2$, $c=1$ y $k=1$, por lo tanto se puede aplicar el teorema
 203 maestro y la complejidad algorítmica resulta;
 204 
 205 $$T(n) = \mathcal{O}(n^{k}\cdotp log(n)) = \mathcal{O}(n^{1}\cdotp log(n))$$
 206 
 207 $$\Rightarrow\boxed{T(n) = \mathcal{O}(n\cdotp log(n))}$$
 208 
 209 De manera análoga, utilizando el método de expansion:
 210 
 211 $$\begin{align}
 212 T(n) = 2T\left(\frac{n}{2}\right) + \mathcal{O}(n)
 213 \end{align}$$
 214 
 215 Pero $T\left(\frac{n}{2}\right)$ resulta de reemplazar $n =
 216 \frac{n}{2}$ en $(17)$, de lo cual resulta
 217 
 218 $$\begin{align}
 219 T\left(\frac{n}{2}\right) = 2T\left(\frac{n}{4}\right) + \mathcal{O}\left(\frac{n}{2}\right)
 220 \end{align}$$
 221 
 222 Entonces de $(18)$ en $(17)$
 223 
 224 $$\begin{align}
 225 T(n) &= 2\cdotp \left[2 T\left(\frac{n}{4}\right) + \mathcal{O}\left(\frac{n}{2}\right)\right] + \mathcal{O}(n) \nonumber \\
 226      &= 4\cdotp T\left(\frac{n}{4}\right) + 2\cdotp \mathcal{O}\left(\frac{n}{2}\right) + \mathcal{O}(n)
 227 \end{align}$$
 228 
 229 Realizando una iteración más $T\left(\frac{n}{4}\right)$ resulta de reemplazar $n =
 230 \frac{n}{4}$ en $(17)$, de lo cual resulta
 231 
 232 $$\begin{align}
 233 T\left(\frac{n}{4}\right) = 2T\left(\frac{n}{8}\right) + \mathcal{O}\left(\frac{n}{4}\right)
 234 \end{align}$$
 235 
 236 Y de $(20)$ en $(19)$
 237 
 238 $$\begin{align}
 239 T(n) &= 4\cdotp \left[ 2T\left(\frac{n}{8}\right) +
 240             \mathcal{O}\left(\frac{n}{4}\right) \right] + 2\cdotp
 241             \mathcal{O}\left(\frac{n}{2}\right) + \mathcal{O}(n) \nonumber \\
 242      &= 8\cdotp T\left(\frac{n}{8}\right) +
 243             4\cdotp \mathcal{O}\left(\frac{n}{4}\right) +
 244             2\cdotp \mathcal{O}\left(\frac{n}{2}\right) + \mathcal{O}(n)
 245 \end{align}$$
 246 
 247 Es decir, luego de $k$ iteraciones se llega a:
 248 
 249 $$\begin{align}
 250 T(n) &= 2^{k}\cdotp T\left(\frac{n}{2^{k}}\right) +
 251             \sum^{k-1}_{i=0} 2^{i} \cdotp \mathcal{O}\left(\frac{n}{2^{i}}\right) \nonumber \\
 252      &= 2^{k}\cdotp T\left(\frac{n}{2^{k}}\right) +
 253             \sum^{k-1}_{i=0} \mathcal{O}(n)
 254 \end{align}$$
 255 
 256 Pero cuando se llega al caso base se cumple:
 257 
 258 $$\begin{align}
 259 \frac{n}{2^{k}} = 1 \Rightarrow 2^{k} = n \Rightarrow\boxed{k = log_{2}(n)}
 260 \end{align}$$
 261 
 262 De $(23)$ en $(22)$:
 263 
 264 $$\begin{align*}
 265 T(n) &= n\cdotp T(1) + \sum^{log_{2}(n-1)}_{i=0} \mathcal{O}(n) \\
 266      &= n\cdotp T(1) + log_{2}(n-1) \cdotp \mathcal{O}(n) \\
 267      &= \mathcal{O}(n) + \mathcal{O}(n\cdotp log(n))
 268 \end{align*}$$
 269 
 270 El termino dominante de la expresión anterior es $n\cdotp log(n)$ entonces:
 271 
 272 $$\Rightarrow\boxed{T(n) = \mathcal{O}(n\cdotp log(n))}$$
 273 
 274 La expresión anterior coincide con la complejidad algorítmica hallada mediante
 275 el método de aplicar el teorema maestro, pero se es evidente que el método de
 276 expansion en algunos casos se puede volver mucho mas tedioso en comparación a
 277 aplicar el teorema.