Introducción al Análisis de Algoritmos

Estructura de
Datos
Andrea Rodrı́guez
Análisis de
Algoritmos
Notaciones
Introducción al Análisis de Algoritmos
Sistemas
Recurrentes
Dividir para
conquistar
M. Andrea Rodrı́guez-Tastets
Universidad de Concepción,Chile
www.inf.udec.cl\ ∼andrea
[email protected]
I Semestre - 2010
Estructura de
Datos
Andrea Rodrı́guez
Análisis de
Algoritmos
Objetivos de la Clase
Durante esta clase revisaremos análisis de algoritmos. Se espera
que al final de la clase los alumnos puedan:
I
Entender el concepto de tiempo de ejecución de un algoritmo.
I
Poder determinar una cota de tiempo de ejecución de
algoritmos simples.
I
Entender los sistemas recurrentes y aplicar análisis de
algoritmos a ellos.
Notaciones
Sistemas
Recurrentes
Dividir para
conquistar
Estructura de
Datos
Andrea Rodrı́guez
Definiciones
I
Analizar un algoritmo es predecir cuánto recurso requiere su
ejecución. Ocasionalmente, recursos de comunicación y
hardware son de ı́nteres particular, sin embargo, en la mayorı́a
de los casos, es el tiempo de ejecución el de mayor ı́nteres.
I
El tiempo de ejecución de un algoritmo en base a una
entrada en particular de datos es el número de operaciones
primitivas o pasos ejecutados.
I
En este curso nosotros trataremos con algoritmos eficientes
(polinomiales). Sin embargo, existen algunos problemas para
los cuales no existe un algoritmo eficiente conocido. Un
subconjunto de estos casos son los llamados problemas
NP-complete. Esta temática se ve en un curso de
complejidad de algoritmos.
Análisis de
Algoritmos
Notaciones
Sistemas
Recurrentes
Dividir para
conquistar
Estructura de
Datos
Andrea Rodrı́guez
Análisis de
Algoritmos
Notaciones
Análisis de Algoritmos
Tiempo ejecución
n
n2
n3
2n
3n
n!
n = 10
0,00001 seg.
0,0001 seg.
0,001 seg.
0,001 seg.
0,059 seg.
3,63 seg
Sistemas
Recurrentes
n = 20
0,00002 seg.
0,0004 seg.
0,008 seg.
1 seg.
58 min.
771 siglos
n = 30
0,00003 seg.
0,009 seg.
0,027 seg.
17,9 min.
6,5 años
8x1016 siglos
n = 40
0,00004 seg.
0.0016 seg.
0.064 seg.
12,7 dı́as
3.855 siglos
3x1032 siglos
Dividir para
conquistar
Modelo de Implementación
Estructura de
Datos
Andrea Rodrı́guez
I
I
Para realizar un análisis de algoritmos, se requiere de un
modelo de implementación en el cual se ejecutan nuestros
algoritmos y que es independiente del lenguaje de máquina.
Instrucciones son las comúnmente encontradas en
computadores, para las cuales asumimos un tiempo
constante.
I
El modelo random access machine (RAM) considera que la
ejecución de intrucciones de algoritmos es secuencial, una
tras otra, sin operaciones concurrentes. Cada operación
simple (+, −, =, if) toma un paso. Loops y llamadas a
subrutinas son operaciones no simples o complejas. Ellas
dependenden de la entrada de datos y del contenio de las
subrutinas. Cada acceso a memoria toma un paso.
I
En RAM no se modela la memoria jerárquica (virtual, cache,
etc..), lo cual puede ser significante para cierta clase de
problemas.
Análisis de
Algoritmos
Notaciones
Sistemas
Recurrentes
Dividir para
conquistar
Estructura de
Datos
Andrea Rodrı́guez
Análisis de
Algoritmos
Notaciones
Considere el siguiente programa de ordenamiento, donde tk
representa el número de veces la condición del if es satisfecha:
Sistemas
Recurrentes
Dividir para
conquistar
lı́nea
1
2
3
4
5
6
código
for i := 1 to n-1 do [
for j:= 1 to n-i do[
if A[j] > A[j+1] do [
temp:= A[j];
A[j] := A[j+1];
A[j+1] : = temp;] ] ]
costo constante
c1
c2
c3
c4
c5
c6
ejecuciones
n
Pn−1
(k + 1)
k=1
Pn−1
k
k=1
Pn−1
t
k=1 k
Pn−1
t
k=1 k
Pn−1
t
k=1 k
Estructura de
Datos
Andrea Rodrı́guez
Análisis de
Algoritmos
Peor, mejor y caso promedio
I La complejidad del peor caso de un algoritmo es una función definida
por el número maximo de pasos tomados para instancia n.
I La complejidad del mejor caso de un algoritmo es una función definida
por el número mı́nimo de pasos tomados para instancia n.
I La complejidad del caso promedio de un algoritmo es una función
definida pornúmero promedio de pasos tomados para instancia n.
Cada una de estas complejidades define una función númerica: tiempo versus
tamaño.
Notaciones
Sistemas
Recurrentes
Dividir para
conquistar
Estructura de
Datos
Andrea Rodrı́guez
Análisis de
Algoritmos
Para la mayorı́a de los algoritmos que veremos en este curso, nos
interesará ver el peor caso del análisis, es decir, el mayor tiempo
para cualquier entrada de largo n. Las razones son las siguientes:
I el peor caso es un lı́mite superior en tiempo de ejecución de cualquier
entrada. Ası́, se garantiza que el algoritmo nunca tomará más que eso;
I para algunos casos el peor caso es bastante común; y
I el caso promedio es muchas veces tan malo como el peor caso. Por
ejemplo, considere el ejemplo anterior. En promedio, uno podrı́a estimar
que if ocurre la mitad de las veces, osea tk = k/2. Esto resulta en que
la función T (n) sigue siendo cuadrática para otra constante dada.
Notaciones
Sistemas
Recurrentes
Dividir para
conquistar
Estructura de
Datos
Andrea Rodrı́guez
Análisis de
Algoritmos
Notaciones
Sistemas
Recurrentes
Análisis exacto!!!
Es muy difı́cil tener una función exacta del mejor, peor o caso promedio. Es
más fácil hablar de un lı́mite superior o inferior de la función. Notación
asintóticas son una forma más práctica de manejar funciones de complejidad.
Dividir para
conquistar
Estructura de
Datos
Andrea Rodrı́guez
Notación Θ
Análisis de
Algoritmos
Para una función dada g (n):
Notaciones
Θ(g (n))
=
{f (n) : existen las constantes positivas c1 , c2 y n0 tal que
0 ≤ c1 g (n) ≤ f (n) ≤ c2 g (n)para todo n ≥ n0 }
Sistemas
Recurrentes
Dividir para
conquistar
Estructura de
Datos
Andrea Rodrı́guez
Notación O
Análisis de
Algoritmos
Para una función dada g (n):
Notaciones
O(g (n))
=
{f (n) : existen las constantes positivas c y n0 tal que
0 ≤ f (n) ≤ cg (n)para todo n ≥ n0 }
cg(n)
f(n)
Sistemas
Recurrentes
Dividir para
conquistar
Estructura de
Datos
Andrea Rodrı́guez
Notación Ω
Análisis de
Algoritmos
Para una función dada g (n):
Notaciones
Ω(g (n))
=
{f (n) : existen las constantes positivas c y n0 tal que
0 ≤ cg (n) ≤ f (n)para todo n ≥ n0 }
f(n)
cg(n)
Sistemas
Recurrentes
Dividir para
conquistar
Estructura de
Datos
Andrea Rodrı́guez
Análisis de
Algoritmos
Notaciones
Ejemplos de O
2
Sistemas
Recurrentes
2
2
3n − 100n + 6 = O(n2) porque 3n > 3n − 100n + 6
3n2 − 100n + 6 = O(n3) porque ,01n3 > 3n2 − 100n + 6
3n2 − 100n + 66 6= O(n) porque c ∗ n < 3n2 cuando n > c
Se debe pensar en igualdad como en el conjunto de funciones
Dividir para
conquistar
Estructura de
Datos
Andrea Rodrı́guez
Análisis de
Algoritmos
Notaciones
Sistemas
Recurrentes
Ejemplos de ω
Dividir para
conquistar
3n2 − 100n + 6 = Ω(n2) porque 2 : 99n2 < 3n2 − 100n + 6
3n2 − 100n + 6 6= Ω(n3) porque 3n2 − 100n + 6 < n3
2
10
3n − 100n + 66 = Ω(n) porque 1010 n < 3n2 − 100 + 6
Estructura de
Datos
Andrea Rodrı́guez
Análisis de
Algoritmos
Notaciones
Ejemplos de θ
Sistemas
Recurrentes
Dividir para
conquistar
3n2 − 100n + 6 = Θ(n2) porque se cumple O, Ω
3n2 − 100n + 6 6= Θ(n3) porque se cumple sólo O
3n2 − 100n + 66 6= Θ(n) porque se cumple sólo Ω
Estructura de
Datos
Andrea Rodrı́guez
Análisis de
Algoritmos
Notaciones
Propiedades Notaciones
I Θ(g (n)) ⊆ O(g (n))
I Θ(g (n)) ⊆ Ω(g (n))
I Transitividad: (f (n) ∈ O(g (n)) ∧ g (n) ∈ O(h(n))) → f (n) ∈ O(h(n))
I Reflexividad: f (n) ∈ O(f (n))
I Simetrı́a Transpuesta: f (n) ∈ O(g (n)) iff g (n) ∈ Ω(f (n))
Sistemas
Recurrentes
Dividir para
conquistar
Estructura de
Datos
Andrea Rodrı́guez
Análisis de
Algoritmos
Notaciones
Suma y sustracción
Si tenemos que f (n) = O(n2 ) y g (n) = O(n2 ), entonces:
I ¿Qué sucede con f 0 (n) = f (n) + g (n)?
I ¿Qué sucede con f 00 (n) = f (n) − g (n)?
Sistemas
Recurrentes
Dividir para
conquistar
Estructura de
Datos
Andrea Rodrı́guez
Ejemplo
Dado el siguiente algoritmo, donde A[1:n] es un arreglo ordenado,
indique qué hace y derive su complejidad. Indique el caso peor de
la complejidad.
Análisis de
Algoritmos
Notaciones
Sistemas
Recurrentes
Dividir para
conquistar
Procedure UNKNOW (intA[1 : n],int n,int x,var int j)
l := 1; u := n;
while l < u do [
m := b(l + u)/2c;
case [
x > A[m] : l := m + 1;
x < A[m] : u := m − 1;
else j := m; return; ] ]
j: = 0;
end UNKNOW
Estructura de
Datos
Andrea Rodrı́guez
Análisis de
Algoritmos
Sistemas recurrentes
Notaciones
En muchos casos de algoritmos, las técnica básicas de conteo para conjuntos
finitos no funcionan. Estos casos son los sistemas recursivos que usan un
enfoque de ecuaciones recurrentes. Ejemplo clásico de tales funciones son la
secuencia Fibonacci.
Sistemas
Recurrentes
F (0)
=
F (1)
=
0
1
F (n)
=
F (n − 1) + F (n − 2), n > 1
Dividir para
conquistar
Estructura de
Datos
Andrea Rodrı́guez
Análisis de
Algoritmos
Notaciones
Un sistema recursivo es un conjunto de condiciones de borde y ecuaciones
recurrentes, las cuales especifican una secuencia única o una función desde N k
to R, con N siendo los naturales, R los reales y k perteneciente a los enteros
positivos I +. Un solución a un sistema recurrente es una función f : N k → R
que satisface las condiciones de borde y los ecuaciones de recurrencia.
Ejemplo: El número de permutaciones de n objetos puede ser expresado
usando el sistema recurrente:
P(0)
=
1,
P(n)
=
nP(n − 1), n > 0
Sistemas
Recurrentes
Dividir para
conquistar
Estructura de
Datos
Andrea Rodrı́guez
Ejemplo: sistemas recursivos
Ejemplo: Considere el siguiente algoritmo que retorna la suma de
los n primeros números en el arreglo A.
ProcedureSUM(n)
begin
total ← 0;
for i ← 1 to n step 1 do
total ← total + A[i];
return total;
end
Análisis de
Algoritmos
Notaciones
Sistemas
Recurrentes
Dividir para
conquistar
Estructura de
Datos
Andrea Rodrı́guez
Análisis de
Algoritmos
Notaciones
Asuma que definimos la complejidad de SUM como el número de
sumas realizadas. Esta función es caracterizada por el siguiente
sistema recursivo o recurrente:
f (0)
= c1 ,
f (1)
= c2 ,
f (n)
=
f (n − 1) + c2 , n > 1
Sistemas
Recurrentes
Dividir para
conquistar
Estructura de
Datos
Andrea Rodrı́guez
Ejercicio
Considere el siguiente algoritmo recursivo de evaluación de un polinomio.
Function P(p)
if p es constante then return p
a := constante del polinomio
pa:= factorización de p por x
return P(pa)x+a
Ejemplo: Sea p = 5x 4 + 3x 3 + 2x 2 + 1, entonces el algoritmo se ejecuta de la
siguiente forma:
P(5x 4 + 3x 3 + 2x 2 + 1)
=
P(5x 3 + 3x 2 + 2x)x + 1 =
P(P(5x 2 + 3x + 2)x)x + 1 =
P(P(P(5x + 3)x + 2)x)x + 1 =
P(P(P(P(5)x + 3)x + 2)x)x + 1
Se pide determinar el tiempo de ejecución estimado de la evaluación de un
polinomio en función de n, donde n es el orden del polinomio.
Análisis de
Algoritmos
Notaciones
Sistemas
Recurrentes
Dividir para
conquistar
Estructura de
Datos
Andrea Rodrı́guez
Análisis de
Algoritmos
Notaciones
Un tipo especial de sistemas recursivos en algoritmos, son los
llamados algoritmos que dividen para conquistar. Estos algoritmos
dividen el problema en problemas más pequeños, resuelven estos
sub problemas y los combinan para obtener la solución global. Este
tipo de enfoque de algoritmos es usualmente implementado como
un sistema recursivo, ya que en muchos casos, los subproblemas
son equivalentes al problema general.
Sistemas
Recurrentes
Dividir para
conquistar
Estructura de
Datos
Andrea Rodrı́guez
Análisis de
Algoritmos
Los casos de algoritmos de este tipo que veremos cumplen las
siguientes restricciones:
1. El costo de resolver un problema de tamaño n = 1 es c,
donde c es una constante no negativa.
2. Para k > 0, problemas de tamaño n = b k son divididos en ‘a’
diferentes problemas de tamaño n/b
3. Para todos los problemas de tamaño n > 1, el costo de
dividir el problema en subproblemas más el costo de
combinar las soluciones para obtener la solución al problema
original es h(n), una función de n.
Notaciones
Sistemas
Recurrentes
Dividir para
conquistar
Estructura de
Datos
Andrea Rodrı́guez
Análisis de
Algoritmos
Notaciones
Sistemas
Recurrentes
Estas condiciones producen problemas de la siguiente forma:
f (1)
= c,
f (n)
= af (n/b) + h(n), n = b k , k > 0
Dividir para
conquistar
Estructura de
Datos
Andrea Rodrı́guez
Análisis de
Algoritmos
Notaciones
Determinación de Costo de Recurrencia
En general, determinar el costo de una recurrencia se hace por:
I
Método de sustitución
I
Método del árbol recursivo
Sistemas
Recurrentes
Dividir para
conquistar
Estructura de
Datos
Andrea Rodrı́guez
Análisis de
Algoritmos
Notaciones
Método de Sustitución
El método de sustitución utiliza dos pasos:
I
Proponer una forma de solución.
I
Hacer un prueba por inducción para encontrar las constantes
y mostrar que la solución funciona.
Sistemas
Recurrentes
Dividir para
conquistar
Estructura de
Datos
Andrea Rodrı́guez
Análisis de
Algoritmos
Notaciones
Inducción matemática
I
Probar que funciona para P(1)
I
Probar que si P(1) . . . P(n) son ciertas, entonces también
funciona para P(n + 1), con n cualquier entero positivo.
Sistemas
Recurrentes
Dividir para
conquistar
Estructura de
Datos
Ejemplo de sustitución
Considere el caso de una recurrencia T (n) = 2T (bn/2c) + n, y
asumamos una solución T (n) = O(nlogn).
La idea entonces es probar que T (n) ≤ cnlgn para un c > 0.
Haciendo inducción:
I
Asumimos que se cumple para bn/2c, esto es
T (bn/2c) = cbn/2clog (bn/2c)
I
Luego por sustitución:
T (n) ≤
2(cbn/2clog (bn/2c)) + n
≤ cnlog (n/2) + n
≤ cnlogn − cnlog 2 + n
≤ cnlogn − cn + n
≤ cnlogn
con c ≥ 1.
Andrea Rodrı́guez
Análisis de
Algoritmos
Notaciones
Sistemas
Recurrentes
Dividir para
conquistar
Estructura de
Datos
Andrea Rodrı́guez
Análisis de
Algoritmos
Ejemplo de sustitución (cont..)
La inducción requiere que se pruebe que la solución funciona para
las condicions de borde. Si asumimos que T (1) = 1 es la condición
de borde, pero T (n) ≤ cnlogn nos da que T (1) ≤ c1lg 1 = 0, una
contradicción.
Sin embargo, podemos hacer que T (n) ≤ cnlogn sea verdadero
para algún n ≥ n0 . Entonces lo que debemos hacer es definir por
ejemplo para un n0 = 2. Ası́ hemos extendido la condición de
borde.
Notaciones
Sistemas
Recurrentes
Dividir para
conquistar
Estructura de
Datos
Andrea Rodrı́guez
Ábol recursivo
El siguiente árbol representa la recurrencia T (n) = 3T (n/4) + cn2 .
T(n/4)
T(n/4)
Notaciones
Sistemas
Recurrentes
cn2
cn2
Análisis de
Algoritmos
Dividir para
conquistar
c(n/4)2
c(n/4)2
c(n/4)2
T(n/4)
.........
.........
.........
............
.........
.........
.........
.........
.........
.........
T(1) T(1) T(1)
Estructura de
Datos
Andrea Rodrı́guez
Análisis de
Algoritmos
Notaciones
Método del árbol recursivo
En un árbol recursivo, cada nodo representa el costo de un
subproblema dentro del proceso recursivo. Se suma el costo de
cada nivel del árbol para obtener el costo por nivel y luego se
suman los costos por niveles. Estos métodos son especialmente
útiles cuando se usan en algoritmos que dividen para conquistar.
Sistemas
Recurrentes
Dividir para
conquistar
Estructura de
Datos
Método del árbol recursivo (cont...)
El tiempo de ejecución estimado para la recurrencia
T (n) = 3T (n/4) + cn2 es:
Andrea Rodrı́guez
Análisis de
Algoritmos
Notaciones
cn2
cn2
Sistemas
Recurrentes
Dividir para
conquistar
c(n/4)2
c(n/4)2
c(n/4)2
.........
.........
.........
............
.........
.........
.........
.........
.........
.........
T(1) T(1) T(1)
3/16 cn2
O(nlog43)
Total : O(n2)
Estructura de
Datos
Andrea Rodrı́guez
Teorema Maestro
Teorema: Sea a y b enteros tal que a ≥ 1 y b > 1, y sea T : N → R la
función recurrente:
Análisis de
Algoritmos
Notaciones
Sistemas
Recurrentes
T (n)
=
aT (n/b) + f (n), n = b k , k > 0
donde n/b es bn/bc o dn/be, entonces:
I Si f (n) = O(nlogb a− ), para alguna constante > 0, entonces T (n) es
acotada por Θ(nlogb a ).
I Si f (n) = O(nlogb a ), entonces T (n) es acotada por Θ(nlogb a logn).
I Si f (n) = Ω(nlogb a+ ), para alguna constante > 0, si af (n/b) ≤ cf (n)
para un c < 1 entonces f (n) y si n es suficientemente grande, entonces
T (n) es acotada por Θ(f (n)).
Dividir para
conquistar
Estructura de
Datos
Andrea Rodrı́guez
Ejemplo: Dividir para Conquistar
El siguiente algoritmo de MAXMIM aplica una técnica de dividir para
conquistar para devolver los valores máximo y mı́nimo de las entradas
A[i], . . . , A[j] de un vector A de enteros.
Function MAXMIN(int i, int j) :< int, int >
begin
if i = j then return < A[i], A[i] >
else
< max1, min1 >← MAXMIN(i, b i+j
c);
2
< max2, min2 >← MAXMIN(b i+j
c + 1, j);
2
if max1 < max2 then max1 ← max2;
if min1 > main2 then min1 ← min2;
return < min1, max2 > end
Análisis de
Algoritmos
Notaciones
Sistemas
Recurrentes
Dividir para
conquistar
Estructura de
Datos
Andrea Rodrı́guez
Análisis de
Algoritmos
Notaciones
Se define la complejidad de este algoritmo como T (n), con T (n) el número de
comparaciones entre elementos del arreglo cuando A tiene n entradas. El
siguiente sistema recurrente describe la función T para cada argumento
potencia de 2:
T (1)
=
c1 ,
T (n)
=
2f (n/2) + c2 , n = 2 , k > 0
k
Entonces por el teorema, donde f (n) = Θ(nlogb a ) = Θ(1), T (n) = Θ(logn).
Sistemas
Recurrentes
Dividir para
conquistar
Estructura de
Datos
Andrea Rodrı́guez
Análisis de
Algoritmos
Notaciones
Ejercicios
Sistemas
Recurrentes
I Use el teorema Maestro par determinar las cotas de las siguientes
funciones de costo computacional:
I
I
I
I
T (n) = 4T (n/2) + n;
T (n) = 9T (n/3) + n;
T (n) = 4T (n/2) + n2 ;
T (n) = 4T (n/2) + n3 ;
Dividir para
conquistar
Estructura de
Datos
Andrea Rodrı́guez
Ejercicios (cont...)
Considere el siguiente algoritmo con un arreglo A[1.. n] :
Procedure XX (var int A[1..n], i, j)
begin
if j ≤ i then return
XX (A, i, b(i + j)/2c);
XX (A, b(i + j)/2c + 1, j);
for k = i to j do
for l = k + 1 to j do
if (A[k] < A[l]) then
temp := A[k];
A[k] := A[l];
A[l] := temp;
endif
enddo
enddo
end
Indique lo que hace el algoritmo y dé su tiempo de ejecución estimado em
función del largo del arreglo A (n).
Análisis de
Algoritmos
Notaciones
Sistemas
Recurrentes
Dividir para
conquistar