modulo 12 - Universidad Nacional de Colombia

Anuncio
3004597 – Estructura de Datos – Modulo 12
Universidad Nacional de Colombia – Sede Medellín
MODULO 12
OBJETIVOS
Por medio de este módulo se pretende:
-
Afiance sus conocimientos acerca de la medida del orden de los algoritmos.
-
Aplique métodos prácticos que le permitan calcular el orden de un algoritmo dado y
aprenda a interpretar los resultados.
-
Compare la eficiencia de diferentes algoritmos que llevan a cabo la misma tarea por medio
del cálculo del orden.
CONTENIDO
1. Métodos de análisis de algoritmos
1.1 Reglas teóricas
1.2 Reglas prácticas
1.3 Barómetro
1.3.1
Selección de barómetro
A Referencias
B Taller
02-2003
1
3004597 – Estructura de Datos – Modulo 12
1
Universidad Nacional de Colombia – Sede Medellín
MÉTODOS DE ANÁLISIS DE ALGORITMOS
Realmente no existe una fórmula siempre aplicable para analizar la eficiencia de un algoritmo.
Básicamente se trata de una cuestión de buen juicio, intuición y experiencia. A menudo un primer
análisis da origen a una función de apariencia complicada, que envuelve sumatorias y recurrencias
y el siguiente paso es simplificar esta expresión buscando una función que represente el orden del
algoritmo de la manera más sencilla posible. A continuación se presentan algunos ejemplos
prácticos. Es importante recordar que N representa el tamaño del programa y su principal
característica es que varía en cada ejecución, por ejemplo, en un programa de ordenamiento de un
arreglo, N sería el número de elementos del arreglo.
1.1 REGLAS TEÓRICAS
A continuación se presentan varias reglas que son muy útiles para efectuar cálculos de órdenes de
algoritmos:
1. Sea k una constante, si f es de orden O(g) entonces k*f es de orden O(g)
2. Si f1 es de orden O(h1), f2 es de orden O(h2) y fn es de orden O(hn), entonces f1 + f2 +
…+ fn es de orden O( max(h1,h2, … , hn) ).
3. Si f es de orden O(h1) y g es de orden O(h2) entonces f*g es de orden O(h1*h2)
4. Sean los reales 0<a<b entonces O(na) es subconjunto (es decir, pertenece a) de O(nb)
5. Sea P(n) un polinomio de grado k entonces P(n) es de orden O(nk)
6. Sean los reales a, b > 1 entonces O(loga) = O(logb)
La regla 6 permite olvidar la base en la que se calculan los algoritmos en expresiones de
complejidad. La combinación de las reglas 1 y 5 es probablemente la más usada, permitiendo
ignorar todos los componentes de un polinomio, menos su grado. Por último, la regla 2 es básica
para analizar el concepto de secuencia en un programa: la composición secuencial de varios trozos
de programa es de orden de complejidad igual a la suma de los órdenes de sus partes.
1.2 REGLAS PRÁCTICAS
Las siguientes reglas son útiles al momento de analizar el orden un algoritmo, se basan en las
reglas teóricas dadas en la sección anterior, pero ahora se muestran aplicadas a programación:
Sentencias sencillas:
Se conocen como sentencias sencillas a las sentencias de asignación, entrada/salida, cálculos
aritméticos simples y en general toda instrucción básica que tome un tiempo de ejecución
constante y que no trabaje sobre variables estructuradas cuyo tamaño esté relacionado con el
tamaño N del problema. Todas las sentencias sencillas tienen orden de complejidad O(1)
(conocido como orden constante).
02-2003
2
3004597 – Estructura de Datos – Modulo 12
Universidad Nacional de Colombia – Sede Medellín
Secuencias:
El orden de una secuencia de instrucciones en un programa, es la suma de los órdenes de las
instrucciones que la componen.
Decisión (IF … ELSE IF … ELSE, SWITCH … CASE):
El orden de un bloque de decisiones es el orden del peor caso posible. (Es decir, se calcula el
orden de cada bloque if, else if, else y el mayor de estos se establece como orden de todo el
bloque de decisión). (Este es un ejemplo del criterio de peor caso).
Bucles (WHILE, FOR)
El orden de un bucle iterativo es igual al producto del orden de la función que indica el número de
iteraciones que ejecutará el bucle según el tamaño del problema y del orden de las operaciones
internas en el bucle.
Este producto de órdenes es, a su vez, igual al orden del producto de la función que expresa el
número de iteraciones y las que representan a las operaciones internas al bucle. Si el bucle se
ejecuta un número fijo de veces, independiente de N, entonces la repetición sólo introduce una
constante multiplicativa que puede absorberse.
Llamadas a procedimientos (Funciones)
La complejidad de llamar a un procedimiento (función) viene dada por la complejidad del
contenido del procedimiento. El coste de llamar al procedimiento es una constante que
podemos obviar inmediatamente de nuestro análisis de orden. El cálculo de la complejidad
asociada a un procedimiento puede complicarse notablemente si se trata de procedimientos
recursivos, en ocasiones se requiere utilizar técnicas propias de la matemática discreta y del
cálculo de series.
Ejemplo 1.1: Orden de un bucle
Calcular el orden de la siguiente sección de programa:
int c=N;
while( c>1 )
{
…
sentencias de orden 1
…
c=c/2;
}
El valor inicial de c es N, siendo igual a N/2k después de k iteraciones, el bucle se ejecutará un
número de veces k dado por la ecuación N/2k = 1, así que para obtener el número de veces que se
ejecutará el interior del ciclo para un tamaño de programa N dado, basta despejar k de esta
ecuación:
N = 2k sacando log2 a ambos lados: k = log2(N) así que de acuerdo a las reglas teóricas, se
concluye que la sección de programa es de orden O(log(n))
02-2003
3
3004597 – Estructura de Datos – Modulo 12
Universidad Nacional de Colombia – Sede Medellín
Ejemplo 1.2: Aplicación de la regla del producto
El orden de un proceso iterativo (ciclo) es igual al producto del orden de la función que indica el
número de iteraciones en función del tamaño del problema y del orden de la operación interna en el
bucle. El orden de este producto es, a su vez, igual al orden del producto de la función que
expresa el número de iteraciones multiplicado por el orden de las operaciones internas al bucle.
En este ejemplo se tiene un algoritmo para ordenar cada una de las filas de una matriz cuadrada
de n x n elementos. El bucle iterativo es:
for(int i=0; i<n; n++)
{
Ordenar fila i;
}
O su equivalente en la forma básica "mientras":
int i=0;
while( i<=n )
{
Ordenar fila i;
i++;
}
Es inmediato que el número de repeticiones del ciclo es n, que es una función de orden lineal O(n).
Supóngase que para ordenar la fila se utiliza un algoritmo de tipo cuadrático. Entonces el orden de
complejidad de todo el algoritmo será cúbico, ya que, por la regla del producto:
O(n).O(n2) = O(n.n2) = O(n3)
1.3 BARÓMETRO
En el ejemplo anterior se dieron todos los detalles del análisis. Los detalles como la inicialización
de los ciclos (que se consideraron mediante una constante) raramente se consideran de manera
explícita. (Sin embargo, en el siguiente ejemplo se muestra que no siempre pueden hacerse
ignorarse las inicializaciones y control de los ciclos). Por lo general es suficiente escoger
alguna instrucción representativa (que por regla general es la instrucción simple que más se
repite en el algoritmo) y contar cuantas veces es ejecutada, de la ecuación resultante de esta
operación se calcula el orden del algoritmo.
A esta instrucción representativa se le conoce como barómetro, la selección del barómetro
adecuado en el algoritmo es una de las fases más importantes del análisis de orden. Por
supuesto, se considera que el tiempo de ejecución de la instrucción seleccionada como barómetro
puede delimitarse con una constante. En el ejemplo anterior, un buen barómetro sería el if dentro
del ciclo interior, en este caso, el barómetro se ejecuta n(n-1)/2 veces cuando se organiza un
vector de n elementos.
1.3.1
02-2003
SELECCIÓN DEL BARÓMETRO
4
3004597 – Estructura de Datos – Modulo 12
Universidad Nacional de Colombia – Sede Medellín
Cuando un algoritmo incluye varios ciclos anidados, como es el caso del sort de selección,
cualquier instrucción del ciclo interior puede escogerse, usualmente, como barómetro. Sin
embargo, existen casos donde es necesario tener en cuenta el control implícito de los ciclos.
Consideremos el siguiente algoritmo:
T es un arreglo de n enteros tales que 0<=T[i]<=i para todo i<n. Sea s la suma de los elementos
de T. ¿Cuánto tiempo toma la ejecución del algoritmo?
void ejemplo(int *T, int n)
{
int k=0;
for(int i=0; i<n; i++)
{
for(int j=0; j<T[i]; j++)
{
k=k+T[j];
}
}
}
contador
1
n+1
s+n
s
s
n
----------------3n + 3s + 2
Orden: O(n+s) O(Max(n,s))
Para cada valor de i la instrucción “k=k+T[j]” se ejecuta T[i] veces. El número total de veces que es
ejecutada “k=k+T[j]” es por tanto T[i] = s veces donde la sumatoria va de i=0 a i=n-1.
Si esta instrucción fuera seleccionada como barómetro, se concluiría que el algoritmo toma
un tiempo de ejecución de orden s. Un ejemplo simple es suficiente para convencernos de que
este no es el caso.
Supongamos que T[i] =1 cuando i tiene raíz cuadrada exacta y T[i] = 0 de lo contrario. En este
caso s=sqrt(n) como fácilmente puede comprobarse y de acuerdo al barómetro anterior
concluiríamos que el algoritmo es de orden O(sqrt(n)), Sin embargo, claramente el algoritmo toma
un tiempo de orden O(n) ya que cada elemento de T se procesa al menos 1 vez (en la condición
del ciclo for).
Esta circunstancia ocurre debido a que la instrucción “k=k+T[j]” no siempre se ejecuta en el ciclo
más interno, pero su condición sí.
El análisis detallado del algoritmo es como sigue: Sea a, el tiempo necesario para ejecutar una
vuelta del ciclo interior, incluyendo el tiempo consumido en el control del ciclo. Para ejecutar el
ciclo interior completamente para un valor de i dado se requiere un tiempo b+aT[i], donde b es una
constante que representa el tiempo de inicialización del ciclo. Este tiempo no es cero cuando T[i] =
0. A continuación, el tiempo necesario para ejecutar una vuelta del ciclo exterior es c+b+aT[i],
donde c es una nueva constante.
Finalmente, el algoritmo completo toma un tiempo d+[c+b+aT[i]], donde la sumatoria va desde i=0
hasta i=n-1. Cuando se simplifica, esta expresión lleva a (c+b)n+as+d. Por tanto, el tiempo
depende de dos parámetros independientes n y s y no puede ser expresado como una
función de sólo una de estas variables.
Esta situación es típica en algoritmos de manejo de grafos, donde el tiempo de ejecución depende
tanto del número de nodos como del número de enlaces. Volviendo a nuestro problema, podemos
expresar el orden de este algoritmo de dos maneras: O(n+s) ó O(max(n, s)).
02-2003
5
3004597 – Estructura de Datos – Modulo 12
Universidad Nacional de Colombia – Sede Medellín
ANEXOS
A REFERENCIAS
http://web.jet.es/jqc/progii.html. Pagina Web del curso Programación II de Ingeniería Informática de
la UNED. Jeronimo Quesada 1998.
B TALLER
Crear un algoritmo que calcule el n-ésimo término de la serie de Fibonacci y calcular su orden.
C CREDITOS
Editor: PhD. Fernando Arango
Colaboradores: Ing. Edwin Hincapie, Ms.C Francisco Moreno, Santiago Londoño, Alberto Jiménez,
Juan Carlos Hernández, Carlos Andrés Giraldo, Diego Figueroa.
02-2003
6
Documentos relacionados
Descargar