Ejemplos 2
Búsqueda binaria
ALGORÍTMICA Y COMPLEJIDAD
Grados en Ing. Informática y Matemáticas
Universidad de Cantabria
Camilo Palazuelos Calderón
Sea $A[0..\ell - 1]$ un array de elementos ordenados de menor a mayor. Definimos la función Buscar
, que busca en el subarray $A[i..j]$ la posición del elemento $x$, recibidos $A[0..\ell - 1]$, $i$, $j$ y $x$ como parámetros: si la encuentra, devuelve un entero no negativo $k$ tal que $A[k] = x$; si no, -1
.
def Buscar(A, i, j, x):
if i > j:
return -1
else:
k = (i + j) // 2
if A[k] == x:
return k
elif A[k] < x:
return Buscar(A, k + 1, j, x)
else:
return Buscar(A, i, k - 1, x)
Probamos la función con el array $A[0..5] = (-20, -19, 3, 10, 18, 22)$ y los elementos $x = 18$ e $y = 19$. Buscar(
$A[0..5]$,
$x$,
$0$,
$5$)
y Buscar(
$A[0..5]$,
$y$,
$0$,
$5$)
deberían devolver 4
y -1
, respectivamente.
A = [-20, -19, 3, 10, 18, 22]
x = 18
y = 19
print(f'Buscar({A}, {0}, {len(A) - 1}, {x}) = {Buscar(A, 0, len(A) - 1, x)}')
print(f'Buscar({A}, {0}, {len(A) - 1}, {y}) = { Buscar(A, 0, len(A) - 1, y)}')
Buscar([-20, -19, 3, 10, 18, 22], 0, 5, 18) = 4 Buscar([-20, -19, 3, 10, 18, 22], 0, 5, 19) = -1
Para evaluar el coste temporal empírico de Buscar
en el peor caso, creamos un array $B[0..m - 1] = (0, \ldots, 0)$ con $m$ suficientemente grande (por ejemplo, $m = 10^7$). Iterativamente, buscaremos el elemento $x = 1$ en las primeras $n$ posiciones de $B[0..m - 1]$ y añadiremos el coste temporal empírico de Buscar(
$B[0..m - 1]$,
$x$,
$0$,
$n - 1$)
a una lista $T$.
m = 10000000
B = [0 for _ in range(m)]
T = [None]
Modificamos Buscar
para añadir una pequeña latencia (por ejemplo, $5$ ms) en cada llamada recursiva.
import time
def Buscar2(A, i, j, x):
time.sleep(0.005)
if i > j:
return -1
else:
k = (i + j) // 2
if A[k] == x:
return k
elif A[k] < x:
return Buscar2(A, k + 1, j, x)
else:
return Buscar2(A, i, k - 1, x)
Evaluamos el coste temporal empírico de Buscar2
con arrays de longitud $n$ desde $1 \cdot 10^5$ hasta $100 \cdot 10^5$ (de $10^5$ en $10^5$).
for n in range(m // 100, m + 1, m // 100):
t = time.time()
Buscar2(B, 0, n - 1, 1)
T.append(time.time() - t)
#print(f'Iteración {n // (m // 100)} de 100')
Dibujamos una gráfica con el tiempo de ejecución $T(n)$ de Buscar2
en función de la longitud $n$ del array de entrada.
import matplotlib.pyplot as plt
import numpy as np
with plt.style.context('ggplot'):
plt.plot(np.array(T), label = '$T(n) \in \Theta(\log n)$')
plt.xticks(
[ x for x in range(0, 101, 20)],
[f'${x} \cdot 10^5$' for x in range(0, 101, 20)])
plt.xlabel( '$n$', fontsize = 12)
plt.ylabel('$T(n)$ en s', fontsize = 12)
plt.title('Complejidad temporal')
plt.legend()
plt.show()