1. Programación II
Análisis de Algoritmos (I)
Ing. Mary López
Universidad Autónoma Gabriel Rene Moreno
FICCT
Semestre I/2019
2. Palabras Clave
- Algoritmo: Conjunto de pasos o instrucciones descritas
en un lenguaje sencillo que permiten llegar a la solución
sistemática de un problema.
- Lenguaje Algorítmico: Son una serie de símbolos y
reglas que se utilizan para describir de manera explícita un
proceso.
- Complejidad Algorítmica: Se le denomina a la medida
de la eficiencia de un programa.
- Análisis de un Algoritmo: Observar el comportamiento
de un algoritmo y predecir la cantidad de recursos
(tiempo, memoria, CPU) que un algoritmo requerirá para
cualquier entrada.
5. Historia
La definición de
“algoritmo” data del
siglo IX se atribuye su
invención al
matemático Abu Ja’far
Muhammad ibn Musa
al-Khwarizmi.
Algoritmos
Definición
Un algoritmo es un
conjunto finito de
instrucciones
precisas que
realizan una tarea, la
cual, dado un estado
inicial, culminará por
arrojar un estado final
reconocible
9. Resolver un problema mediante alguna técnica:
Algoritmos Voraces.
Algoritmos Recursivos.
Algoritmos Paralelos.
Algoritmos Probabilísticos
Algoritmos Determinísticos
Algoritmos No Determinísticos
Algoritmos Modulares.
Que es el Diseño de un
Algoritmo ?
Este capitulo basara su estudio mas que todo en Algoritmos
Modulares y Recursivos.
10.
11. 2
¿Cuál es la base del
análisis de
Algoritmos?
- Conteo del numero
de instrucciones
necesarias para
resolver un
determinado
tamaño de
problema con un
numero de
entradas N.
3
¿Que factores afectan
el desempeño del
algoritmo?
- Hardware:
Procesador,
Memoria, Disco duro.
- Software:
Sistema Operativo,
Compilador.
1
¿Cuándo realizar
análisis de
Algoritmos?
Si el propósito es resolver
el problema
eficientemente.
Entonces habrá que
medir el grado de
complejidad del mismo.
12. Es una de las herramientas de evaluación de un
diseño sin necesidad de desarrollarlo.
Puede establecer una medida de la calidad de los
algoritmos.
Permite comparar un algoritmo con otros diseños.
Para que sirve el Análisis de
Algoritmos ?
Recuerde !!!
Complejidad no tiene que ver con su dificultad, mas
bien con el rendimiento!!
13. 2.1 Elementos del AA
El Análisis de Algoritmos consta de 2 partes:
1. Tiempo Running Time (Contador de frecuencias)
• Hallar el T(n) para :
– Caso Peor = Ejecutar el mayor numero de instruc.
– Caso Promedio = Ejecutar un promedio de instruc.
– Caso Mejor =Ejecutar el menor numero de instruc.
2. Orden de Magnitud Complejidad Algorítmica
• Caso Peor O(n)
• Caso Promedio Ɵ(n)
• Caso Mejor Ω(n)
14. 2.1.1 El running time
El calculo del running time es el conteo de pasos
elementales para cierto tamaño de entrada.
Por ejemplo un programa puede tener un tiempo de ejecución
y=2*x 1
ShowMessage(y) 1
T(n) = 2
• Las unidades de T(n) se dejan sin indicar, ya que no es
posible especificarla ni siquiera en segundos ya que esto
variaría de computador a computador.
15. 2.1.2 Complejidad Algorítmica
Existe la complejidad: Espacial (memoria) y la temporal (tiempo).
Por su mayor incidencia en el rendimiento se estudia la complejidad
temporal.
La complejidad temporal cubre 3 aspectos:
Peor caso (Big O): mayor número posible de instrucciones
ejecutadas
Mejor caso (Ω Omega): menor número posible de
instrucciones ejecutadas
Caso medio (Ɵ Teta): Se puede calcular como la suma de
las comparaciones mínimas y máximas dividida entre dos.
16.
17. Notación Asintótica
Llamamos notación asintótica a la familia de relaciones
conocidas como notación de Bachmann-Landau, la cual es
usada para describir el tiempo de ejecución de los algoritmos
(O, Ω, Ɵ).
Un algoritmo tiene comportamiento asintótico cuando el tamaño de
las entradas N tiende a infinito.
Se puede analizar:
• El caso peor => Big O (“o mayúscula”)
• El caso mejor => Ω
• Por ejemplo cuándo el tiempo de ejecución de un algoritmo
es igual a O(f(n)) debe interpretarse como que tiene una
velocidad de crecimiento f(n) en el peor de los casos.
18. 3.1 Factor de crecimiento
Es este análisis matemático asintótico se observan 4
tipos de orden de crecimiento.
Figura 1.1 Comparación de 4 programas
19.
20. Crecimiento Logarítmico
Esta complejidad se presenta en algoritmos que
presentan duplicaciones repetidas o divisiones a la
mitad
En la complejidad logarítmica no se tiene en cuenta
la base, porque un cambio entre bases sólo implica
multiplicar por una constante.
Log10n = log2n / log210
1/log210 = 0.301
Log10n = 0.301 * log2n.
21. Empezando con X=1
¿Cuántas veces debe ser X duplicado antes de que sea mayor que N?
Empezando en 1 se puede duplicar un valor repetidamente solamente un
número de veces logarítmico antes de alcanzar N
Nro. de Veces que se duplica 1 2 3 .. P
Valor x 2 4 8 .. N
Valor equivalente a x 21 22 23 .. 2P
El número de veces que se duplica es p. 2p= N
Aplicando logaritmo en ambos lados se tiene p = log2N.
Por lo tanto se duplica en un número logarítmico de veces.
Duplicaciones Repetidas
Crecimiento Logarítmico
22. Empezando con X=N
Si N se divide de forma repetida por la mitad
¿cuántas veces hay que dividir para hacer N menor o igual a 1?
Nro de Veces
que se divide
1 2 3 .. P
Nro de datos N/2 N/4 N/8 .. N/N
Nro de datos N/21 N/22 N/23 .. N/2P
El número de veces que se divide es p. (N/N = N/2p)
Por lo tanto se divide N hasta llegar a 1 un número logarítmico
de veces.
2p= N p = log2N.
Divisiones a la mitad
Crecimiento Logarítmico
23. ¿ Importa la Eficiencia ?
¿Que utilidad tiene diseñar algoritmos eficientes si las computadoras
procesan la información cada vez más rápido?
Bien; para demostrar la importancia de la elaboración de algoritmos
eficientes, se plantea el siguiente problema:
Contamos con una computadora capaz de procesar
datos en 10-4 segundos. Existen dos algoritmos para
leer los registros de una base de datos, uno de los
algoritmos tienen una complejidad exponencial 2n y
el otro complejidad cubica n3
¿Cuánto tiempo se tardará en procesar cada algoritmo para una
entrada de N datos?
24. Polinómica Vs Exponencial
Para N3Para 2N
Se puede concluir, que solo un algoritmo eficiente, con un orden de
complejidad bajo, puede tratar grandes volumen de datos.
Luego es cierto que:
• Muy eficiente si su complejidad es de orden log n
• Eficiente si su complejidad es de orden N
a
• Ineficiente si su complejidad es de orden 2n
Se considera que un problema es tratable si existe un algoritmo que lo
resuelve con complejidad menor que 2n , y que es intratable o
desprovisto de solución en caso contrario.
Se dice que los
computadores
cuánticos podrán
resolver algoritmos de
orden complejidad 2N
25. Orden Nombre Comentario
O(1) Constante Sea cual sea la talla del problema responde a un tiempo constante.
O(long n) Logarítmico
Los que el tiempo crece con un criterio logarítmico, independiente de cual
se la base mientras esta sea mayor que1. Implican que un bucle realiza
menos iteraciones que la talla del problema (raro). Por ejemplo búsqueda
binaria.
O(n) Línea El tiempo crece linealmente con respecto a la talla. Por ejemplo encontrar
el valor máximo de un vector.
O(n log n) Log lineal Es un orden relativamente bueno, porque la mayor parte de los algoritmos
tienen un orden superior. En este grupo esta el algoritmo de ordenación
QuickSort.
O(nc) con c>1 Polinomico Este es muy común. Cuando se c=2 es cuadrático, cuando c=3 es cubico.
A partir de este orden el resto son bastante complejos.
O(cn) con c>1 Exponencial Este crece muchísimo mas rápido que el anterior.
O(n!) Factorial Son algoritmos que para encontrar una solución prueban todas las
combinaciones posibles.
o(nm
) Combinatorio
Es tanto INTRATABLE como el anterior. Usualmente no se los distingue.
26.
27. Se realizara el conteo de frecuencias aplicando
notación asintótica :
Por ejemplo:
Leer a, b 1
x=a+b 1
Mostrar a,x 1
-----------------------------------------------
Contador de Frecuencia 3
• Se suma todo aplicando Aritmética Asintótica
• Se encontrara una expresión algebraica que indica el
numero de veces que se ejecutan las instrucciones
del algoritmo.
Como encontrar la expresión del conteo de frecuencias para N
entradas?
3.2 Tiempo de Ejecución (running time)
28. Tomando la expresión que representa el Running Time:
Buscar en el termino dominante (el que mas crece cuando N
aumenta ):
• La constante multiplicativa del termino dominante se ignora (valor=1).
• Se quitan los coeficientes, constantes y términos negativos.
• De cada conjunto de términos dependientes se selecciona el termino
dominante (MAYOR).
• Los términos inferiores al dominante se ignoran (valor=0)
• El resultado será la suma que usualmente llega a ser de un solo
termino.
3.3 Reglas para determinar la Complejidad del Algoritmo
29. Son las sentencias que son independientes al tamaño del problema:
asignaciones, entradas, salidas, etc.
Su tiempo de ejecución es constante Complejidad es O(1)
y=2*x O(1)
mostrar x,y O(1)
Regla 1: Sentencias Simples
30. Si se tienen instrucciones simples.
x = 1;
y = pow(2,3); 1*O(1) = O(1)
Ejemplos
Si k es una constante.
for (int i= 0; i < K; i++) {
algo_de_O(1) ; } K*O(1) = O(1)
1*O(1) = O(1)
31. Los condicionales suele ser O(1), a menos que estos involucren un llamado a un
procedimiento, y siempre se le debe sumar la peor complejidad posible de las 2
alternativas del condicional. En decisiones múltiples (switch) para el análisis del caso
peor se tomará las ramas mas pesadas en tiempo.
Por ejemplo
If (a>b)
{
suma=1;
s=pow((suma*x),2)
}
else
{
suma=0;
}.
O(T(Si−Entonces−Sino))=O(T(condición))+Máx(O(T(Entonces)),O(T(Sino)))
Complejidad=O(2)
Complejidad=O(1)
Complejidad=O(1)
Regla 2: Sentencias If/Else
O(3)
32. Si N es el limite de las iteraciones
for (int i= 0; i < N; i++) {
algo_de_O(1) ; } N * O(1) = O(n)
Ejemplo
33. El tiempo para ejecutar un ciclo es la suma, sobre todas las
iteraciones del ciclo, del tiempo de ejecución del cuerpo y del
empleado para evaluar la condición de terminación (este último suele
ser O(1)).
A menudo este tiempo es, despreciando factores constantes, el
producto del número de iteraciones del ciclo y el mayor tiempo
posible para una ejecución del cuerpo.
O(T(ciclo)) = O(T(iteraciones)) *O(T(cuerpo))
Regla 3: Ciclos repetitivos
34. Si se tiene ciclos anidados
for (int i= 0; i < N; i++) {
for (int j= 0; j < N; j++) {
algo_de_O(1) ; }
}
El ciclo anidado con variable dependiente: El clico exterior se
realiza N veces, el interior 1, 2, ... N veces. Regla de la Serie
for (int i= 0; i < N; i++) {
for (int j= 0; j < i; j++) {
algo_de_O(1);
}
}
N * N * O(1) = O(n2)
(N * (0+N-1)/2)) * (1)
= ((1/2*N2)-(1/2*N))
= O(n2)
Ejemplos
35. Si N es el limite de iteraciones..
for (int i= 0; i < N; i++) {
c= i;
while (c > 0) {
algo_de_O(1)
c= c/2;
}
}
El bucle interno de orden O(log n) que se ejecuta N
veces, luego el conjunto es de orden O(n log n)
Ejemplos
36. A veces aparecen bucles multiplicativos, donde la evolución de la
variable de control no es lineal (Ver presentación anterior)
Si C es una variable.
c= 1;
while (c < N)
{ algo_de_O(1)
c= 2*c;
}
Si C es una variable.
c= N;
while (c > 1) {
algo_de_O(1)
c= c / 2;
}
El valor inicial de "c" es 1, llegando
a "2k" al cabo de "k" iteraciones. El
número de iteraciones es tal que:
2k >= N => k= es (log2 (N))
[el entero inmediato superior]
Por tanto, la complejidad del
bucle es O(log n).
La variable se divide a la mitad
en cada vuelta de ciclo.
Por lógica :
log2(N) iteraciones
Por tanto, con un orden :
O(log n).
Ejemplos
37. Si el coste del algoritmo T(n) es:
an2 +bn + c
- Se deprecian la constante sumativa c=0
- Se deprecian la constante multiplicativa a =1 y b=1
- Luego : n2 + n
- Luego el término dominante: n2 O(n2)
Un ejemplo completo
38. Existen reglas que permiten simplificar la expresión
algebraica de tal forma que se pueda llegar a un
elemento de la tabla 1.1 y determinar el orden de
complejidad del algoritmo.
3.4 Aritmética Asintótica
39. • La diferencia (d) entre términos (t) sucesivos es constante:
t, t + d, t + 2d, ..., t + (n – 1)d => 1, 3, 5, 7, 9
Regla 1: Serie Aritmética
•Luego: (Si N es el numero de términos) : Sn = [(PrimerTerm+UltimoTerm)/2]* N
•Regla útil para conteo de repeticiones en ciclos o bucles anidados.
• Considere el siguiente programa :
For i=1 to m
p(i)
1. En este programa el tamaño de la instancia es M. Si p(i) toma un tiempo constante
t y no depende de i , entonces este tiempo se repetirá m veces :
𝑡=1
𝑚
𝑡 = 𝑡
𝑡=1
𝑚
1 = 𝑡 ∗ 𝑚 = 𝑂(𝑚)
2. En un caso mas general donde t(i) no es constante y depende de i debería calcular
el resultado de la siguiente sumatoria:
𝑡=1
𝑚
𝑡 𝑖 Aplicar regla de la serie = 𝑚
1 + 𝑚
2
40. • Si:
T1(n) y T2(n) son tiempos de ejecución de dos algoritmos A1 y A2.
• Donde:
T1(n) es O(f(n)) y T2(n) es O(g(n))
• Entonces:
T1(n) + T2(n)
• Luego el tiempo de ejecución de A1 seguido de A2:
O(max(f(n),g(n)))
Regla2 : Sumar y Simplificar
•Ejemplo 4: Calcular la complejidad del programa:
x = 1; y = 2n; z = n2; O(Max(1,2n,n2)) O(n2)
41. • Si:
T1(n) y T2(n) son tiempos de ejecución de dos algoritmos A1 y A2.
• Donde:
T1(n) es O(f(n)) y T2(n) es O(g(n))
• Entonces:
T1(n)*T2(n) es O(f(n)*g(n)))
Regla 3: Producto para representar repetición
• Si alguna acción es repetida un cierto número de veces, y cada
repetición tiene el mismo costo, entonces el costo total es, el
costo de la acción multiplicado por el número de veces que la
acción tuvo lugar
42. • Si C es una constante:
• O(c*f(n)) = O(f(n))
• Por ejemplo:
O((n2)/2) O(n2)
Regla 4: Constante Multiplicativa
• Se puede ignorar cualquier constante multiplicativa en
las ecuaciones.
43. Int calcula(){
int x=2*5; 1
return x; 1
}
// Calcula es 2
Void main(){
int a=0; 1
int b=calcula(); 2
count<< b; 1
}
//Conteo total seria 4
Regla 5: Llamadas a procedimientos
La complejidad de la llamada a un procedimiento será igual
a la complejidad del procedimiento.
Notas del editor
Algoritmo se refería originalmente a las reglas de la aritmética con números arábigos.
Hasta el siglo XVIII su significado se difundió abarcando procedimientos para resolver problemas o algunas tareas.
Se considera que el primer caso de algoritmo para una computadora esta en las notas escritas por Ada Byron en 1842
Las notas eran para el motor analítico de Charles Babbage.
Babbage nunca termino su motor analítico por lo que el algoritmo jamás llego a implementarse
Sin embargo Ada Byron es considerada la primera programadora de la historia
Entrada Cero o Mas
Salida Uno o Mas
Precondiciones Condiciones que se asume los datos de entrada cumplen
Pos condiciones Describen como deben estar calculados los datos de salida
Los algoritmos voraces suelen ser simples. Se emplean sobre todo para resolver problemas de optimización (Encontrar la secuencia óptima para procesar un conjunto de tareas)
Un algoritmo recursivo es un algoritmo que expresa la solución de un problema en términos de una llamada a sí mismo.
Paralelos: En oposición a los algoritmos secuenciales, es un algoritmo que puede ser ejecutado por partes en el mismo instante de tiempo por varias unidades de procesamiento, para finalmente unir todas las partes y obtener el resultado correcto.
Probabilisticos: En términos informales, es completamente predictivo si se conocen sus entradas. Dicho de otra forma, si se conocen las entradas del algoritmo siempre producirá la misma salida, y la máquina interna pasará por la misma secuencia de estados
Denomina tratable si existe un algoritmo de complejidad polinomial para resolverlo, caso contrario se denomina intratable.
A medida que el problema aumenta los algoritmos de complejidad polinomial dejan de ser utilizables gradualmente mientras
los que no EXPLOTAN de un MOMENTO AL OTRO
El trabajo con algoritmos ha generado varias interrogantes a los ingenieros que se dedican a trabajar en el campo de las ciencias de la computación, por ejemplo
¿Cómo podemos caracterizar un algoritmo que resuelve determinado problema?
¿Cómo podemos comparar dos algoritmos que resuelven el mismo problema?
¿Existirá un algoritmo ideal para resolver cualquier problema computacional?
La primera condición que se debe tener en cuenta al elegir el algoritmo es la Simplicidad de su diseño.
La segunda condición es la Claridad del algoritmo, a modo que sea factible documentarlo de manera eficiente.
La tercera y una de las características más importantes que se debe considerar es la Eficiencia del mismo
Para el desarrollo de aplicación grandes lo que se tomara mayormente en cuenta será el TIEMPO DE EJECUCION DEL ALGORITMO.
Las entradas influyen bastante al calcular el T(n). Por ejemplo para un algoritmo de ordenación dependerá de lo ordenado que estén las entradas.
Se puede calcular como la suma de las comparaciones mínimas y máximas dividida entre dos
n 2^N seg min horas dias Años Siglos
10 1024 0.1024
20 1048576 104.8576 1.7476
30 1073741824 107374.1824 1789.5697 29.8261 1.2427
40 1.09951E+12 109951162.8 1832519.38 30541.9896 1272.5829 3.4865
50 1303124.89 3570.20 35.70
Estos ejemplos solo analizan el incremento de variable J para ubicar el termino DOMINANTE del algoritmo
Int calcula(){
int x=2*5; 1
return x; 1
}
// Calcula es 2
Void main(){
int a=0; 1
int b=calcula(); 2
count<< b; 1
}
//Conteo total seria 4
Dadas dos partes de un programa ejecutadas en secuencia, sólo se necesita considerar la parte más cara.
Solución Ejemplo 4:
Si la complejidad de cada asignación es O( 1 ).
Se puede concluir que :
TA( n ) es O( max( 1,1,1 ) ) O( 1 ).
Int calcula(){
int x=2*5; 1
return x; 1
}
// Calcula es 2
Void main(){
int a=0; 1
int b=calcula(); 2
count<< b; 1
}
//Conteo total seria 4