Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Algoritmia
Guía de Práctica para el Curso de Técnicas de
Construcción de Programas
SEMESTRE 2008 - I
ALGORITMIA
1. INTRODUCCION
Es necesario, antes de comenzar el estudio de la algoritmia, entender que es lo que significan
y son los algoritmos. El termino algoritmo proviene del matemático persa al-Khowârizmî. Un
algoritmo puede ser definido como un conjunto finito de pasos finitos y bien establecidos
para la resolución de un problema. Los algoritmos pueden ser usados en diversas áreas
científicas en la actualidad, como la medicina, ingeniería, etc.; sin embargo, nosotros
estaremos interesados solo en los algoritmos que son implementados en un computador.
2. CONCEPTOS FUNDAMENTALES
Antes de adentrarnos más en el estudio de la algoritmia y la eficiencia de los algoritmos,
debemos conocer algunos conceptos los cuales son necesarios para comprender el contenido
de esta unidad
2.1. Algoritmia
Según Brassard y Bratley, quienes acuñaron el término “algoritmics” en 1988, “es el
estudio sistemático de las técnicas fundamentales utilizadas para diseñar y analizar
algoritmos eficientes”. Este estudio ha sido posteriormente ampliado en 1997,
considerando además que la determinación de la eficiencia de un algoritmo se podía
expresar en el tiempo requerido para la realización de la tarea computacional en
función del tamaño de la muestra e independientemente del ambiente en el que se
efectue.
2.2. Problemas y Ejemplares
a. Problema: Definimos como problema a lo que se trata de resolver mediante un
algoritmo. Por lo general, el problema objeto de un algoritmo, se encuentra en el
último nivel del diseño descendente de un problema más grande.
Ejm: Determinar los múltiplos de dos números positivos determinado.
b. Ejemplar: Un ejemplar es un caso particular dentro del dominio del problema.
Multiplos(4512,7411)
Ejm: Según el ejemplo anterior:
Multiplos(-8541,-4885)
Uno de los aspectos importantes entre las técnicas de construcción de programas es la
correctitud, y en base al cual tenemos que construir algoritmos que funcionen de
manera correctas con todos los casos del problema que queremos resolver. Para mostrar
que un algoritmo no es correcto solo basta con encontrar un ejemplar del problema
para el cual, el algoritmo no es capaz de encontrar una respuesta correcta. En base a lo
anterior podemos decir que un algoritmo puede rechazarse tomando como base un caso
Debemos tener presente que, mientras el tamaño del ejemplar crezca, el computador
tardará mas tiempo en resolver el problema. Es decir, el tamaño del ejemplar afecta
directamente a la eficiencia de un algoritmo dado. El análisis de eficiencia, en
general se hace para valores altos de tamaño de ejemplar.
Respecto al uso eficiente de los recursos computacionales, este suele medirse en función de
dos parámetros: El espacio, es decir, memoria que se utiliza, y el tiempo, lo que tarda en
ejecutarse. Ambos representan los costes que supone encontrar la solución a un problema
planteado mediante el desarrollo de un algoritmo. Dichos parámetros nos servirán además
para comparar algoritmos entre sí, permitiendo entre ellos determinar el más adecuado para
la solución del problema. Solo nos basaremos en la eficiencia temporal.
Ambas medidas son importantes, puesto que, si bien la primera nos ofrece estimaciones del
comportamiento de los algoritmos de forma independiente del ordenador en donde serán
implementados y sin necesidad de ejecutarlos, la segunda representa las medidas reales del
comportamiento del algoritmo. Estas medidas son funciones temporales de los datos de
entrada.
La unidad de tiempo a la que debe hacer referencia estas medidas de eficiencia no puede ser
expresada en segundos o en otra unidad de tiempo concreta, puesto a que no existe un
ordenador estándar al que puedan hacer referencia todas las medidas. Denotaremos por
T(n) el tiempo de ejecución de un algoritmo para una entrada de tamaño n.
Principio de Invarianza
Dado un algoritmo y dos implementaciones suyas I1 e I2, que tardan T1(n) y T2(n)
segundos respectivamente, el Principio de Invarianza afirma que existe una constante real c >
0 y un número natural n0 tales que para todo n ≥ n0 se verifica que T1(n) ≤ cT2(n).
Con esto podemos definir sin problemas que un algoritmo tarda un tiempo del orden de T(n)
si existe una constante real c>0 y una implementación I del algoritmo que tarda menos que
cT(n), para todo n de tamaño de entrada.
Dos factores a tener en cuenta siempre son la constante multiplicativa y el n0 para los que se
verifican las condiciones, pues si bien a priori un algoritmo de orden cuadrático es mejor que
un algoritmo de orden cubico, en el caso de tener dos algoritmos cuyos tiempos de ejecución
son 106n2 y 5n3, el primero solo sería mejor que el segundo para tamaños de la entrada
superiores a 200 000.
El caso mejor corresponde a la traza (secuencia de sentencias) del algoritmo que realiza
menos instrucciones. Análogamente, el caso peor corresponde a la traza del algoritmo que
realiza más instrucciones. Respecto al caso medio, corresponde a la traza del algoritmo que
realiza un número de instrucciones igual a la esperanza matemática de la variable aleatoria
definida por todas las posibles trazas del algoritmo para un tamaño de la entrada dado, con
las probabilidades de que éstas ocurran para esa entrada.
Es muy importante destacar que esos casos corresponden a un tamaño de la entrada dado,
puesto que es un error común confundir el caso mejor con el que menos instrucciones realiza
en cualquier caso, y por lo tanto contabilizar las instrucciones que hace para n = 1.
rápido en el sentido que nos interesa. También es posible distinguir entre los tiempos de
ejecución de las diferentes operaciones elementales, lo cual es necesario a veces por las
características específicas del ordenador (por ejemplo, se podría considerar que las
operaciones + y ÷ presentan complejidades diferentes debido a su implementación). Sin
embargo, en este texto tendremos en cuenta, a menos que se indique lo contrario, todas las
operaciones elementales del lenguaje, y supondremos que sus tiempos de ejecución son
todos iguales.
Para hacer referencia a los tres casos citados analizaremos un ejemplo concreto:
1 4 2 4! 2 1 6 2
1 $1
#
2
%
Tendríamos pues, que:
)
( +
1 '( 4 2+ 2, 2 1 3 3
& *
& *
En este caso, un examen más detallado de la función (¡y no de su nombre!) nos muestra
que tras su ejecución, la función devuelve la posición de un entero dado c dentro de un
vector ordenado de enteros, devolviendo 0 si el elemento no está en el vector. Lo que
acabamos de probar es que su caso mejor se da cuando el elemento está en la primera
posición del vector. El caso peor se produce cuando el elemento no está en el vector, y el
caso medio ocurre cuando consideramos equiprobables cada una de las posiciones en las
que puede encontrarse el elemento dentro del vector (incluyendo la posición especial 0,
que indica que el elemento a buscar no se encuentra en el vector).
SWITCH(C){
CASE a: S1; break
CASE b : S2; break;
DEFAULT : Sn; break;
}
IF (C)
S 1;
ELSE
S 2;
SENTENCIA_REPETITIVA(C){
S;
}
4. NOTACION ASINTOTICA
Una vez que hemos visto la forma de calcular el tiempo de ejecución T de un algoritmo,
nuestro propósito es intentar clasificar dichas funciones de forma que podamos compararlas.
Para ello, vamos a definir clases de equivalencia, correspondientes a funciones que “crecen
de la misma forma”.
como:
5. 67: 0 10, ∞|:; < =, > ? 0, :% < 0 @ 7 A >.B C % D
Intuitivamente, E < 5. indica que t está acotada superiormente por algún múltiplo
de f Nosotros estaremos interesados por la menor función f tal que t pertenezca a O(f).
5. 67: 0 10, ∞|:; < =, > ? 0, :% < 0 @ 7 C >.B C % D
Intuitivamente, E < Ω. indica que t está acotada inferiormente por algún múltiplo de
f. Normalmente estaremos interesados en la mayor función f tal que t pertenezca a
Ω(f), a la que denominaremos su cota inferior.
O lo que es igual a:
5. 67: 0 10, ∞|:; < =, ;, X ? 0, :% < 0 @ ;. A 7 A X.B C
% D
Intuitivamente, E < Θ. indica que t está acotada tanto superior como inferiormente
por múltiplos de f, es decir, que t y f crecen de la misma forma.
Para poder resolver este tipo de ecuaciones debemos encontrar una expresión no recursiva
de T, lo cual en algunos casos es algo complicado. Lo que trataremos en esta sección es
como podemos resolver algunos tipos de ecuaciones en recurrencia que se dan con mayor
frecuencia.
Donde los coeficientes son constantes. Se llama lineal porque la ecuación es lineal y
homogénea porque no tiene término independiente y homogéneo porque no tiene
término independiente de t.
% [ [ Y Z [ Z 0
% [ Z [ Z Y Z [ % 0
(s1,s2,…,sk), en cuyo caso, toda combinación lineal E ∑Z; es una solución
Puede ocurrir que las soluciones de la ecuación característica sean todas distintas
de la recurrencia, y para determinar los valores de las constantes habrá que resolver el
sistema formado interponiendo las condiciones iniciales.
[ $ ^ [ $ ) … [ $ Z^`
^ Z
;
;
^`
^`
Este caso puede ser generalizado de la siguiente forma. Si
,
) ,
a, … ,
Z son las raíces
de la ecuación característica de una ecuación en recurrencia homogénea, cada una de
multiplicidad mi, esto es, si la ecuación característica puede expresarse como :
^b ^b ^b
; ;) 0
ecuaciones:
; 2;) 2;a 1
; 4;) 8;a 2
Podemos escribir la ecuación de dos formas distintas. En primer lugar, para n+1
Que resulta ser una ecuación homogénea cuya solución, aplicando lo visto
3 $ 2
anteriormente, es:
F2Z G 4F2Z G 2Z
EZ 4EZ 2Z
EZ ; 2Z ) ;) 2Z
viene dada por la expresión:
; ) ;)
Deshaciendo el cambio que realizamos al principio, obtenemos que:
2) $
Calculando entonces las constantes a partir de las condiciones iniciales tenemos:
Por ejemplo, sea la ecuación ) /2 para n potencia de 2, n>1, con la
condición inicial T(1) = 1/3. Llamando EZ 2Z , la ecuación quedaría así:
Z $ 2Z >
Ecuación de recurrencia no homogénea, cuya ecuación característica asociada es (x-
Z ; 2Z ;) ;a >
2)(x-1)2 = 0. Por tanto:
2)
43
6. EJERCICOS PROPUESTOS
Recursividad
a. Implemente de forma recursiva e iterativa y obtenga la complejidad del algoritmo
del MCD recursivo e iterativo mediante notación asintótica O.
b. Implemente de forma recursiva e iterativa y obtenga la complejidad del algoritmo de
inversión de una cadena, mediante notación asintótica O.
c. Implemente de forma recursiva e iterativa y obtenga la complejidad de un algoritmo
que verifique que una si una cadena es simétrica o no, mediante notación O.
d. Obtener el T(n) y la complejidad usando notación asintótica O del siguiente
algoritmo:
q. Resolver la ecuación
1
# ;
%
Siendo T(0) = 0
Resolver la ecuación recurrente E E q s ;Z , # ? % siendo h C
r
r.
2, % C 1, > C 0, a y c reales positivos y n una potencia de b
Análisis de complejidad
s. Hacer un algoritmo recursivo para el calculo del n-ésimo numero de Fibonacci y
calcular el orden de su complejidad usando la notación O.
t. Encontrar el orden O del algoritmo :
max = 0
for i=1 to n
cont = 1
j = j+1
cont = cont+1
endwhile
if cont>max
max = cont
endif
endfor
u. Calcular el tiempo de ejecución en función del valor de n, del siguiente algoritmo:
procedure algoritmo(x: array[1..max, 1..max]de enteros, n:entero)
if n>1
for i=1 to n/2
for j=1 to n/2
a[i,j] = x[i,j]
b[i,j] = x[i,j+n/2]
c[i,j] = x[i+n/2,j]
endfor
endfor
algoritmo(a,n/2)
algoritmo(b,n/2)
algoritmo(c,n/2)
endif
end procedure
v. Dado el programa
i=1
while(# A X A )
j=1
if 1#, 1t u 1# 1, t
j = j+1
else
i = i+1
endif
endwhile
Calcular su O(f(n))