Está en la página 1de 44

Análisis y Diseño de

Algoritmos
Unidad 1
Notación Asintótica
¿Por qué el análisis de algoritmos?
• Determinar tiempos de respuesta (runtime).
• Determinar recursos computacionales.

Aproximación teórica
• Generaliza el número de operaciones que requiere un algoritmo para
encontrar la solución a un problema.
Análisis de algoritmos

• Estudio teórico de la eficiencia de un algoritmo.


• Eficiencia: Medida del coste en el uso de recursos que necesita el algoritmo para llevar a
cabo su tarea.
• Recursos más importantes:
• Tiempo de ejecución
• Espacio de almacenamiento
• La medida se denomina complejidad del algoritmo.
• Otros aspectos (contemplados en otras materias):

Funcionalidad Robustez Modularidad

Facilidad de
Mantenibilidad Extensibilidad
uso

Corrección
Ventajas Desventajas
• Elección de algoritmos eficientes para • Para muchos casos, en análisis no es trivial
resolver problemas específicos
• No depende de lenguajes de programación
ni de hardware
Para realizar el análisis de un algoritmo
es necesario:

Determinar el
número de
Conocer la operaciones a
dimensión de la realizar
Conocer la entrada
complejidad (número de
del problema elementos)
que resuelve el
algoritmo
• La complejidad de un algoritmo se representa a través de una función matemática

Polinomios Logaritmos Exponentes …


Funciones
• Un algoritmo puede estar compuesto de
dos o más operaciones, por lo que
determinar la complejidad depende de
• f(n) = cn (algoritmos lineales) identificar la operación más costosa en el
• f(n) = cn2 (algoritmos cuadráticos) algoritmo.
• f(n) = cn3 (algoritmos cúbicos)
• Por ejemplo, sumar 2 matrices e
imprimir el resultado. ¿de qué orden
es el problema?
Principio de invarianza

• A través de un análisis teórico, se pueden obtener funciones que


representen el número de operaciones, independientemente de
cómo se implementaron
Análisis “Principio de la Invarianza”
• Dos implementaciones distintas de un mismo algoritmo no van
a diferir en su eficiencia en más de una constante multiplicativa
“c”
Búsqueda secuencial
type
TDato = ...;
// Array dinámico (indices 0‐based)
TVector = array of TDato;

{ Devuelve el índice donde se encuentra la primera


aparición de x en V o ‐1 si no existe }
function Busqueda(const V: TVector; x: TDato): integer; var
n,i :
integer;
begin
i := 0; n := length(V); { Nº de elementos de V }
while (i < n) and (V[i] <> x) do i := i+1;
if i < n then Busqueda := i else Busqueda := ‐1
end;
Problemas detectados

Dependencia con el Dependencia de valores Dependencia del


tamaño de la entrada: de la entrada: procesador:
• No se tarda lo mismo • Aunque fijemos el • Aunque fijemos el
en buscar en un vector tamaño del vector, no tamaño y los valores
de 10 elementos que se tarda lo mismo en concretos del vector y el
buscar en uno de buscar un valor que valor buscado, el
1,000,000 está en la primera algoritmo tardará
posición que otro que tiempos distintos en
no esté en el vector. computadoras
diferentes.
Dependencia del procesador

• Solución: No medir tiempo (segundos, por ejemplo), sino el número de operaciones elementales
ejecutadas.
• Operación Elemental: Toda operación que tarda un tiempo constante en cualquier procesador
razonable.
• Típicamente, se consideran elementales las asignaciones, operaciones aritméticas y relacionales
con tipos de datos de tamaño fijo, acceso a arrays.
• En general se cuenta sólo un tipo de operación concreta (la más relevante para la eficiencia del
algoritmo)
• Conociendo el número de operaciones y lo que tarda cada una en un procesador concreto, se puede
hallar el tiempo para ese procesador.
• Medida independiente del procesador.
Tamaño de la entrada

• Definición estricta: El mínimo número de bits necesario para representar la parte no pre -
calculable de la entrada del algoritmo.
• Definición útil: Uno o más valores relacionados con los datos de entrada que sirven de parámetros
para expresar las funciones que miden el uso de recursos del algoritmo.
• En el caso de algoritmos que trabajan sobre colecciones de datos, suele ser el número de datos
que contienen.
• Para algoritmos de cálculo con enteros de tamaño arbitrario, se suele usar el número de bits de
esos enteros (se tratan como arrays de bits).
• El tamaño de la entrada puede indicarse por más de un valor (siempre enteros positivos).
Dependencia con el tamaño

• Solución: Expresar la complejidad no mediante un valor sino por una función cuyo parámetro(s) es
el tamaño de la entrada.
• El tamaño de la entrada, si es un único valor, se suele denominar n.
• La complejidad temporal se denominará mediante la función T(n), y la espacial por E(n).
• De esa función interesa, más que su forma concreta, su ritmo de crecimiento.
• Nos da la idea de como escala un algoritmo: Cómo crece su complejidad cuando aumenta el
tamaño de la entrada.
Dependencia con los valores

• Solución: Dividir el análisis en casos.


• Analizar subconjuntos de las entradas cuya complejidad es la misma para todos las entradas de
ese subconjunto. (análisis de peor y mejor caso)
• Calcular un promedio, dada una distribución estadística de las entradas. Típicamente, se
supone que todas las posibles entradas son equiprobables (análisis de caso promedio).
• Nota: Estos análisis trabajan sobre entradas de un tamaño fijo
Análisis Peor Caso – Caso Promedio - Mejor
Caso

• El tiempo que requiere un algoritmo para dar una respuesta, se divide generalmente en
3 casos
• Peor Caso: caso más extremo, donde se considera el tiempo máximo para solucionar un
problema
• Caso promedio: caso en el cual, bajo ciertas restricciones, se realiza un análisis del
algoritmo
• Mejor caso: caso ideal en el cual el algoritmo tomará el menor tiempo para dar una
respuesta
• Por ejemplo, ¿Cuál es el peor y mejor caso de el algoritmo de ordenamiento
“burbuja”?
Operación elemental

Es aquella operación cuyo tiempo de ejecución se puede acotar superiormente por una constante que
solamente dependerá de la implementación particular usada.

• No depende de parámetros
• No depende de la dimensión de los datos de entrada
Crecimiento de Funciones

Orden de crecimiento de funciones


• Caracteriza eficiencia de algoritmos
• Permite comparar performance relativo de algoritmos
Es posible en ocasiones calcular el tiempo de ejecución exacto
• No siempre vale la pena el esfuerzo
• Las constantes y términos de orden más bajo son dominados por los
efectos del tamaño de la entrada
Crecimiento de Funciones
• Diccionario de la Real Academia Española
• Asintótico, ca (De asíntota).
• Adj. Geom. Dicho de una curva: Que se acerca de continuo a una recta o a otra curva sin llegar
nunca a encontrarla.
Crecimiento de Funciones

• Eficiencia Asintótica de Algoritmos


• Cuando el tamaño de la entrada es suficientemente grande que sólo el
orden de crecimiento del tiempo de ejecución es relevante.
• Sólo importa cómo incrementa el tiempo de ejecución con el tamaño de
la entrada en el límite
• El tamaño de la entrada incrementa sin frontera
• Usualmente el algoritmo asintóticamente más eficiente es la
mejor opción, excepto para entradas muy pequeñas
Notación Asintótica

Para determinar la complejidad de un


Eficiencia Asintótica
algoritmo, se siguen los siguientes pasos:

• Orden de crecimiento del algoritmo • Se analiza el algoritmo para determinar


conforme el tamaño de la entrada se una función que represente el número de
acerca al límite (incrementa sin frontera) operaciones a realizar por el mismo
• Se define el orden de la función en
términos de funciones matemáticas,
• Se clasifica de acuerdo a su complejidad
NOTACIÓN ASINTÓTICA
O grande
Dada una función f(n) la notación O(f(n)) representa al
conjunto de funciones con la siguiente propiedad:

⚫ El conjunto O(f(n)) se denomina conjunto de cotas


superiores generado por f(n).

⚫ Toda función que pertenece a O(f(n)) se dice que está


acotada superiormente por f(n).
⚫ Definición alternativa: g(n) mas lenta que f(n)

g(n) mismo crecimiento


que f(n)
Explicación O grande

El conjunto O(f(n)) representa a las funciones que:


⚫ Tienen un ritmo de crecimiento igual o menor que f(n)
⚫ No importan las constantes de proporcionalidad (positivas)
por las que esté multiplicada la función (podemos ajustar
el valor de c en la definición)

⚫ Solo importa el comportamiento para valores de n


grandes, tendentes a infinito (podemos elegir n0 como
queramos)
⚫ Nota: Las funciones con las que tratamos son no
decrecientes, positivas y con parámetros enteros positivos.
Representación gráfica
c se elige para quef(n)
supere a g(n) c · f(n)

g(n)

c·f(n) mayor que g(n) a


partir de aquí

n0 n
Ejemplos

⚫ ¿3·n2  O(n2)?
⚫ Aplicando la definición se debe cumplir:

⚫ Es cierto, escogiendo n0 = 0, cualquier c > 3


⚫ ¿n/10  O(n2)?

⚫ Es cierto escogiendo n0 = 0, cualquier c > 0.1


⚫ ¿0.01·n3  O(n2)?
Ejemplos
30

⚫ ¿3·n2  O(n2)? Si
⚫ Aplicando la definición se debe cumplir:

⚫ Es cierto, escogiendo n0 = 0, cualquier c > 3


⚫ ¿n/10  O(n2)? Si

⚫ Es cierto escogiendo n0 = 0, cualquier c > 0.1


⚫ ¿0.01·n3  O(n2)? No
Falso!
Propiedades
⚫ f(n)  O(f(n)): Paso de expresión a notación asintótica.

⚫ O(k·f(n))  O(f(n)): Las constantes de proporcionalidad se omiten.


⚫ O(f(n) + g(n))  O(máx{f(n), g(n)}) : Sólo importa el término de mayor
crecimiento.

⚫ O(f(n) - g(n))  O(f(n)) si g(n) crece más lentamente que


f(n), en caso contrario no se puede simplificar.
⚫ Los productos y divisiones pueden incorporarse a la
notación O-grande sin problemas.
⚫ Dentro de la O-grande se debe usar la función más
sencilla posible!
Regla del máximo

• Si f(n) y g(n) son funciones de comportamiento, entonces


• O(f(n) + g(n)) = O(máx{f(n), g(n)})
Ejemplo: Relación de orden
Transitiva
𝑔 𝑛 ∈ 𝑂 ℎ 𝑛 : 𝑠𝑖 𝑠𝑎𝑏𝑒𝑚𝑜𝑠 𝑞𝑢𝑒 𝑔 𝑛 ≤ 𝑐𝑓 𝑛 𝑦 𝑞𝑢𝑒 𝑓 𝑛 ≤
𝑑ℎ 𝑛 , 𝑒𝑛𝑡𝑜𝑛𝑐𝑒𝑠 𝑔 𝑛 ≤ 𝑐𝑑ℎ 𝑛 , 𝑒𝑠𝑜𝑡 𝑒𝑠 𝑔 𝑛 ∈ 𝑂 ℎ 𝑛 .
Reflexiva
𝑃𝑎𝑟𝑎 𝑡𝑜𝑑𝑎 𝑓𝑢𝑛𝑐𝑖ó𝑛 𝑑𝑒 𝑐𝑜𝑚𝑝𝑜𝑟𝑡𝑎𝑚𝑖𝑒𝑛𝑡𝑜 𝑓 𝑠𝑒 𝑐𝑢𝑚𝑝𝑙𝑒 𝑞𝑢𝑒 𝑓 𝑛 ∈ 𝑂 𝑓 𝑛 .
Antisimetrica
Jerarquía de funciones

⚫ T(n)  O(1)  T(n) = cte: Funciones constantes, que no dependen del


tamaño de la entrada.

⚫ T(n)  O(n1/a), a → : Funciones subpolinómicas, crecen más lentamente que


cualquier polinomio.

⚫ T(n)O(na): Funciones polinómicas, existe un polinomio que las acota.

⚫ T(n)O(na), a→: Funciones no polinómicas, crecen más rápido que


cualquier polinomio.
Ejemplo

o Considere la función f(n) = 2n2 + 3n + 1


o Debido a que f(n) es un polinomio cuadrático, se deduce que su estructura general tiene la forma
an2 + bn + c

o Para n muy grande, an2 “domina” al resto de laecuación

o Por tanto, se propone una g(n) = n2 de tal forma que se demostrará si f(n)  O(n2)
Ejemplo

oPara demostrarlo, se debe apelar a la definición de O:


O(n2) = {f(n) |  c constante positiva, n0: 0 < f(n) ≤ c n2,  n ≥ n0}, donde f(n) = 2n2 + 3n + 1

Se deben encontrar c y n0 para los cuales se cumple

0 < 2n2 + 3n + 1 ≤ cn2


0 < 2 + 3/n + 1/n2 ≤ c

Notemos que si n ->, 2 + 3/n + 1/n2 - > 2 Si n = 1 entonces 2 + 3/n +


1/n2 = 6
Por tanto, para c = 6 y n0 = 1, se demuestra que f(n)  O(n2)
Notación Omega

o f(n) = Ω(g(n)), g(n) es una cota asintótica inferior de f(n)


o Dada una función g(n), denotamos al conjunto de
funciones Ω(g(n)) de la siguiente forma:
(g(n)) = { f:N-> R+| c constante positiva y n0: 0 < cg(n) ≤ f (n),
n ≥ n0}
NOTA: f(n) Ω (g(n)) sí y solo si g(n) O( f(n) )
Propiedades de omega

1. Para cualquier función de f se tiene que f Ω(f).


2. f Ω(g) Ω(f) Ω(g).
3. (f) = Ω(g) f Ω(g)y g Ω(f).
4. Si f Ω(g) y g Ω(h) f Ω(h ).
5. Si f Ω(g) y f Ω(h) f Ω(max(g,h)) .
6. Regla de la suma: Si f1 Ω ( g ) y f2 Ω ( h ) f1 + f2 Ω ( g +h)
7. Regla del producto: Si f1 Ω ( g ) y f2 Ω ( h ) f1 f2 Ω ( g h).
8. Si existe lim f (n) k , dependiendo del valor de kobtenemos:
n g(n)
a) Si k 0 y k < entonces Ω ( f ) =Ω(g).
b) Si k = 0 entonces g Ω(f), es decir, Ω ( g ) Ω(f),pero sin embargo se verifica que f Ω(g).
z
Notación Theta: 

o f(n) =  (g(n)), c2g(n) y c1g(n) son las cotas asintóticas de f(n) tanto superior como inferior
respectivamente
o Diremos que f(n)   (g(n)) si f(n) pertenece tanto a

O(g(n)) como a  (g(n))


(g(n)) = { f : N ->R+| c1,c2 constantes positivas, n0: 0 < c1g(n) ≤ f(n)
≤ c2g(n),  n ≥ n0}
Propiedades de Theta

1. Para cualquier función f se tiene que f  (f).


2. f  (g)  (f)  (g).
3. (f ) = (g)  f  (g) y g  (f ).
4. Si f  (g) y g  (h)  f  (h).
5. Regla de la suma: Si f1  (g) y f2  (h)  f1 + f2  (max(g,h)).
6. Regla del producto: Si f1  (g) y f2  (h)  f1 • f2  (g • h) .
f (n)
7. Si existe lim = k , dependiendo del valor de k obtenemos:
n→ g(n)

a) Si k  0 y k <  entonces (f) = (g).


b) Si k = 0 entonces los órdenes exactos de f y g son distintos.
Teorema

Teorema

f(n) = Q (g(n)) sí y solo si f(n) = O(g(n)) y f(n) = Ω (g(n)).


Ejemplo

Considere la función f(n) = ½ n2 – 3n


Debido a que f(n) es un polinomio cuadrático, se deduce que su estructura general tiene la
forma an2 + bn + c
Para n muy grande, an2 “domina” al resto de laecuación
Por tanto, se propone una g(n) = n2 de tal forma que se demostrará si f(n)   (n2)
Para demostrarlo, se debe apelar a la definición de O:
O(n2) = {f(n) |  c constante positiva, n0: 0 < f(n) ≤ c n2,  n ≥ n0}, donde f(n) = 2n2 + 3n + 1
Se deben encontrar c y n0 para los cuales se cumple
0 < 2n2 + 3n + 1 ≤ cn2
0 < 2 + 3/n + 1/n2 ≤ c
Notemos que si n ->, 2 + 3/n + 1/n2 → 2
Si n = 1 entonces 2 + 3/n + 1/n2 = 6
Por tanto, para c = 6 y n0 = 1, se demuestra que f(n)  O(n2)
Para demostrarlo, se debe apelar a la definición de
:
(n2) = {f(n) | c1,c2 constantes positivas, n0: 0 < c1n2 ≤ f(n) ≤c2 n2,
n ≥ n0}, donde f (n) = ½ n2 –3n
Se deben encontrar c1, c2 y n0 para los cuales se cumple
0 < c1n2 ≤ ½ n2 – 3n ≤ c2 n2
0 < c1 ≤ ½ – 3/n ≤ c2
Esta ecuación se analiza por casos:
c1 ≤ ½ –3/n
½ – 3/n ≤ c2

También podría gustarte