2. Índice
Funciones de coste de un algoritmo
Caracterización asintótica del coste
Notación O
Reglas de caracterización
Consecuencias prácticas
Coste y principio de invariancia
Ejemplos de coste asintótico
2 / 25
3. Funciones de coste de un algoritmo
Búsqueda binaria – coste en tiempo
//Pre: n ≤ #v ∧ ∃α ∈ [0, n − 1].(v[α] = dato)
// ∧ ∀α ∈ [0, n − 2].(v[α] ≤ v[α + 1])
//Post: v[buscar(v, n, dato)] = dato
int buscar(const int v[], const int n, const int dato) {
int izdo = 0, dcho = n-1; // a
while (izdo != dcho) { // b
int medio = (izdo + dcho) / 2; // c
if (dato <= v[medio]) // d
dcho = medio; // e
else
izdo = medio + 1; // f
}
return izdo; // g
}
tbuscar(v,n,dato)(n) = (tc + td + tf + tb) · log2n + tinvoc + ta + tb + tg
3 / 25
4. Función de coste (en tiempo)
Búsqueda binaria
tbuscar(v,n,dato)(n) = (tc + td + tf + tb) · log2n + tinvoc + ta + tb + tg
El coste depende:
I de la velocidad a la que ejecuta instrucciones el
computador
I del número de datos n del vector v
Agrupando constantes:
k1 = tc + td + tf + tb, k2 = tinvoc + ta + tb + tg
la función de coste del algoritmo es:
tbuscar(v,n,dato)(n) = k1 · log2 n + k2
4 / 25
5. Funciones de coste de un algoritmo
Búsqueda binaria – coste en memoria
//Pre: n ≤ #v ∧ ∃α ∈ [0, n − 1].(v[α] = dato)
// ∧ ∀α ∈ [0, n − 2].(v[α] ≤ v[α + 1])
//Post: v[buscar(v, n, dato)] = dato
int buscar(const int v[], const int n, const int dato) {
int izdo = 0, dcho = n-1;
while (izdo != dcho) {
int medio = (izdo + dcho) / 2;
if (dato <= v[medio])
dcho = medio;
else
izdo = medio + 1;
}
return izdo;
}
membuscar(v,n,dato)(n) = meminvoc + memref + 6 · memint
5 / 25
6. Función de coste (en memoria)
Búsqueda binaria
membuscar(v,n,dato)(n) = meminvoc + memref + 6 · memint
El coste depende:
I del espacio en memoria requerido por cada
elemento en el computador
Agrupando constantes:
k = meminvoc + memref + 6 · memint
la función de coste del algoritmo es:
membuscar(v,n,dato)(n) = k
6 / 25
7. Funciones de coste de un algoritmo
Búsqueda secuencial
//Pre: n ≤ #v ∧ ∃α ∈ [0, n − 1].(v[α] = dato)
//Post: v[buscar(v,n,dato)]=dato
int buscar(const int v[], const int n, const int dato) {
int indice = 0; // a
while (v[indice] != dato) { // b
indice = indice + 1; // c
}
return indice; // d
}
// Coste en tiempo (hay que desglosarlo)
tbuscar(v,n,dato)(n) = tinvoc + ta + twhile + td
// Coste en memoria
membuscar(v,n,dato)(n) = meminvoc + memref + 4 · memint
7 / 25
8. Funciones de coste de un algoritmo
Búsqueda secuencial – caso mejor
//Pre: n ≤ #v ∧ ∃α ∈ [0, n − 1].(v[α] = dato)
//Post: v[buscar(v, n, dato)] = dato
int buscar(const int v[], const int n, const int dato) {
int indice = 0; // a
while (v[indice] != dato) { // b
indice = indice + 1; // c
}
return indice; // d
}
// Coste en tiempo (caso mejor)
tbuscar(v,n,dato)(n) = tinvoc + ta + tb + td
// Coste en memoria
membuscar(v,n,dato)(n) = meminvoc + memref + 4 · memint
8 / 25
9. Funciones de coste
Búsqueda secuencial – caso mejor
// Coste en tiempo (caso mejor)
tbuscar(v,n,dato)(n) = tinvoc + ta + tb + td
// Coste en memoria
membuscar(v,n,dato)(n) = meminvoc + memref + 4 · memint
Agrupando constantes:
k3 = tinvoc + ta + tb + td, k0
= meminvoc + memref + 4 · memint
las funciones de coste del algoritmo son constantes:
tbuscar(v,n,dato)(n) = k3 ← en tiempo
membuscar(v,n,dato)(n) = k0
← en memoria
9 / 25
10. Funciones de coste de un algoritmo
Búsqueda secuencial – caso peor
//Pre: n ≤ #v ∧ ∃α ∈ [0, n − 1].(v[α] = dato)
//Post: v[buscar(v, n, dato)] = dato
int buscar(const int v[], const int n, const int dato) {
int indice = 0; // a
while (v[indice] != dato) { // b
indice = indice + 1; // c
}
return indice; // d
}
// Coste en tiempo (caso peor)
tbuscar(v,n,dato)(n) = (tc + tb) · n + tinvoc + ta − tc + td
// Coste en memoria
membuscar(v,n,dato)(n) = meminvoc + memref + 4 · memint
10 / 25
11. Funciones de coste
Búsqueda secuencial – caso peor
// Coste en tiempo (caso peor)
tbuscar(v,n,dato)(n) = (tc + tb) · n + tinvoc + ta − tc + td
// Coste en memoria
membuscar(v,n,dato)(n) = meminvoc + memref + 4 · memint
Agrupando constantes:
k4 = tc + tb, k5 = tinvoc + ta − tc + td,
k0
= meminvoc + memref + 4 · memint
las funciones de coste del algoritmo son:
tbuscar(v,n,dato)(n) = k4 · n + k5 ← en tiempo
membuscar(v,n,dato)(n) = k0
← en memoria
11 / 25
12. Caracterización asintótica del coste
Definición
Notación O
Sea f una función f : N → R+
0 . El conjunto de
funciones del orden de f(n) se define de la forma:
O(f(n)) =
g : N → R+
0 | ∃k ∈ R+
, n0 ∈ N,
∀n ∈ N.n ≥ n0 → g(n) ≤ k · f(n)
Es decir, es el conjunto de funciones g acotables
superior y asintóticamente (para n → ∞) por
k · f(n), siendo k una constante (real) positiva.
12 / 25
13. Caracterización asintótica del coste
Ejemplos
f(n) O(f(n))
1 {1.0, 100.0, 135199, 0, . . . }
n {5.0,
√
n, 2n + 10, log2n, n − log2n, . . . }
n2
{1.5,
√
n, 25n, n2
+ 125.0, 15log2n, . . . }
Denotamos estos conjuntos seleccionando, para
cada uno, la función del conjunto más sencilla como
representante: O(1), O(n), O(n2
).
13 / 25
14. Complejidad de un algoritmo
Tabla de resumen con las funciones más sencillas
O(f(n)) Tipo de crecimiento Complejidad
O(1) constante constante
O(log n) logarítmico logarítmica
O(n) lineal lineal
O(n log n) n log n n log n
O(na
) polinómico polinómica (a ≥ 2)
O(n2
) cuadrático cuadrática
O(an
) exponencial exponencial (a 1)
O(n!) factorial factorial
O(1) ⊂ O(log n) ⊂ O(n) ⊂ O(nlog n) ⊂ O(n2
) ⊂ · · · ⊂ O(an
) ⊂ O(n!)
14 / 25
16. Reglas de caracterización
I Constantes multiplicativas:
O(k · f(n)) = O(f(n)), k ∈ R+
I Suma de funciones
O(f1(n) + · · · + fj(n)) = O(Máx{f1(n), . . . , fj(n)}), j ∈ N
I Sustitución de funciones
O(f(n)) = O(f0
(n)) → O(f(n) + g(n)) = O(f0
(n) + g(n))
O(f(n)) = O(f0
(n)) → O(f(n) · g(n)) = O(f0
(n) · g(n))
16 / 25
17. Consecuencias prácticas
Algoritmo buscar(v,n,dato) – búsqueda secuencial
Función de coste (tiempo, caso peor):
tbuscar(v,n,dato)(n) = k4 · n + k5, k4, k5 ∈ R+
Caracterización asintótica:
O(tbuscar(v,n,dato)(n)) = O(n)
Aproximación asintótica:
tbuscar(v,n,dato)(n) ≈ k4 · n, k4 ∈ R+
¿Cómo calcular k4?
De forma experimental!
17 / 25
18. Consecuencias prácticas
Estimación de la constante k4
I Sea X un valor suficientemente grande de n
I Ejecutamos, en nuestro ordenador, el algoritmo
buscar(v,n,dato) M veces con n = X
I En cada experimento i = 1, . . . , M, medimos el
tiempo de ejecución ti
I Calculamos el tiempo medio: t̄ = 1
M
PM
i=1 ti
I Como tbuscar(v,n,dato)(X) = t̄ ≈ k4 · X:
k4 ≈
t̄
X
I La función de coste aproximada es:
tbuscar(v,n,dato)(n) ≈ t̄
X · n
18 / 25
19. Coste y principio de invariancia
Sea tA(n) la función de coste de un algoritmo A
que verifica:
O(tA(n)) = O(f(n)) → limn→∞tA(n) = k · f(n)
La constante k depende de la velocidad de la
máquina donde se ejecuta A.
// en Computador 1:
limn→∞tA(n) = k · f(n)
// en Computador 2, doble de rápido:
limn→∞tA(n) = (k/2) · f(n)
// en Computador 3, el décuplo de rápido:
limn→∞tA(n) = (k/10) · f(n)
19 / 25
20. Coste y principio de invariancia
Principio de invariancia: la función f(n) que
caracteriza asintóticamente el coste no varía, solo
varía la constante multiplicativa k, que depende del
entorno de ejecución.
20 / 25
21. Ejemplos de coste asintótico
I factorial(n) – algoritmo iterativo
I sumaPares(v,n) – recorrido de un vector
I buscar(v,n,dato) – búsqueda binaria
I buscar(v,n,dato) – búsqueda secuencial
21 / 25
22. Coste asintótico de factorial()
//Pre: 0 ≤ n
//Post: factorial(n) =
Qn
α=1 .α
int factorial(const int n) {
int indice = 0, resultado = 1; // a
while (indice != n) { // b
indice = indice + 1; // c
resultado = indice * resultado; // d
}
return resultado; // e
}
// funciones de coste
mem(n) = meminvoc + 4 · memint
t(n) = (tc + td + tb) · n + tinvoc + ta + tb + te
// costes asintóticos
O(mem(n)) = O(1)
O(t(n)) = O(n)
22 / 25
23. Coste asintótico de sumaPares()
//Pre: 0 ≤ n ≤ #v
//Post: sumarPares(v,n) devuelve la suma de todos los datos
// con valor par almacenados en v[0,n-1]
int sumarPares(const int v[], const int n) {
int suma = 0; // a
for (int i = 0; i n; i++) { // b
if (v[i] % 2 == 0) { // c
suma = suma + v[i]; } } // d
return suma; } // e
// funciones de coste
mem(n) = meminvoc + memref + 4 · memint
t(n) = (tc + td + tb) · n + tinvoc + ta + tb + te
// costes asintóticos
O(mem(n)) = O(1)
O(t(n)) = O(n)
23 / 25
24. Coste asintótico de buscar(v,n,dato)
Búsqueda binaria
//Pre: n ≤ #v ∧ ∃α ∈ [0, n − 1].(v[α] = dato)
// ∧ ∀α ∈ [0, n − 2].(v[α] ≤ v[α + 1])
//Post: v[buscar(v,n,dato)]=dato
int buscar(const int v[], const int n, const int dato) {
int izdo = 0, dcho = n-1; // a
while (izdo != dcho) { // b
int medio = (izdo + dcho) / 2; // c
if (dato = v[medio]) // d
dcho = medio; // e
else
izdo = medio + 1; // f
}
return izdo; } // g
// funciones de coste
mem(n) = meminvoc + memref + 6 · memint
t(n) = (tc + td + tf + tb) · log2n + tinvoc + ta + tb + tg
// costes asintóticos
O(mem(n)) = O(1), O(t(n)) = O(log2 n)
24 / 25
25. Coste asintótico de buscar(v,n,dato)
Búsqueda secuencial
//Pre: n ≤ #v ∧ ∃α ∈ [0, n − 1].(v[α] = dato)
//Post: v[buscar(v,n,dato)]=dato
int buscar(const int v[], const int n, const int dato) {
int indice = 0; // a
while (v[indice] != dato) { // b
indice = indice + 1; // c
}
return indice; } // d
// funciones de coste
mem(n) = meminvoc + memref + 4 · memint
t(n) = tinvoc + ta + tb + td // caso mejor
t(n) = (tc + tb) · n + tinvoc + ta − tc + td // caso peor
// costes asintóticos
O(mem(n)) = O(1)
O(t(n)) = O(n)
25 / 25