Tema 2
Tema 2
Tema 2
n
.
Sin embargo, la formula de De Moivre es de poca ayuda inmediata para el calculo
exacto de f
n
ya que conforme mas grande se hace n, mayor es el grado de precision
requerido para los valores de 5
1/2
y .
El algoritmo obtenido directamente de la definicion de sucesion de Fibonacci es el
siguiente,
function fib(n)
if n < 2 then return n
else return fib1(n-1) + fib1(n-2)
Este algoritmo es muy ineficiente porque recalcula los mismos valores muchas
veces. Por ejemplo, para calcular fib1(5) necesitamos los valores de fib1(4) y de
fib1(3); pero fib1(4) necesita a su vez fib1(3). Vemos que fib1(3) se calculara dos
veces, fib1(2), tres veces, fib1(1) cinco veces y fib1(0) tres veces. Efectivamente, el
tiempo requerido para calcular f
n
usando este algoritmo esta en el orden del valor de
f
n
, es decir, en el orden de
n
.
Para evitar tener que calcular muchas veces el mismo valor, es natural proceder
como a continuacion,
function fib2(n)
i := 1; j := 0
for k := 1 to n do j := i + j
i := j - i
return j
Este segundo algoritmo toma un tiempo en el orden de n, supuesto que contamos
cada adicion como una operacion elemental. Este es mucho mejor que el primer
algoritmo. Sin embargo, existe un tercer algoritmo que da con mucho una mejora del
segundo algoritmo aun mayor que la de este sobre el primero. El tercer algoritmo, que
al principio parece un poco misterioso, consume un tiempo del orden del logaritmo
de n y sera explicado mas adelante.
function fib3(n)
i := 1; j := 0; k := 0; h := 1
while n > 0 do
if n es impar then t := jh
j := ih +jk + t
i := ik + t
t := h ; h := 2kh + t; k := k + t; n := n div 2
return j
Si los algoritmos se implementan en Pascal, usando el enfoque hibrido, podemos
estimar el tiempo consumido por esas implementaciones de estos tres algoritmos
aproximadamente. Notando el tiempo consumido por fibi en el caso de tamao n por
t
i
(n), tenemos
t
1
(n) =
n -20
segundos
t
2
(n) = 15n microsegundos
t
3
(n) = (1/4)logn miliseg.
Se necesita un valor de n 10.000 veces mayor para hacer que fib3 consuma un
milisegundo extra de tiempo de computacion.
Algunas tecnicas para el diseo de algoritmos
Estamos siendo testigos privilegiados de una revolucion historica sin prece- dentes y
sin posibilidad de retorno al punto de partida: La de las Tecnologias de la
Informacion. Conforme crece el parque de ordenadores disponibles, los calculos mas
dificiles de efectuar se convierten en rutinas. A este respecto, la primera duda que hay
que despejar es la de la necesidad de tener que estudiar algoritmos, habida cuenta de
los prodigiosos adelantos que, por ejemplo en el aspecto de velocidad de calculo de
los ordenadores mas convencionales, estamos presenciando.
Un simple ejemplo, debido al Profesor G.B. Dantzig, nos servira para justificar este
aspecto. Consideremos el problema de asignar 70 hombres a 70 trabajos (un caso
sencillo del problema de asignacion). Una actividad consiste en asignar el hombre i-
esimo al trabajo j- esimo. Las restricciones son: a) Cada hombre debe ser asignado a
algun trabajo, y hay 70 de ellos. b) Cada trabajo debe ser ocupado, y tambien hay 70.
El nivel de una actividad es o cero, o uno, segun se use o no. Por tanto son 2x70, o
140, restricciones, y 70x70, es decir, 4900 actividades con sus correspondientes 4900
variables de decision 0-1. Evidentemente, hay 70! posibles soluciones diferentes o
formas de llevar a cabo las asignaciones. El problema consiste simplemente en
comparar una con otra, para todos los casos, y seleccionar aquella que sea major
frente a algun criterio previamente establecido.
Pero 70! es un numero muy grande, mayor que 10. Supongamos que tenemos un
ordenador, poco convencional por sus prestaciones, capaz de examinar un billon de
asignaciones por segundo. Si lo tuvieramos trabajando sobre las 70! asignaciones
desde el mismo instante del Big Bang, hace 15 billones de aos, hoy en dia aun no
habria terminado los calculos. Incluso si la Tierra estuviera cubierta de ordenadores de
esas caracteristicas, todos trabajando en paralelo, la res- puesta seguiria siendo no.
Pero si, sin embargo, dispusieramos de 10 Tierras, o 10 Soles, todos recubiertos con
ordenadores de velocidad del nano segundo, todos programados en paralelo,
trabajando desde el mismo instante del Big Bang hasta el del enfriamiento del Sol,
entonces quizas la respuesta fuera si.
Desde esta base, el primer enfoque que se presenta para el diseo de
algoritmos es el greedy. El metodo greedy quizas sea la tecnica de diseo mas directa
que se explica a lo largo del curso y, ademas, es aplicable a una gran variedad de
problemas, de ahi que el programa se inicie analizandolo. Esto se debe a su forma de
actuar que, en sintesis, es la siguiente. Gran parte de estos problemas se plantean de
modo que, de un input de tamao n, hemos de obtener cierto subconjunto que
satisfaga algunas restricciones. Un subconjunto satisfaciendo las restricciones se llama
factible. Lo que se quiere, entonces, es encontrar una solucion factible que
optimice una funcion objetivo dada. A la solucion factible que proporciona ese
optimo, se le llama solucion optima. Generalmente existe una forma obvia de
determinar las soluciones factibles, pero no las optimales. Cuando para resolver un
cierto problema, aplicamos un metodo greedy, caso de ser susceptible de ello, el
algoritmo resultante es lo que llamamos un algoritmo greedy.
Otra de las tecnicas de diseo es la comunmente conocida como divide y venceras.
Dada una funcion que hay que evaluar sobre un input de tamao n, esta tecnica nos
invita a dividir el input en k subconjuntos disjuntos, 1 < k n, de modo que podamos
contar con k subproblemas. Tras resolver estos subproblemas, hay que encontrar un
metodo para combinar sus soluciones en una unica solucion que corresponda al
problema global de partida. Si, a pesar de esto, aun los sub- problemas fueran grandes,
la estrategia divide y venceras puede volver a aplicarse.
En esta descripcion resumida de la tecnica hay que destacar que generalmente, los
subproblemas resultantes de un diseo divide y venceras, son del mismo tipo que el
problema inicial. En tales casos, la reaplicacion del metodo divide y venceras se hace
mediante el uso de procedimientos recursivos, con lo que se van generando cada vez
mas pequeos problemas del mismo tipo, e incluso se llegan a obtener subproblemas
de un tamao lo suficientemente pequeo, como para no tener que volver a dividirlos.
La aplicacion de esta tecnica a problemas concretos que sean adecuados a ella, hace
que los algoritmos resultantes, como con el anterior metodo, se denominen algoritmos
divide y venceras.
Otra importante tecnica de diseo de algoritmos es la conocida con el nombre de
Programacion Dinamica (PD), que esta especialmente indicada para usarse cuando la
solucion de un problema puede entenderse como el resultado de una sucesion de
decisiones. En este tipo de problemas, la sucesion optimal de decisiones pue- de
encontrarse eligiendo una decision cada vez de modo optimal, lo que tambien podria
hacerse con todos los problemas que son resolubles con un enfoque greedy, pero esta
forma de trabajar no puede aplicarse siempre porque, en general, esta metodologia
(basada solo en informacion local) no conduce a una solucion optimal global. Una
forma de resolver este inconveniente es evaluando todas las posibles sucesiones de
decisiones para, despues de ello, quedarnos con la mejor. La tecnica de la PD, a
menudo, reduce el numero de enumeraciones, para lo que se hace uso del bien
conocido Principio de Optimalidad de Bellman. Aunque los problemas sobre los que
puede aplicarse esta tecnica podrian resolverse tambien con el enfoque greedy, la
diferencia esencial que presenta, y sobre la que hay que insistir, es que el enfoque
greedy construye una unica sucesion de decisiones, mientras que la PD genera un
conjunto de tales sucesiones, en el que existe, y ademas siempre se encuentra, la
sucesion optimal.
Desde otro punto de vista, la solucion de muchos problemas supone manipular
arboles o grafos. A menudo, esta manipulacion nos exige que determinemos un vertice,
o un subconjunto de vertices, entre los datos de partida que satisfaga cierta
propiedad. La determinacion de tal subconjunto de vertices, puede llevarse a cabo de
forma sistematica examinando los vertices del conjunto dado, lo que frecuentemente
supone efectuar una busqueda. Cuando la busqueda necesariamente supone el
examen de cualquier vertice del conjunto de partida, la denominamos barrido. Las
tecnicas que se examinan, en relacion con estos problemas, se dividen en tres grandes
grupos. Por un lado estan los metodos de busqueda y barrido sobre grafos y arboles.
Por otro lado, las tecnicas backtracking y, finalmente, los metodos branch and bound,
cada uno de los cuales tiene caracteristicas propias que, mas adelante, se explicaran
con detalle.