You are on page 1of 29

Captulo 2

Analisis de algoritmos
El an alisis de algoritmos es el campo de la algortmica que estudia la eciencia de los algoritmos: el tiempo
necesario para su ejecuci on y la cantidad de memoria que consumen. El estudio de la eciencia nos interesa:
para estimar los recursos computacionales (tiempo de proceso y espacio de memoria) que consume
un algoritmo en funci on de la talla de las instancias del problema que resuelve,
y para comparar dos algoritmos que resuelven un mismo problema de modo que podamos seleccionar
el m as eciente.
Centraremos el discurso en el consumo de tiempo y s olo al nal nos ocuparemos del consumo de espa-
cio. Hablaremos de complejidad o coste temporal y espacial para referirnos al consumo de estos recursos. Es tan importante estu-
diar la eciencia cuando
los computadores son ca-
da vez m as r apidos y la
memoria es cada vez m as
barata? La ((ley de Moore))
conjetura que la poten-
cia de los ordenadores se
duplica cada 18 meses.
Cabe pensar que el algorit-
mo que pueda resultar hoy
lento ser a alg un da su-
cientemente r apido. Pero
la ((ley de Moore)) tiene
una vigencia esperada de
unos 20 a nos: los lmites
del mundo fsico impiden
duplicar la velocidad ilimi-
tadamente. Y en cualquier
caso, hay problemas para
los que los algoritmos m as
ecientes tienen un coste
computacional que crece
tanto con la talla del prob-
lema que por mucho que
evolucionen los computa-
dores seguir an siendo ine-
cientes para instancias de
talla moderada.
Una primera idea consiste en estudiar el tiempo real necesario para resolver instancias concretas del
problema con una implementaci on particular del algoritmo ejecut andose en un sistema computador con-
creto. Surge entonces una primera cuesti on: con qu e lenguaje de programaci on hemos de implementar el
algoritmo? La respuesta es inmediata si el objetivo es la comparaci on de dos o m as algoritmos distintos:
el lenguaje de programaci on resulta indiferente si los dos algoritmos se implementan con el mismo. Pero
no basta con usar el mismo lenguaje para garantizar una comparaci on justa: hemos de ser cuidadosos y
realizar todas las implementaciones con el mismo grado de competencia y usando habilidades t ecnicas sim-
ilares. No sera justo implementar uno de los programas de forma directa y burda cuando otro se desarrolla
prestando atenci on a todo tipo de detalles.
En cualquier caso, los algoritmos son independientes de los lenguajes de programaci on y de los sistemas
computadores, as que el an alisis del tiempo y la memoria necesarios para la ejecuci on deberan ser ajenos a
estos elementos. C omo podemos obtener an alisis de tiempos de ejecuci on y consumo de memoria sin tener
en cuenta ((detalles)) como el computador o el lenguaje de programaci on usados? Un enfoque del an alisis
de algoritmos se centra en el estudio de la evoluci on asint otica del n umero de operaciones elementales y
del n umero de celdas de memoria en funci on de la talla del problema. Ciertas t ecnicas que estudiaremos
permiten establecer algunos resultados sobre el comportamiento de un algoritmo a partir de estimaciones
muy groseras sobre el n umero de operaciones que han de ejecutarse o el n umero de celdas de memoria
consumidas. Estudiaremos, fundamentalmente, cotas al coste en el mejor y el peor de los casos. Tambi en
consideraremos otras caractersticas del coste temporal y espacial de los algoritmos, como el coste promedio
o el coste amortizado.
2.1. Medicion de tiempos de ejecucion
Vamos a plantearnos, como primer objetivo, aprender a medir tiempos de ejecuci on de programas. Con-
sideremos un problema concreto: la b usqueda de un valor en un vector ordenado de enteros. Se trata de
una simplicaci on del problema de la b usqueda de un nombre en la gua telef onica. Nuestro problema se
enuncia as: ((dado un vector con n n umeros enteros positivos, diferentes y ordenados de menor a mayor,
encu entrese el ndice de un elemento de valor x y, si no est a, indquese)). Estudiaremos tres algoritmos
diferentes:
1. Una t ecnica de b usqueda secuencial que resulta evidentemente ineciente: recorrer el vector com-
pletamente y, al encontrar el valor x, memorizar su ndice para devolverlo al nal del recorrido del
vector. Denominaremos a esta t ecnica ((b usqueda secuencia naf)). B usqueda secuencial
naf: Naive sequential
search.
Apuntes de Algortmica 2-1
2.1 Medicion de tiempos de ejecucion 2004/09/24-15:52
2. Un renamiento de la t ecnica anterior: recorrer el vector hasta encontrar el valor x, momento en el
que se devuelve su ndice, o hasta llegar a un valor que permita concluir que el valor buscado no
est a presente, momento en el que detenemos la b usqueda y avisamos de este hecho. Esta t ecnica se
denominar a ((b usqueda secuencial)), sin m as. B usqueda secuencial:
Sequential search.
3. Y una t ecnica iterativa muy diferente que considera en cada instante la b usqueda en un ((subvector))
del vector original (y que inicialmente es el propio vector original). Con cada iteraci on se compara x
con el elemento central de un ((subvector)) y
si coinciden, nalizar la b usqueda devolviendo el ndice del elemento;
si x es menor, aplicar el mismo procedimiento al subvector izquierdo;
y si x es mayor, al derecho.
Si en alg un momento se pretende efectuar la b usqueda en un subvector vaco, el m etodo naliza
concluyendo que el elemento buscado no se encuentra en el vector. Llamaremos a este m etodo
((b usqueda binaria)). B usqueda binaria:
Binary search.
Implementemos los tres algoritmos con el lenguaje de programaci on Python. El vector ser a una lista y
el valor especial con el que indicamos que no se encontr o el valor buscado ser a None: Evidentemente, el primer
m etodo es poco razon-
able: ((deberamos)) abor-
tar la b usqueda tan pron-
to encontramos el elemen-
to buscado en el vec-
tor o estamos seguros de
que no est a en el vec-
tor, como hace el se-
gundo m etodo. No ob-
stante, se trata de un er-
ror relativamente frecuente
en programadores primeri-
zos, as que tiene cierto in-
ter es estudiar qu e repercu-
siones tiene su comisi on.
Python permite expresar
la b usqueda secuencial de
forma m as r apida medi-
ante el uso del m etodo
a.index(x), pero no sera
justo usar este m etodo en
una comparaci on, pues su
ejecuci on se realiza di-
rectamente en c odigo de
m aquina (generado a partir
de una rutina escrita en C)
y, por tanto, es signicati-
vamente m as r apida.
search.py
1 def naive sequential search(a, x):
2 index = None
3 for i in xrange(len(a)):
4 if x == a[i]:
5 index = i
6 return index
7
8 def sequential search(a, x):
9 if x < a[0]:
10 return None
11 for i in xrange(len(a)):
12 if x == a[i]:
13 return i
14 elif x < a[i]:
15 return None
16 return None
17
18 def binary search(a, x):
19 left, right = 0, len(a)
20 while left < right:
21 i = (right + left) / 2
22 if x == a[i]:
23 return i
24 elif x < a[i]:
25 right = i
26 else:
27 left = i + 1
28 return None
2.1.1. La funcion clock
Ocup emonos ya de la cuesti on t ecnica de la medici on del tiempo de ejecuci on. El m odulo time de la
colecci on de bibliotecas est andar de Python ofrece una funci on para la medici on de tiempos. Su nombre es Seg un la documentaci on,
la funci on clock de la bib-
lioteca est andar de Python
no funciona correctamente
en Microsoft Windows: no
mide el tiempo de CPU
transcurrido, sino el tiem-
po real transcurrido. Debe
tenerse en cuenta que am-
bos no se corresponden en
un sistema multitarea.
clock y devuelve el n umero de segundos (un valor en coma otante) que ha dedicado la CPU a la ejecuci on
del programa hasta el punto en que se efect ua la llamada a la funci on. Si efectuamos dos medidas de tiempo,
una antes de efectuar el c alculo y otra inmediatamente despu es, el tiempo transcurrido ser a la diferencia
entre las dos medidas.
2-2 Apuntes de Algortmica
c 2003, 2004 A. Marzal, M.J. Castro y P. Aibar 2 Analisis de algoritmos
1 from time import clock
2
3 t1 = clock()
4 acciones
5 t2 = clock()
6 t = t2 - t1
Las funciones de b usqueda dise nadas necesitan de vectores y valores de x concretos para su ejecuci on.
Asumamos, por el momento, un vector de tama no jo (pongamos que n = 10) cuyos elementos son enteros
no repetidos en un rango determinado (por ejemplo, en el rango [0..5n 1]) y que buscamos un elemento
cualquiera de los presentes en el vector:
timing1.py
1 from search import naive sequential search
2 from time import clock
3 from random import seed, randrange, sample
4
5 # Generaci on de un vector aleatorio.
6 seed(0) # Semilla del generador de n umeros aleatorios.
7 n = 10 # Talla del vector.
8 a = sorted(sample(xrange(5*n), n)) # Vector ordenado de n valores aleatorios en [0..5n1] sin repetici on.
9 x = a[ randrange(n) ] # Selecci on de un elemento al azar.
10
11 # Ejecuci on con medici on de tiempo.
12 t1 = clock()
13 index = naive sequential search(a, x)
14 t2 = clock()
15 t = t2 - t1
16
17 print Tiempotranscurrido: %.8fsegundos % t

Este es el resultado de su ejecuci on con el int erprete de Python 2.4a en un ordenador con procesador El m odulo random
ofrece funciones para la
generaci on de n umeros
aleatorios. La semilla se
inicializa con seed. La
funci on sample selecciona
una muestra aleatoria
sin reemplazamiento de
una poblaci on descrita
mediante una secuencia
o iterable; por ejemplo,
sample([1,2,3], 2)
devuelve, al azar, una
de estas listas: [1, 2],
[2, 1], [2, 3], [3, 2],
[1, 3] o [3, 1]. La
llamada randrange(m)
genera un n umero entero
aleatorio en el rango
[0..m 1] con una dis-
tribuci on uniforme. La
funci on sorted devuelve
una lista ordenada de
menor a mayor con los
mismos valores que
contiene la secuencia
que se proporciona como
argumento.
Intel Pentium 4 a 2.6 GHz, 512 Mb de memoria RAM y sistema operativo Linux con n ucleo 2.6:
Tiempo transcurrido: 0.00000000 segundos
C omo puede tardar cero segundos la ejecuci on de la funci on? Hay un problema con la funci on clock
que no hemos comentado: la resoluci on del reloj est a en el entorno de las cent esimas de segundo, as que
no podemos medir eventos que transcurran en menos de una cent esima.
2.1.2. Ejecucion repetida un n umero jo de veces
Podemos superar este problema si repetimos la ejecuci on del programa un n umero suciente de veces:
timing2.py
1 from search import naive sequential search
2 from time import clock
3 from random import seed, randrange, sample
4
5 # Generaci on de un vector aleatorio.
6 seed(0)
7 n = 10
8 a = sorted(sample(xrange(5*n), n))
9 x = a[ randrange(n) ]
10
11 # Ejecuci on repetida con medici on de tiempo.
12 r = 100000
13 t1 = clock()
14 for i in xrange(r):
15 index = naive sequential search(a, x)
Apuntes de Algortmica 2-3
2.1 Medicion de tiempos de ejecucion 2004/09/24-15:52
16 t2 = clock()
17 for i in xrange(r):
18 pass
19 t3 = clock()
20
21 t = ((t2 - t1) - (t3 - t2)) / r
22 print Tiempomedioporejecucion: %.8fsegundos % t
N otese que el c alculo del tiempo transcurrido se ha complicado un poco. El objetivo del bucle entre los
instantes t2 y t3 es poder descontar el tiempo que supone la ejecuci on del bucle que repite el c alculo. He
aqu el resultado:
Tiempo medio por ejecucion: 0.00000430 segundos
Como se puede ver, hemos entrado en la escala de lo que el reloj puede medir. C omo determinamos
el n umero de repeticiones necesarias para entrar en la escala de lo que el reloj puede medir en funci on del
tama no del vector? Si hacemos pruebas con vectores de menor tama no, el n umero de repeticiones deber a ser
mayor. Podramos determinar este n umero para cada tama no de vector por prueba y error, pero resultara
extremadamente pesado.
2.1.3. Ejecucion repetida hasta superar una cantidad de tiempo
Una t ecnica que permite solucionar este problema consiste en repetir la ejecuci on de la rutina durante al
menos una cantidad de tiempo preestablecida. Si la rutina efect ua el c alculo muy r apidamente, tendr a que
repetirse un gran n umero de veces para conseguir que transcurra el tiempo necesario. Si, por contra, la
rutina se ejecuta en un lapso de tiempo grande, una sola ejecuci on ser a suciente para tener una medici on
razonablemente precisa. Este programa expresa esta idea en Python:
timing3.py
1 from search import naive sequential search
2 from time import clock
3 from random import seed, randrange, sample
4
5 tmin = 2 # Tiempo mnimo de ejecuci on: dos segundos.
6
7 seed(0)
8 n = 10
9 a = sorted(sample(xrange(5*n), n))
10 x = a[ randrange(n) ]
11
12 t1 = t2 = clock()
13 r = 0
14 while t2 - t1 < tmin:
15 index = naive sequential search(a, x)
16 t2 = clock()
17 r += 1
18
19 t3 = t4 = clock()
20 aux = 0
21 while aux < r:
22 t4 - t3
23 t4 = clock()
24 aux += 1
25
26 t = ((t2 - t1) - (t4 - t3)) / r
27 print Tiempomedioporejecucion: %.8fsegundos % t
Tiempo medio por ejecucion: 0.00000432 segundos
2-4 Apuntes de Algortmica
c 2003, 2004 A. Marzal, M.J. Castro y P. Aibar 2 Analisis de algoritmos
Las lneas 1924 tienen por objeto estimar el tiempo que requiere la ejecuci on del c odigo extra que
hemos a nadido para aplicar la t ecnica de medici on, pues contienen el mismo n umero de operaciones (un
bucle que se itera el mismo n umero de veces y en el que cada iteraci on supone efectuar una comparaci on,
una resta, una llamada a clock, una asignaci on y un incremento.
Pasemos a ocuparnos del estudio de la evoluci on del tiempo de ejecuci on con la talla de las instancias del
problema. Pero, antes, deteng amonos a considerar qu e entendemos exactamente por talla de una instancia.
2.2. Talla de una instancia
Podemos denir informalmente la talla de una instancia del problema como la cantidad de memoria nece-
saria para describirla. Dicha especicaci on pasa por la denici on de una codicaci on, es decir, una cor-
respondencia entre los datos y cadenas de smbolos de un determinado alfabeto. La ocupaci on espacial
guarda relaci on con el n umero de dichos smbolos.
Un n umero entero y positivo n puede codicarse, por ejemplo, con un alfabeto unario, como {1},
formando una cadena con n repeticiones del mismo smbolo. En el alfabeto binario {0, 1}, el mismo n umero
podra codicarse en binario natural. El n umero de smbolos necesarios para codicar n con la primera
codicaci on es s
1
(n) = n. La segunda codicaci on s olo requiere s
2
(n) = 1+lgn smbolos. La diferencia
en la cantidad de memoria necesaria si usamos una u otra codicaci on es enorme, como se puede ver al
comparar la columnas 2 y 3 en la tabla 2.1.
n s
1
(n) s
2
(n)
0 0 1
1 1 1
2 2 2
3 3 2
4 4 3
5 5 3
6 6 3
7 7 3
8 8 4
9 9 4
10 10 4
11 11 4
.
.
.
.
.
.
.
.
.
1 023 1 023 10
.
.
.
.
.
.
.
.
.
1 048 575 1 048 575 20
.
.
.
.
.
.
.
.
.
1 099 511 627 775 1 099 511 627 775 40
.
.
.
.
.
.
.
.
.
Tabla 2.1: Smbolos necesarios para expresar un
entero positivo n usando una codicaci on unaria
(s
1
(n)) y una codicaci on en binario natural (s
2
(n)).
Hay una gran diferencia entre el n umero de smbolos necesarios para codicar un entero positivo con un
alfabeto unario (base 1) y para codicarlo en binario natural (base 2). No ser a mejor a un usar un sistema
de numeraci on en una base mayor? El n umero de smbolos necesarios para expresar un entero positivo n en
base 10 es s
10
(n) = 1+log
10
n. Como log
10
n = lgn/lg10, el n umero de cifras necesarias en base 2 s olo
es unas 3.32 veces mayor que en base 10. Si bien se trata de una reducci on del n umero de smbolos, esta se
mantiene constante para cualquier valor de n. En cambio, la ratio entre el n umero de smbolos necesarios
al codicar en base 1 y en binario natural no es constante.
Llamamos codicaci on razonable a toda codicaci on que permite representar la informaci on con un
n umero de smbolos proporcional al requerido con el sistema binario. N otese que es necesario que el
alfabeto tenga al menos dos smbolos para que sea razonable.
Si usamos una codicaci on razonable y medimos la talla de la instancia en funci on del n umero de
smbolos necesarios para expresarla, diremos que hacemos uso del criterio del coste logartmico: un
n umero entero positivo puede expresarse con un n umero de smbolos proporcional a su logaritmo; los
elementos de un conjunto con n valores pueden expresarse con un n umero de smbolos proporcional al
logaritmo de n.
Apuntes de Algortmica 2-5
2.3 Perles de ejecucion: evolucion del tiempo de ejecucion en funcion de la talla de las instancias 2004/09/24-15:52
Los computadores actuales suelen usar una cantidad de memoria ja para representar valores escalares.
Es habitual, por ejemplo, usar 32 o 64 bits para codicar los n umeros enteros sin signo. Cualquier can-
tidad que podamos expresar con la cantidad de bits correspondiente ocupa el mismo n umero de celdas
de memoria. El criterio del coste uniforme es una simplicaci on inspirada en este hecho y que asume
que toda cantidad num erica puede expresarse usando una unica celda de memoria. As, un vector con n
enteros positivos, por ejemplo, ocupar a n celdas de memoria. El criterio del coste uniforme ayuda notable-
mente a simplicar los an alisis de coste que efectuaremos, as que lo adoptaremos en adelante salvo cuando
indiquemos lo contrario.
Cabe decir, nalmente, que la talla de un problema no tiene por qu e venir determinada por un unico
par ametro. Si multiplicamos dos matrices, una de dimensi on p q y otra de dimensi on q r, la talla del
problema depende de los tres valores: p, q y r.
2.3. Perles de ejecucion: evolucion del tiempo de ejecucion en
funcion de la talla de las instancias
Hemos de decidir qu e entendemos por ((talla de una instancia)) en el contexto del problema de b usqueda
que estamos resolviendo por tres procedimientos diferentes. Asumiremos el criterio del coste uniforme,
as que no nos preocuparemos por la magnitud de los valores del vector ni, en consecuencia, del n umero
de bits necesarios para codicar cada uno de ellos. La talla del problema, que denotaremos con la letra n,
vendr a determinada unicamente por el tama no del vector sobre el que se efect ua la b usqueda. Estudiaremos
la evoluci on del tiempo de ejecuci on con el par ametro n.
timing4.py
1 from search import naive sequential search
2 from time import clock
3 from random import seed, randrange, sample
4
5 seed(0)
6
7 tmin = 1 # Tiempo mnimo de ejecuci on: un segundo.
8
9 for n in xrange(1, 11):
10 a = sorted(sample(xrange(5*n), n))
11 x = a[ randrange(n) ]
12
13 t1 = t2 = clock()
14 r = 0
15 while t2 - t1 < tmin:
16 index = naive sequential search(a, x)
17 t2 = clock()
18 r += 1
19
20 t3 = t4 = clock()
21 aux = 0
22 while aux < r:
23 t4 - t3
24 t4 = clock()
25 aux += 1
26
27 t = ((t2 - t1) - (t3 - t2)) / r
28 print Tiempomedioporejecucionparan= %d: %.8fsegundos % (n, t)
Tiempo medio por ejecucion para n=1: 0.00000197 segundos
Tiempo medio por ejecucion para n=2: 0.00000228 segundos
Tiempo medio por ejecucion para n=3: 0.00000258 segundos
Tiempo medio por ejecucion para n=4: 0.00000274 segundos
Tiempo medio por ejecucion para n=5: 0.00000292 segundos
Tiempo medio por ejecucion para n=6: 0.00000330 segundos
Tiempo medio por ejecucion para n=7: 0.00000369 segundos
2-6 Apuntes de Algortmica
c 2003, 2004 A. Marzal, M.J. Castro y P. Aibar 2 Analisis de algoritmos
Tiempo medio por ejecucion para n=8: 0.00000378 segundos
Tiempo medio por ejecucion para n=9: 0.00000410 segundos
Tiempo medio por ejecucion para n=10: 0.00000432 segundos
La gura 2.1 muestra gr acamente el resultado de esta medici on de tiempos de ejecuci on de naive_sequential_search.
Se puede apreciar que una lnea recta se ajustara razonablemente bien a los puntos de la gr aca: el tiempo
necesario crece proporcionalmente con la longitud del vector.
n
0 1 2 3 4 5 6 7 8 9 10
t
(
e
n
s
e
g
u
n
d
o
s
)
0e+00 0.0e+00
1.0e-06
2.0e-06
3.0e-06
4.0e-06
5.0e-06
Figura 2.1: Medici on de tiempo de ejecu-
ci on de naive_sequential_search para vec-
tores de tama no entre 1 y 10.
Si repetimos el experimento con la funci on sequential_search obtenemos los resultados que se muestran
gr acamente en la gura 2.2. Se puede apreciar una mayor variabilidad en el tiempo medido. Ello se debe
a que el m etodo de b usqueda secuencial es sensible a la ubicaci on en el vector del valor buscado: por
ejemplo, acaba m as r apidamente si el elemento buscado ocupa las primeras posiciones y tarda m as si ocupa
las ultimas.
n
0 1 2 3 4 5 6 7 8 9 10
t
(
e
n
s
e
g
u
n
d
o
s
)
0e+00 0.0e+00
1.0e-06
2.0e-06
3.0e-06
4.0e-06
5.0e-06
Figura 2.2: Medici on de tiempo de ejecu-
ci on de sequential_search para vectores de
tama no entre 1 y 10.
Podemos efectuar una medida de tiempo para cada una de las posibles ubicaciones del elemento bus-
cado. El resultado se muestra en la gura 2.3. El tiempo mnimo para cada valor de n corresponde a la
b usqueda del valor que ocupa la primera posici on del vector. Es su mejor caso. El tiempo m aximo corre-
sponde a la b usqueda del valor que est a en ultima posici on, y es el peor caso al que se enfrenta la rutina
(equivalente, en tiempo de ejecuci on, a buscar un valor mayor que el del ultimo elemento del vector).
Podemos acotar superior e inferiormente cada medida de tiempo con sendas funciones de n, como se
muestra en la gura 2.4. Una funci on constante constituye una cota inferior ajustada y una lnea recta acota
superiormente, tambi en de forma ajustada, los tiempos m aximos para cada valor de n.
Podemos repetir el experimento para el m etodo de b usqueda secuencial naf. La gura 2.5 muestra el
resultado. Podemos destacar que, a diferencia de lo que ocurre con la b usqueda secuencial, el tiempo para
el mejor de los casos se acota superior e inferiormente por sendas lneas recta (no constante).
Si realizamos el mismo experimento con el m etodo de b usqueda binaria obtenemos los resultados que
se muestran en la gura 2.6. En este caso, la cota inferior sigue siendo una constante, pero la cota superior
m as ajustada es una funci on logartmica.
Apuntes de Algortmica 2-7
2.4 Comparacion de perles de ejecucion 2004/09/24-15:52
n
0 1 2 3 4 5 6 7 8 9 10
t
(
e
n
s
e
g
u
n
d
o
s
)
0e+00 0.0e+00
1.0e-06
2.0e-06
3.0e-06
4.0e-06
5.0e-06
Figura 2.3: Medici on de tiempo de eje-
cuci on de sequential_search para vectores
de tama no comprendido entre 1 y 10. Para
cada vector buscamos todos sus elementos,
uno con cada ejecuci on.
n
0 1 2 3 4 5 6 7 8 9 10
t
(
e
n
s
e
g
u
n
d
o
s
)
0e+00 0.0e+00
1.0e-06
2.0e-06
3.0e-06
4.0e-06
5.0e-06
Figura 2.4: En trazo discontinuo, cotas
superior e inferior para las medidas de
tiempo con sequential_search y las ejecu-
ciones con vectores de tallas comprendidas
entre 1 y 10.
2.4. Comparacion de perles de ejecucion
La gura 2.7 permite comparar las cotas superior e inferior que hemos mostrado en las gr acas de las g-
uras 2.4 y 2.6. A tenor de la gr aca, parece que sequential_search sea m as eciente que binary_search.
Pero si atendemos a la tendencia de la cota superior del coste, es posible que las dos curvas se crucen y que
binary_search pase a ser m as eciente a partir de cierto valor de n. Comprob emoslo. La gura 2.8 muestra
los resultados de la medici on de tiempos para sequential_search y binary_search. Se puede apreciar (gu-
ra 2.9) que para valores de n superiores a 15, el m etodo binary_search presenta un comportamiento mejor
que sequential_search para el peor de los casos. Este comportamiento es tanto mejor cuanto mayor es el
valor de n.
Estamos comparando los algoritmos por el tiempo que necesitan para resolver el peor caso que se les
plantea para cada valor de n. Es este un buen criterio de comparaci on? Si nuestro programa debe garantizar
un tiempo de respuesta determinado, es obvio que s: si acotamos el tiempo m aximo que necesita para
resolver un problema de tama no n, sabremos si un algoritmo es o no es adecuado. Pero no es el unico criterio
que podemos considerar. Como hemos visto, los m etodos naive_sequential_search y sequential_search
presentan id entico comportamiento para el peor de los casos. Pero es obvio que resulta m as adecuado el
m etodo sequential_search, pues, adem as, presenta un mejor comportamiento en el mejor de los casos.
Hay una alegaci on que hacer a la comparaci on basada en el coste en el peor (y mejor) de los casos, y si
el peor caso para cada valor de n corresponde a una instancia muy improbable? No sera mejor seleccionar
el m etodo que se comporta mejor por t ermino medio? En principio, el tiempo promedio se obtiene ejecu-
tando el programa sobre diferentes instancias de igual talla y calculando la media del tiempo de ejecuci on.
Pero hay un problema: dada una talla , qu e instancias usamos para estimar la media? Diferentes instancias
proporcionar an diferentes valores del tiempo promedio para esa talla. Si, por ejemplo, seleccionamos in-
stancias en los que el n umero buscado aparece en las primeras posiciones obtendremos un tiempo promedio
menor que si escogemos instancias en las que el n umero buscado aparece en las ultimas posiciones. Para
efectuar una estimaci on v alida debi eramos seleccionar instancias siguiendo una distribuci on de probabili-
dad similar a la que se da en la explotaci on del algoritmo. En nuestro caso, supondremos que hay id entica
probabilidad de que la b usqueda del algoritmo secuencial se detenga en cualquier punto del vector.
La gura 2.10 muestra, adem as de las cotas inferior y superior al tiempo de ejecuci on de sequen-
tial_search y binary_search, el tiempo promedio para cada valor de n. El coste temporal promedio de
2-8 Apuntes de Algortmica
c 2003, 2004 A. Marzal, M.J. Castro y P. Aibar 2 Analisis de algoritmos
n
0 1 2 3 4 5 6 7 8 9 10
t
(
e
n
s
e
g
u
n
d
o
s
)
0e+00 0.0e+00
1.0e-06
2.0e-06
3.0e-06
4.0e-06
5.0e-06
Figura 2.5: Medici on de tiempo de eje-
cuci on de naive_sequential_search para
vectores de tama no entre 1 y 10. Las
lneas discontinuas acotan superior e infe-
riormente los valores de tiempo obtenidos
al ejecutar el programa de b usqueda sobre
cada uno de los elementos del vector.
n
0 1 2 3 4 5 6 7 8 9 10
t
(
e
n
s
e
g
u
n
d
o
s
)
0e+00 0.0e+00
1.0e-06
2.0e-06
3.0e-06
4.0e-06
5.0e-06
Figura 2.6: Medici on de tiempo de eje-
cuci on de binary_search para vectores de
tama no entre 1 y 10. Para cada tama no de
vector se efect ua la b usqueda de cada uno
de sus elementos. Las curvas acotan supe-
rior e inferiormente los valores obtenidos.
n
0 1 2 3 4 5 6 7 8 9 10
t
(
e
n
s
e
g
u
n
d
o
s
)
0e+00 0.0e+00
1.0e-06
2.0e-06
3.0e-06
4.0e-06
5.0e-06
Figura 2.7: Comparaci on entre cotas su-
perior e inferior para las ejecuciones de se-
quential_search (trazo discontinuo) y bi-
nary_search (trazo continuo) para vectores
de talla comprendida entre 1 y 10.
n
0 10 20 30 40 50 60 70 80 90 100
t
(
e
n
s
e
g
u
n
d
o
s
)
0e+00 0.0e+00
5.0e-06
1.0e-05
1.5e-05
2.0e-05
2.5e-05
n
0 10 20 30 40 50 60 70 80 90 100
Figura 2.8: A la izquierda, resultado de la medici on de tiempo de ejecuci on de sequential_search para vectores
de tama nos comprendidos entre 1 y 100. A la derecha, dem para binary_search. La escala para el tiempo es la
misma en las dos guras.
Apuntes de Algortmica 2-9
2.4 Comparacion de perles de ejecucion 2004/09/24-15:52
n
0 10 20 30 40 50 60 70 80 90 100
t
(
e
n
s
e
g
u
n
d
o
s
)
0e+00 0.0e+00
5.0e-06
1.0e-05
1.5e-05
2.0e-05
2.5e-05
Figura 2.9: Comparaci on entre cotas su-
perior e inferior para las ejecuciones de se-
quential_search (trazo discontinuo) y bi-
nary_search (trazo continuo) sobre vec-
tores de talla entre 1 y 10.
n
0 10 20 30 40 50 60 70 80 90 100
t
(
e
n
s
e
g
u
n
d
o
s
)
0e+00 0.0e+00
5.0e-06
1.0e-05
1.5e-05
2.0e-05
2.5e-05
Figura 2.10: En trazo grueso se muestra
el tiempo promedio para las ejecuciones de
sequential_search (lnea discontinua) y bi-
nary_search (lnea continua), y en trazo -
no, las respectivas cotas superior e inferior.
sequential_search es, bajo este supuesto de equiprobabilidad, la semisuma del coste para el mejor y para el
peor de los casos. No ocurre lo mismo con la b usqueda binaria. M as adelante estudiaremos analticamente El tiempo promedio no
siempre coincide con la
semisuma del tiempo en el
mejor y en el peor de los
casos, aunque en este caso
ocurra as.
este comportamiento.
Podemos observar que tambi en en tiempo promedio resulta m as eciente el m etodo binary_search
cuando n es sucientemente grande: el tiempo promedio de sequential_search se puede ajustar bien con
una lnea recta mientras que el de binary_search se describe mejor con una funci on logartmica.
Cabe destacar que hemos supuesto que la probabilidad de que busquemos cualquiera de los elementos
del vector es id entica. En una aplicaci on pr actica, no obstante, puede que no sea el caso y, por tanto, que el
coste promedio manieste un comportamiento diferente.
Del estudio realizado podemos sacar algunas conclusiones:
La complejidad temporal relaciona el tiempo necesario para resolver una instancia del problema con
la talla del problema.
El tiempo de ejecuci on no es funci on de la talla en el sentido de que no siempre existe un unico
valor de tiempo asociado a la resoluci on de cualquier instancia con una talla determinada (ni siquiera
descontando los errores de precisi on en la medida). Hay un rango de valores posibles para el tiempo
de ejecuci on en funci on de la talla. El rango viene jado por el tiempo de ejecuci on en el mejor y
peor de los casos.
Cabe esperar que el tiempo de ejecuci on crezca con la talla del problema. No necesariamente
ocurre as si comparamos el tiempo de ejecuci on de instancias particulares, pues algunas resultan
m as ((favorables)) que otras. Por regla general observamos esta tendencia si consideramos el tiempo
de ejecuci on en el peor de los casos o en promedio.
El tiempo de ejecuci on promedio puede utilizarse como criterio de comparaci on entre algoritmos,
pero en tal caso hemos de prestar atenci on a la distribuci on de probabilidad de las instancias para
cada valor de la talla estudiado.
Un programa puede resultar m as eciente que otro para instancias de talla peque na, pero dejar de
serlo cuando nos enfrentamos a instancias de talla sucientemente grande. Los ordenadores suelen
2-10 Apuntes de Algortmica
c 2003, 2004 A. Marzal, M.J. Castro y P. Aibar 2 Analisis de algoritmos
resultar ecaces cuando abordamos problemas de gran talla, por lo que resulta relevante centrar el
estudio en el comportamiento del tiempo de ejecuci on en valores sucientemente grandes de la talla
del problema.
2.5. Conteo de pasos
Las t ecnicas de medici on de tiempo presentan un gran inconveniente: deben efectuarse a partir de una
implementaci on concreta del algoritmo. Por otra parte, las medidas obtenidas son imprecisas. Vamos a
plantear un m etodo que sacrica el detalle para dar una visi on m as basta del coste temporal, pero que puede
resultar suciente para efectuar comparaciones. Una ventaja fundamental del m etodo que proponemos
ahora es que no requiere que dispongamos de un implementaci on concreta del algoritmo. Pretendemos
alcanzar as independencia del sistema computador y del lenguaje de programaci on. Nos basaremos en
el conteo de pasos. Un paso es una instrucci on o conjunto de instrucciones cuyo tiempo de ejecuci on
est a acotado por un valor constante. Consideremos estas dos asignaciones en el lenguaje de programaci on
Python:
1 a = 1
2 b = a * 10 + 1
Ciertamente la segunda ha de tardar m as tiempo en ejecutarse que la primera: comporta una multipli-
caci on, una suma y una asignaci on, cuando la primera es una simple asignaci on; pero ambas sentencias se
ejecutan en tiempo constante, as que cada una de ellas se considera un paso.
N otese que el concepto de paso es independiente del lenguaje de programaci on. Cada una de las dos
sentencias del ejemplo se computa como un paso, y seguiran contando como pasos individuales si estu-
vieran escritas en un lenguaje de programaci on diferente.
2.5.1. Conteo de pasos en algoritmos iterativos
Estudiemos ahora este fragmento de programa:
1 s = 0
2 for i in xrange(n):
3 s += i
La primera lnea se considera un paso, evidentemente. La segunda lnea es especial: su ejecuci on se repite
n veces, as que el tiempo de ejecuci on depende del valor de n. No podemos acotar su tiempo de ejecuci on
por una constante: siempre habr a un valor sucientemente grande de n que haga que el tiempo de ejecuci on
sea superior a cualquier valor constante jado de antemano. No podemos considerar que esta lnea cuente,
pues, como un solo paso, sino n. La tercera lnea, por s sola, es un paso, pero se encuentra en un bucle que
se ejecuta n veces. Hemos de contarla, pues, como n pasos. El n umero de pasos que requiere la ejecuci on
de las tres lneas es 1+n+n = 2n+1.
Veamos un fragmento de programa algo m as complejo:
1 s = 0
2 for i in xrange(n):
3 for j in xrange(n):
4 s += i
La tercera lnea comporta la ejecuci on de n iteraciones de un bucle, pero el bucle completo se ejecuta
n veces (est a dentro de otro bucle), as que su ejecuci on aporta un n umero total de n
2
pasos. La lnea
4 aporta otros n
2
pasos. El n umero de pasos que supone la ejecuci on de este fragmento de programa es
1+n+n
2
+n
2
= 2n
2
+n+1.
Este otro fragmento requiere algo de calma al efectuar el conteo:
1 s = 0
2 for i in xrange(n):
3 for j in xrange(3):
4 s += i
Al contener un bucle dentro de otro, se puede pensar que el n umero de pasos ser a un polinomio de grado
2 con n, pero no es as. El bucle indexado con j se ejecuta tres veces. El n umero de pasos que supone
Apuntes de Algortmica 2-11
2.5 Conteo de pasos 2004/09/24-15:52
la ejecuci on de las lneas 3 y 4 es, pues, 6. Y el n umero de pasos que resulta de ejecutar el fragmento de
programa es 1+n+3n+3n = 7n+1.
El conteo de pasos recoge suciente informaci on sobre un algoritmo como para que sepamos si el tiem-
po crece en proporci on directa a la talla del problema, o a su cuadrado, o a su logaritmo, etc. Puede resultar
suciente para una primera comparaci on entre dos algoritmos: sabemos que si el tiempo de ejecuci on de
un algoritmo crece linealmente ser a peor a partir de determinado valor de la talla del problema que otro que
crezca en proporci on a su logaritmo.
Hay cierta arbitrariedad en la denici on del concepto de paso. Estas lneas, por ejemplo, han sido
computadas antes como dos pasos:
1 a = 1
2 b = a * 10 + 1
Pero, de acuerdo con la denici on, tambi en pueden computarse como un solo paso: el tiempo de ejecuci on
de ambas lneas puede acotarse por una constante (por ejemplo, una que sea el doble de la constante que
la usada cuando cada lnea se comput o como un paso). No hay problema en ello. El conteo de pasos no
pretende estimar con exactitud el tiempo de ejecuci on.
Contemos el n umero de pasos del m etodo de b usqueda secuencial naf, cuya implementaci on en Python
recordamos ahora:
1 def naive sequential search(a, x):
2 index = None
3 for i in xrange(len(a)):
4 if a[i] == x:
5 index = i
6 return index
La primera de las lneas es una cabecera de funci on. Por s sola no parece comportar coste alguno.
Podemos considerar, eso s, que cuando se llame a la funci on se ejecutar a un paso, pues las acciones propias
de la llamada a funci on requieren un tiempo total constante: crear un registro de activaci on en la pila de
llamadas a funci on, apilar la direcci on del vector a y del valor x, reservar espacio para variables locales,
guardar en pila la direcci on de memoria desde la que se efect ua la llamada y saltar a una nueva direcci on
de memoria. La asignaci on de la lnea 2 es un paso. El bucle efect ua tantas iteraciones como tama no tiene
el vector a. Si denotamos dicho tama no con n, el n umero de pasos que comporta el bucle en s es n. La
comparaci on del valor de a[i] con el valor de x se efect ua n veces, as que la lnea 4 aporta a nuestra cuenta
otros n pasos. La lnea 5 s olo se ejecuta una vez (estamos suponiendo que los n elementos del vector son
diferentes), as que s olo aporta un paso, aunque est e dentro de un bucle. Finalmente, la devoluci on del
ndice cuenta como un solo paso, aunque supone la ejecuci on de algunas acciones relativamente complejas
(desapilar el registro de activaci on y saltar a la direcci on desde la que se efectu o la llamada a la funci on).
El n umero total de pasos es, pues, 1+1+n+n+1+1 = 2n+4.
Procedimientos como naive_sequential_search permiten encontrar una relaci on entre la talla del vector,
n, y el n umero de pasos. No siempre es as. Consideremos el otro m etodo de b usqueda secuencial:
1 def sequential search(a, x):
2 if x < a[0]:
3 return None
4 for i in xrange(len(a)):
5 if x == a[i]:
6 return i
7 elif x < a[i]:
8 return None
9 return None
El n umero de pasos depende ahora de la talla del vector y del valor buscado. Podemos, eso s, acotar
superior e inferiormente el n umero de pasos: en el mejor caso se ejecutan las lneas 1 (llamada a la funci on),
2 y 3 y en el peor caso se ejecuta una vez las lneas 1, 2 y 9 y n veces las lneas 4, 5 y 7. As pues, el n umero
de pasos es siempre mayor o igual que 3 y menor o igual que 3n+3. Como se ve, en ese caso no es posible
hablar de una funci on que exprese el coste temporal en funci on de n y hemos de pasar a considerar el
n umero de pasos en el mejor y en el peor de los casos. Podemos proporcionar ambos costes para describir
el comportamiento del algoritmo.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . PROBLEMAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2-1 Calcula el n umero de pasos, en funci on del entero n, de estos programas Python. Si hay un mejor
y peor caso, presenta una cota inferior y una superior, respectivamente, para el n umeros de pasos.
2-12 Apuntes de Algortmica
c 2003, 2004 A. Marzal, M.J. Castro y P. Aibar 2 Analisis de algoritmos
a) 1 def f 1(n):
2 i = n
3 s = 0
4 while i > 0:
5 s += i
6 i -= 1
7 return s
b) 1 def f 2(n):
2 n = int(raw input())
3 s = n
4 return s
c) 1 def f 3(n):
2 v = range(n)
3 s = 0
4 for i in v:
5 s += i
6 return s
d) 1 def f 4(n):
2 for i in range(n):
3 if i == 10:
4 break
5 return i
e) 1 def f 5(n):
2 for i in xrange(n):
3 if i == 10:
4 break
5 return i
f) 1 def f 6(n):
2 return range(n)
g) 1 def f 7(n):
2 for i in xrange(n):
3 aux = range(n)
4 for j in xrange(n):
5 del aux[0]
h) 1 def f 8(n):
2 for i in xrange(n):
3 if i == 10:
4 continue
5 return i
i) 1 def f 9(n):
2 v = range(n)
3 s = 0
4 for i in v:
5 for j in v:
6 s += i*j
7 return s
j) 1 def f 10(n):
2 for i in xrange(n):
3 print i
4 if i == n/2:
5 return
2-2 Te mostramos a continuaci on tres procedimientos para ordenar, de menor a mayor, los elementos
de un vector. Calcula el n umero de pasos que comporta su ejecuci on en funci on del tama no del vector que
se suministra como par ametro:
a) Ordenaci on por selecci on.
1 def selection sort(a):
2 for i in xrange(n-1):
3 k = i
4 for j in xrange(i+1, n):
5 if a[j] < a[k]: k = j
6 a[i], a[k] = a[k], a[i]
b) Ordenaci on por el m etodo de la burbuja.
1 def bubblesort(a):
2 for i in xrange(len(a)):
3 for j in xrange(len(a)-1-i):
4 if a[j] > a[j+1]:
5 a[j], a[j+1] = a[j+1], a[j]
c) Ordenaci on por inserci on.
1 def insertion sort(a):
2 for i in xrange(1, n):
3 x = a[i]
4 j = i-1
5 while j >= 0 and x < a[j]:
6 a[j+1] = a[j]
7 j -= 1
8 a[j+1] = x
Apuntes de Algortmica 2-13
2.5 Conteo de pasos 2004/09/24-15:52
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Analicemos el n umero de pasos con el que binary_search resuelve el problema de la b usqueda. Recorde-
mos su implementaci on en Python:
1 def binary search(a, x):
2 left, right = 0, len(a)
3 while left < right:
4 i = (right + left) / 2
5 if a[i] == x:
6 return i
7 elif a[i] > x:
8 right = i
9 else:
10 left = i + 1
11 return None
Nuevamente el n umero de pasos depende del valor buscado, y no s olo de n. Con el n de simplicar el
estudio supondremos que n es potencia de 2. Si el elemento buscado ocupa la posici on n/2 del vector, se
ejecutan 6 pasos. Qu e ocurre en otro caso? Depende: si el valor del elemento en la posici on n/2 es mayor
que x, se busca en un vector con n/2 elementos; si es menor, se busca en un vector con n/21 elementos.
Consideremos el peor de los casos: que nos toque buscar en el vector con n/2 elementos. Aplicando un
razonamiento similar y considerando tambi en el peor caso, tendremos que continuar la b usqueda en un
vector de n/4 elementos. Y as hasta llegar a considerar un vector con un solo elemento. Esto ocurrir a tras
repetir el proceso lgn veces. Cada una de las iteraciones supone la ejecuci on de 5 pasos, excepto la ultima,
que ejecuta 4. A estos pasos hay que sumar el de la llamada a la funci on y el de la inicializaci on de las
variables left y right. El n umero de pasos ejecutados en el peor de los casos es, pues, 5lgn+2.
2.5.2. Conteo de pasos en algoritmos recursivos
Los algoritmos que hemos visto son de naturaleza iterativa, es decir, se basan en la repetici on de un c alculo
controlado por un bucle (for o while). Tambi en consideraremos en el texto algoritmos recursivos, esto es,
algoritmos que expresamos con funciones que, al resolver una instancia, se llaman a s mismas sobre otras
instancias de menor talla. El procedimiento de b usqueda binaria puede expresarse recursivamente:
1 def recursive binary search(a, x, left, right):
2 if left < right:
3 i = (right + left) / 2
4 if a[i] == x:
5 return i
6 elif a[i] > x:
7 return binary search(a, x, left, i)
8 else:
9 return binary search(a, x, i+1, right)
10 else:
11 return None
Hemos de invocar el m etodo recursivo con recursive_binary_search(a, x, 0, len(a)) para buscar el
elemento x en a. Cuando n vale cero, el algoritmo requiere la ejecuci on de 3 pasos. Para valores de n
mayores, nuestro algoritmo presenta un mejor y un peor caso. El n umero de pasos de un algoritmo recursivo
puede expresarse, en primera instancia, mediante una ecuaci on recursiva. Supongamos que n es potencia
de 2 y mayor que cero. El mejor caso requiere la ejecuci on de 5 pasos. El peor caso obliga a ejecutar un
n umero de pasos que podemos expresar mediante esta ecuaci on recursiva:
f (n) =
_
6+ f (n/2), si n > 1;
5, si n = 1.
El t ermino superior de la ecuaci on es el denominado ((t ermino)) o ((caso general)) (es una expresi on recursi-
va) y el inferior dene su ((caso base)) (cuando naliza la recursi on).
Las ecuaciones recursivas admiten, en muchos casos, una expresi on cerrada. Decimos que la expresi on
cerrada es la soluci on de la ecuaci on recursiva. En la secci on B.12 de los ap endices se resumen algunas
2-14 Apuntes de Algortmica
c 2003, 2004 A. Marzal, M.J. Castro y P. Aibar 2 Analisis de algoritmos
t ecnicas para la resoluci on de ecuaciones recursivas. Una de las t ecnicas com unmente utilizadas es el
desplegado, y consiste en sustituir reiteradamente la parte izquierda de la ecuaci on por su parte derecha con
objeto de identicar un patr on:
f (n) = 6+ f (n/2) = 6+6+ f (n/4) = 6+6+6+ f (n/8) = = 6k + f (n/2
k
).
El desplegado naliza cuando n/2
k
= 1, es decir, cuando k = lgn, pues entonces llegamos al caso base de
la ecuaci on recursiva:
f (n) = 6k + f (n/2
k
) = 6lgn+5.
El m etodo del desple-
gado permite formular
una hip otesis sobre la
expresi on cerrada de
una ecuaci on recursiva,
pero no constituye una
demostraci on de que
es soluci on. Podemos
demostrar por inducci on
que lo es, pero en aras de
brevedad lo dejamos como
ejercicio al lector.
El n umero de pasos de la versi on recursiva de la b usqueda binaria es, pues, comparable al de la versi on
iterativa.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . PROBLEMAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2-3 Calcula el n umero de pasos, en funci on del entero n, de estos programas Python. Si hay un mejor
y peor caso, presenta una cota inferior y una superior, respectivamente, para el n umeros de pasos.
a) 1 def f 1(n):
2 if n == 1:
3 return 1
4 else:
5 return f 1(n-1) * 2
b) 1 def f 2(n):
2 if n == 1:
3 return 1
4 else:
5 return f 2(n-2) + 1
c) 1 def f 3(n):
2 if n == 1:
3 return 1
4 else:
5 return min(f 3(n-1)*2, f 3(n-2)*3)
d) 1 def f 4(n):
2 if n == 1:
3 return 1
4 else:
5 s = 0
6 for i in xrange(n):
7 s += i
8 return f 4(n/2) + s
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.6. Notacion asintotica
Dada la denici on tan laxa del concepto de paso, un an alisis excesivamente detallado puede no merecer la
pena. Cuando decimos que el coste en el peor de los casos es 2n+4 o 3n+3, qu e signican exactamente
sus diferentes constantes? Hay cierta arbitrariedad en su valor, pues dependen de qu e criterio adoptamos al
considerar qu e contamos como un solo paso. Podramos decir que el n umero de pasos es c
1
n+c
0
, para c
1
y c
0
constantes, y estaramos proporcionando la misma informaci on fundamental: que el n umero de pasos
crece en proporci on directa al valor de n.
La denominada notaci on asint otica es una forma de describir el comportamiento de las funciones de
coste temporal (o espacial) en funci on de la talla que resulta muy concisa y, a la vez, simplica los an alisis
de coste. El objetivo de la notaci on asint otica es agrupar las funciones de N en R
0
en una serie de
familias atendiendo a su crecimiento. Cada familia de funciones se caracteriza por estar acotada superior
y/o inferiormente para valores de n sucientemente grandes por una funci on afectada por cierta constante
positiva.
Empezamos por estudiar un agrupamiento de las funciones atendiendo a su cota superior.
2.6.1. Cota asintotica superior: notacion ((orden de))
Dada una funci on g : N R
0
, denotamos con O(g(n)) al conjunto de funciones f (n) tales que existen
una constante positiva c y un entero n
0
que hacen que f (n) c g(n) para n n
0
:
O(g(n)) ={ f : N R
0
| c > 0, n
0
> 0 : f (n) c g(n), n n
0
}. (2.1)
La expresi on O(g(n)) se lee como ((orden de g(n))) y f (n) O(g(n)) se lee tanto diciendo (( f (n) En ingl es se dice ((big-
oh of f (n))), es decir, ((o
may uscula de f (n))).
pertenece a O(g(n)))) como (( f (n) es O(g(n)))). En ocasiones usamos la expresi on f (n) = O(g(n)) para
indicar que f (n) O(g(n)). Si f (n) O(g(n)) decimos que g(n) es una cota superior asint otica de f (n)
Cota superior asint otica:
Asymptotic upper bound.
y que f (n) es ((orden de g(n))).
Apuntes de Algortmica 2-15
2.6 Notacion asintotica 2004/09/24-15:52
La gura 2.11 ilustra la idea de que una funci on f (n) sea orden de otra, g(n): existe un valor c tal que
inicialmente f (n) puede ser mayor o menor que c veces g(n), pero a partir de cierto valor n
0
, f (n) es menor
o igual que c g(n).
n
c g(n)
f (n) O(g(n))
n
0
Figura 2.11: f (n) es O(g(n)) porque, para
n mayor o igual que un n
0
dado y para un c
positivo determinado, f (n) c g(n).
Unos ejemplos ayudar an a entender el concepto de ((orden de)):
La funci on f (n) = n +1, por ejemplo, pertenece a O(n), pues siempre hay un valor n
0
y un valor c
para los que n +1 c n si n n
0
. Consideremos, por ejemplo, n
0
= 1 y c = 2: n +1 es menor o
igual que 2n para todo valor de n mayor o igual que 1.
La funci on f (n) = 5n+12 tambi en es O(n). Se observa 5n+12 6n para todo n mayor o igual que
12.
La funci on f (n) = 4n
2
+2n +1 es O(n
2
). El valor de f (n) es menor o igual que 5n
2
para todo n
mayor o igual que 3.
Vamos a presentar una serie de resultados que nos permitir an clasicar r apidamente una funci on de N
en R
0
en el orden al que pertenece. El primero de ellos nos permite calcular inmediatamente el orden de
un polinomio:
Teorema 2.1 Si f (n) = a
m
n
m
+ +a
1
n
1
+a
0
es un polinomio de grado m, entonces f (n) O(n
m
).
Demostraci on.
f (n) = a
m
n
m
+ +a
1
n
1
+a
0
n
0
|a
m
|n
m
+ +|a
1
|n
1
+|a
0
|n
0
(|a
m
| + +|a
1
| +|a
0
|)n
m
,
para n 1. Si escogemos c =|a
m
| + +|a
1
| +|a
0
| y n
0
= 1 tenemos f (n) O(n
m
).
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . PROBLEMAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2-4 Calcula el orden de estas funciones:
a) f (n) = 3n+2.
b) f (n) = 100n+6.
c) f (n) = 10n
2
4n+12.
d) f (n) = 2n
2
+1000000n+16.
e) f (n) = 10n
10
+4n
4
+2.
f) f (n) = 2.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Teorema 2.2 (Regla de los m aximos) Dadas dos funciones f , g : N R
0
, se cumple O( f (n) +g(n)) =
O(max( f (n), g(n))).
Demostraci on. Obviamente, f (n) +g(n) = min( f (n), g(n)) +max( f (n), g(n)) y 0 min( f (n), g(n))
max( f (n), g(n)). As pues,
max( f (n), g(n)) f (n) +g(n) 2max( f (n), g(n)). (2.2)
Consideremos cualquier funci on t(n) O( f (n) +g(n)). Sea c una constante tal que t(n) c( f (n) +g(n))
para todo n sucientemente grande. Por (2.2), t(n) c(max( f (n), g(n))) para alg un valor de c y los mismos
valores de n, as que O( f (n) +g(n)) O(max( f (n), g(n))).
2-16 Apuntes de Algortmica
c 2003, 2004 A. Marzal, M.J. Castro y P. Aibar 2 Analisis de algoritmos
En el sentido contrario, consideremos cualquier funci on t(n) O(max( f (n), g(n))). Existe una con-
stante c

tal que t(n) c

(max( f (n), g(n))), para todo n sucientemente grande. Tambi en por (2.2), tenemos
t(n) c

( f (n) +g(n)), as que O(max( f (n), g(n))) O( f (n) +g(n)).


La regla de los m aximos nos permite simplicar funciones que son suma de varios t erminos al per-
mitir considerar unicamente el mayor de ellos. Una funci on como f (n) = n
3
+nlgn, por ejemplo, es
O(max(n
3
, nlgn)) = O(n
3
). La regla de los m aximos no se limita a funciones que son suma de otras dos:
se puede generalizar a cualquier n umero de sumandos.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . PROBLEMAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2-5 Son ciertas las siguientes armaciones?
a) 3n+2 O(n).
b) 100n+6 O(n).
c) 10n
2
+4n+2 O(n
2
).
d) 10n
2
+4n+2 O(n
3
).
e) 6 2
n
+n
2
O(2
n
).
f) 6 2
n
+n
2
O(n
100
).
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Presentamos a continuaci on (sin demostraci on) algunos resultados y propiedades interesantes a la hora
de determinar el orden de una funci on. En todos ellos suponemos que las funciones tienen como dominio
N y como rango R
0
:
Transitividad. f (n) O(g(n)) g(n) O(h(n)) f (n) O(h(n)).
Reexividad. f (n) O( f (n)).
Regla de la constante. O(c f (n)) = O( f (n)).
Regla del producto. f
1
(n) O(g
1
(n)) f
2
(n) O(g
2
(n)) f
1
(n) f
2
(n) O(g
1
(n) g
2
(n)).
Si una funci on f (n) pertenece a O(n), por ejemplo, tambi en pertenece a O(n
2
), ya que O(n) O(n
2
).
No obstante, se debe procurar siempre indicar el orden de una funci on con su cota m as ajustada. As, de
f (n) = n+1 no decimos que es O(n
2
) (aunque, evidentemente, lo es), sino que es O(n).
Hay una relaci on de inclusi on entre las diferentes familias de funciones: Evidentemente, esta rela-
ci on no es exhaustiva.
O(1) O(logn) O(

n) O(n) O(nlogn) O(n


2
) O(n
3
) O(2
n
) O(n
n
).
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . PROBLEMAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2-6 Determina el orden de estas funciones:
1) f (n) = 4.
2) f (n) = 0.1.
3) f (n) = 108374.
4) f (n) = n.
5) f (n) = 10n.
6) f (n) = 0.028764n.
7) f (n) =n.
8) f (n) = n+3.
9) f (n) = 2n+1093842.
10) f (n) = n
2
.
11) f (n) = 4n
2
.
12) f (n) = n
2
+n.
13) f (n) = 8n
2
+n.
14) f (n) = 0.1n
2
+2n+1.
15) f (n) = 20n
3
n
2
+1000n.
16) f (n) = n
6
+n
3
+10n.
17) f (n) = n
100
+n
99
.
18) f (n) = 2
n
.
19) f (n) = 2
n
+n
100
.
20) f (n) = 3
n
+2
n
.
21) f (n) = lgn.
22) f (n) = logn.
23) f (n) = n+lgn.
24) f (n) = nlogn.
25) f (n) = n
2
+nlogn.
26) f (n) = nlogn
2
.
27) f (n) = n
2
lgn.
28) f (n) = n
2
+n
2
lnn.
29) f (n) = lnn
2
.
30) f (n) = nlgn
3
.
31) f (n) =
_
3n+2, si n par;
8n
2
+n, si n impar.
32) f (n) =
_
3n+2, si n par;
8n+n, si n impar.
33) f (n) =
_
3n+2, si n < 10;
n
2
+n, si n 10.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Las funciones que pertenecen a cada familia de funciones tienen adjetivos que las identican, como se
muestra en la tabla 2.2. As, decimos que el coste temporal de un algoritmo es lineal cuando su tiempo de
ejecuci on o n umero de pasos es O(n) y c ubico cuando es O(n
3
). De hecho, abusando del lenguaje decimos
que el algoritmo es de tiempo lineal o c ubico, respectivamente.
Apuntes de Algortmica 2-17
2.6 Notacion asintotica 2004/09/24-15:52
Sublineales
Constantes O(1)
Logartmicas O(logn)
Polilogartmicas O(log
k
n)
O(

n)
Lineales O(n)
Superlineales
O(nlogn)
Polin omicas
Cuadr aticas O(n
2
)
C ubicas O(n
3
)
Exponenciales
O(2
n
)
O(n
n
)
Tabla 2.2: Nombre que
reciben las funciones
pertenecientes a algunos
ordenes.
2.6.2. Cota asintotica inferior: notacion ((omega))
As como O() proporciona una cota superior asint otica para una funci on, () proporciona una cota infe-
rior asint otica. (g(n)) es el conjunto de funciones f (n) tales que existen una constante positiva c y un
entero n
0
que hacen que f (n) c g(n) para n n
0
:
(g(n)) ={ f : N R
0
| c > 0, n
0
> 0 : f (n) c g(n), n n
0
}. (2.3)
De una funci on f (n) que pertenece a (g(n)) se dice que ((es (g(n)))) y se lee ((es omega de g(n))).
La gura 2.12 ilustra la idea de que una funci on f (n) sea (g(n)).
n
c g(n)
f (n) (g(n))
n
0
Figura 2.12: f (n) es (g(n)): a partir de
cierto valor n
0
de n, f (n) siempre es mayor o
igual que c veces g(n), para alg un valor posi-
tivo de c.
En ocasiones usamos la notaci on f (n) =(g(n)) para expresar f (n) (g(n)). Por ejemplo, la funci on
f (n) = n+1 es (n), ya que n+1 n para todo n 1 y c = 1.
Hay un teorema an alogo al teorema 2.1:
Teorema 2.3 Si f (n) = a
m
n
m
+a
m1
n
m1
+ +a
1
n +a
0
es un polinomio de grado m, con a
m
> 0, en-
tonces f (n) (n
m
).
Demostraci on.
f (n) = a
m
n
m
+a
m1
n
m1
+a
1
n
1
+a
0
n
0
a
m
n
m
|a
m1
|n
m1
|a
1
|n
1
|a
0
|n
0
= a
m
n
m
(|a
m1
|n
m1
+ +|a
1
|n
1
+|a
0
|n
0
).
Hemos demostrado antes () que |a
m1
|n
m1
+ +|a
1
|n
1
+|a
0
|n
0
O(n
m1
), as que existen un n
0
y una
constante c tales que, para todo n mayor que n
0
, se cumple |a
m1
|n
m1
+ +|a
1
|n
1
+|a
0
|n
0
c n
m1
.
Por tanto,
f (n) a
m
n
m
(|a
m1
|n
m1
+ +|a
1
|n
1
+|a
0
|n
0
) a
m
n
m
c n
m1
para todo n n
0
.
Tenemos que
f (n) a
m
n
m
c n
m1
c

n
m
si c

observa 0 < c

< a
m
y para todo n mayor que n

0
=
c
a
m
c

. Por tanto, f (n) (n


m
).
2-18 Apuntes de Algortmica
c 2003, 2004 A. Marzal, M.J. Castro y P. Aibar 2 Analisis de algoritmos
Al expresar la de una funci on debe usarse la cota m as ajustada posible, es decir, si decimos f (n) =
(g(n)), usamos la funci on g(n) m as ((grande)) posible.
He aqu algunas propiedades equivalentes a otras que ya hemos visto al estudiar la notaci on ((orden de)):
Regla de los m aximos. ( f (n) +g(n)) =(max( f (n), g(n))).
Transitividad. f (n) (g(n)) g(n) (h(n)) f (n) (h(n)).
Reexividad. f (n) ( f (n)).
Regla de la constante. (c f (n)) =( f (n)).
Regla del producto. f
1
(n) (g
1
(n)) f
2
(n) (g
2
(n)) f
1
(n) f
2
(n) (g
1
(n) g
2
(n)).
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . PROBLEMAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2-7 Determina la cota inferior asint otica de las funciones del ejercicio 2-6.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.6.3. Cota asintotica superior e inferior: notacion ((zeta))
Cuando una funci on f (n) es a la vez O(g(n)) y (g(n)), decimos que es (g(n)) (que se lee ((zeta de
g(n)))).
(g(n)) =O(g(n)) (g(n)). (2.4)
Una funci on f (n) que pertenece a (g(n)) est a asint oticamente acotada superior e inferiormente,
por sendas funciones proporcionales a g(n) a partir de un valor determinado de n (v ease la gura 2.13):
(g(n)) ={ f (n) | c
1
, c
2
> 0, n
0
> 0 : c
1
g(n) f (n) c
2
g(n), n n
0
}. (2.5)
En ocasiones usamos f (n) =(g(n)) para expresar f (n) (g(n)) y decimos que (( f es zeta de g)).
n
c
2
g(n)
c
1
g(n)
f (n) (g(n))
n
0
Figura 2.13: f (n) es (g(n)) porque es
O(g(n)) y (g(n)).
Con se induce una relaci on de equivalencia sobre las funciones de N en R
0
. Cada conjunto (g(n))
es una clase de equivalencia. Las clases de equivalencia reciben el nombre de clases de complejidad.
Evidentemente, y a tenor de los teoremas 2.1 y 2.3, si f (n) = a
m
n
m
+a
m1
n
m1
+ +a
1
n+a
0
es un
polinomio de grado m, con a
m
> 0, entonces f (n) (n
m
).
Las siguientes propiedades son de inter es:
Transitividad. f (n) (g(n)) g(n) (h(n)) f (n) (h(n)).
Reexividad. f (n) ( f (n)).
Simetra traspuesta (o regla de la dualidad). f (n) (g(n)) g(n) ( f (n)).
Regla de la constante. (c f (n)) =( f (n)).
Regla del producto. f
1
(n) (g
1
(n)) f
2
(n) (g
2
(n)) f
1
(n) f
2
(n) (g
1
(n) g
2
(n)).
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . PROBLEMAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2-8 Determina ((la zeta)) de las funciones del ejercicio 2-6.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Apuntes de Algortmica 2-19
2.7 Expresion del coste temporal con la notacion asintotica 2004/09/24-15:52
2.6.4. Expresiones aritmeticas con cotas
Sabemos que si f (n) O(h
1
(n)) y g(n) O(h
2
(n)), para f y g funciones de N en R
0
, la suma f (n)+g(n)
es O(max(h
1
(n), h
2
(n))). En ocasiones usaremos expresiones como O(h
1
(n)) +O(h
2
(n)) para denotar el
orden de la suma de las funciones f y g, que es O(h
1
(n)) +O(h
2
(n)) = O(max(h
1
(n), h
2
(n))). N otese
que no se trata de ecuaciones, pues llegaramos a un absurdo si deducimos de O(n) +O(n) = O(n) que
O(n) = 0.
2.7. Expresion del coste temporal con la notacion asintotica
Habamos visto que el n umero de pasos que comporta la ejecuci on de naive_sequential_search era f (n) =
2n +4. Se trata de una funci on O(n) y (n), es decir, (n). Decimos que naive_sequential_search es
un algoritmo de coste temporal lineal (o simplemente ((lineal)), si se sobreentiende que hablamos del coste
temporal).
El b usqueda secuencial sequential_search presentaba un cote temporal comprendido entre 3 (mejor de
los casos) y 3n+3 (peor de los casos). De la primera funci on podemos decir que es (1), y de la segunda,
que es (n). No obstante, del peor de los casos s olo suele ofrecerse su cota superior asint otica y del mejor
de los casos, su cota inferior asint otica. As, decimos que sequential_search es (1) y O(n). N otese
que estamos extendiendo el uso de la notaci on asint otica al coste temporal en s, cuando este no es una
funci on. Se trata de un uso com un de la notaci on asint otica: la notaci on ((orden de)) se usa generalmente
para acotar superiormente el coste temporal en el peor de los casos, y la notaci on ((omega)) para acotar
inferiormente el coste temporal en el mejor de los casos. Siguiendo este proceder, podemos decir que el
m etodo binary_search es (1) y O(lgn).
Por regla general resulta m as informativo el coste en el peor de los casos. Muchas veces, al describir
el coste temporal de un algoritmo, se omite su coste temporal en el mejor de los casos (o si cota asint otica
inferior). S olo cuando la cotas asint oticas inferior y superior coinciden, se indica explcitamente con el
uso de la notaci on . Podramos resumir y sintetizar la informaci on considerada m as relevante en los tres
algoritmos estudiados as:
naive_sequential_search es (n).
sequential_search es O(n).
binary_search es O(lgn).
La expresi on de costes mediante su cota superior (y posiblemente inferior) no s olo sintetiza la informa-
ci on esencial: resulta, adem as, util a la hora de simplicar los c alculos necesarios para efectuar el coste.
Anotamos el aporte al coste global de cada una de las lneas de naive_sequential_search:
1 def naive sequential search(a, x): # (1) por la llamada.
2 index = None # Asignaci on (1)
3 for i in xrange(len(a)): # Bucle que se ejecuta (n) veces.
4 if a[i] == x: # Comparaci on (1) que se repite (n) veces.
5 index = i # Asignaci on (1) que se ejecuta una sola vez.
6 return index # (1) por n de rutina y devoluci on de valor.
No se cuentan los pasos individuales, sino la contribuci on al coste asint otico global, que es independi-
ente de factores constantes. El coste global es (1) +(1) +(n) +(n) +(1) +(1), que es (n).
Analicemos el coste de sequential_search:
1 def sequential search(a, x): # (1).
2 if x < a[0]: # (1).
3 return None # (1), pero cero veces o una, o sea, O(1).
4 for i in xrange(len(a)): # Bucle que se ejecuta cero o n veces: O(n) pasos.
5 if x == a[i]: # Comparaci on (1) que se ejecuta O(n) veces.
6 return i # (1), pero cero o una vez: O(1).
7 elif x < a[i]: # Comparaci on (1), que se ejecuta O(n) veces.
8 return None # (1), pero cero o una vez: O(1).
9 return None # (1), pero cero o una vez: O(1).
El coste temporal global es (1) +(1) +O(1) +O(n) +O(n) (1) +O(1) +O(n) (1) +O(1) +O(1),
o sea, O(n).
Y, nalmente, analicemos el coste temporal de binary_search:
2-20 Apuntes de Algortmica
c 2003, 2004 A. Marzal, M.J. Castro y P. Aibar 2 Analisis de algoritmos
1 def binary search(a, x):
2 left, right = 0, len(a) # (1)
3 while left < right: # O(lgn) veces.
4 i = (right + left) / 2 # (1), ejecutado O(lgn) veces.
5 if a[i] == x: # (1), ejecutado O(lgn) veces.
6 return i # (1), ejecutado a lo sumo una vez: O(1).
7 elif a[i] > x: # (1), ejecutado O(lgn) veces.
8 right = i # (1), ejecutado O(lgn) veces.
9 else:
10 left = i + 1 # (1), ejecutado O(lgn) veces.
11 return None # (1), a lo sumo una vez.
Operando de modo similar a como ya hemos hecho obtenemos un coste temporal O(lgn).
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . PROBLEMAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2-9 Analiza el coste temporal asint otico de los algoritmos del ejercicio 2-2.
2-10 La siguiente funci on calcula el producto de dos matrices, A, de dimensi on pq, y B, de dimensi on
qr:
1 def matrix product(A, B):
2 p, q, r = len(A), len(A[0]), len(B[0])
3 C = [ [0]*r for i in xrange(p) ]
4 for i in xrange(p):
5 for j in xrange(r):
6 C[i][j] = sum( A[i][k]*B[k][j] for k in xrange(q) )
7 return C
Acota asint oticamente, superior e inferiormente, el coste temporal del algoritmo.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.8. Una comparacion de costes de diferentes clases de equiva-
lencia
Decimos que naive_sequential_search y sequential_search son lineales, y que binary_search es logartmi-
co. Qu e implica, a efectos pr acticos, que el coste temporal sea logartmico, lineal, exponencial, etc.?
Resulta interesante tener cierta intuici on sobre lo que supone clasicar un algoritmo por su coste temporal
en el peor de los casos como perteneciente a un orden determinado. Lo mejor ser a que comparemos algunas
gr acas de crecimiento. Empecemos por los crecimientos sublineales y lineal, cuyas gr acas se muestran
en la gura 2.14.
c
0
c
1
logn
c
2

n
c
3
n
n
t
Figura 2.14: Gr acas de crecimiento
de funciones sublineales y lineal.
A la vista de la gura, reexionemos sobre el crecimiento de algunas funciones de coste y las implica-
ciones que tienen al evaluar la eciencia de un algoritmo:
Un algoritmo de coste constante ejecuta un n umero de instrucciones acotado por una constante inde-
pendiente de la talla del problema.
Un algoritmo que soluciona un problema en tiempo constante es lo ideal: a la larga es mejor que
cualquier algoritmo de coste no constante.
Apuntes de Algortmica 2-21
2.8 Una comparacion de costes de diferentes clases de equivalencia 2004/09/24-15:52
El coste de un algoritmo logartmico crece muy lentamente conforme n crece. Por ejemplo, si resolver
un problema de talla n = 10 tarda 1 s, puede que tarde 2 s en resolver un problema 10 veces m as
grande (n = 100) y, en tal caso, s olo 3 s en resolver uno 100 veces mayor (n = 1000). Cada vez que
el problema es 10 veces m as grande, se incrementa en 1 s el tiempo necesario (el factor exacto que
incrementa el tiempo en un microsegundo depende de constantes como la base del logaritmo).
Un algoritmo cuyo coste es (

n) crece a un ritmo superior que otro que es (logn), pero no llega


a presentar un crecimiento lineal. Cuando la talla se multiplica por 4, el coste se multiplica por 2.
Analicemos ahora los crecimientos lineal y superlineales que se muestran gr acamente en la gura 2.15.
c
3
n
n
t
c
4
nlogn
c
5
n
2
c
6
n
3
c
7
2
n
c
8
n
n
Figura 2.15: Gr acas de crecimiento
de funciones lineal y superlineales.
Un algoritmo (nlogn) presenta un crecimiento del coste ligeramente superior al de un algoritmo
lineal. Por ejemplo, si tardamos 10s en resolver un problema de talla 1 000, tardaremos 22s, poco
m as del doble, en resolver un problema de talla 2 000.
Un algoritmo cuadr atico empieza a dejar de ser util para tallas medias o grandes, pues pasar a tratar
con un problema el doble de grande requiere cuatro veces m as tiempo.
Un algoritmo c ubico s olo es util para problemas peque nos: duplicar el tama no del problema hace
que se tarde ocho veces m as tiempo.
Un algoritmo exponencial raramente es util. Si resolver un problema de talla 10 requiere una cantidad
de tiempo determinada con un algoritmo (2
n
), tratar con uno de talla 20 requiere unas 1 000 veces
m as tiempo!
La tabla 2.3 tambi en ayuda a tomar conciencia de las tasas de crecimiento. Supongamos que las instan-
cias de un problema de talla n = 1 se resuelven con varios algoritmos constantes, lineales, etc. en 1s. En
la tabla se muestra el tiempo aproximado que cuesta resolver problemas con cada uno de ellos. El ultimo
n umero es b arbaro: un e on son mil millones de a nos y los cientcos estiman actualmente que la edad del
universo es de entre 13 y 14 eones. No tiene sentido seguir estudiando c omo crece el coste de un algoritmo
exponencial. En la tabla 2.4 se muestran tiempos con tallas de problema mayores para el resto de costes.
Complejidad temp. n = 1 n = 5 n = 10 n = 50 n = 100
Constante 1s 1s 1s 1s 1s
Logartmico 1s 1.7s 2s 2.7s 3s
Lineal 1s 5s 10s 50s 100s
nlogn 1s 8.5s 11s 86s 201s
Cuadr atico 1s 25s 100s 2.5ms 10ms
C ubico 1s 125s 1ms 125ms 1s
Exponencial (2
n
) 1s 32s 1ms 35.75 a nos 4010
6
eones
Tabla 2.3: Tiempo de eje-
cuci on en funci on de n para
algoritmos con diferentes
costes.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . PROBLEMAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2-11 Si la ejecuci on de un programa requiere 1 s de tiempo para resolver una instancia de talla 100,
cu anto tiempo requerir a resolver uno de talla 1 000 en cada uno de estos casos?
a) El algoritmo es lineal.
2-22 Apuntes de Algortmica
c 2003, 2004 A. Marzal, M.J. Castro y P. Aibar 2 Analisis de algoritmos
Complejidad temp. n = 1000 n = 10000 n = 100000
Constante 1s 1s 1s
Logartmico 4s 5s 6s
Lineal 1ms 10ms 100ms
nlogn 4ms 50ms 600ms
Cuadr atico 1s 1 minuto y 40s 2 horas y 46 minutos
C ubico 16.5 minutos 11.5 das casi 32 a nos
Tabla 2.4: Tiempo de eje-
cuci on en funci on de n para
algoritmos con diferentes
costes.
b) El algoritmo es O(nlgn), siendo n la talla de la instancia.
c) El algoritmo es cuadr atico.
d) El algoritmo es c ubico.
e) El algoritmo es O(2
n
).
2-12 Si resolver una instancia de talla 100 con determinado algoritmo requiere 1 , cu al es la mayor
talla que podemos resolver en un minuto en cada uno de estos casos?
a) El algoritmo es lineal.
b) El algoritmo es O(nlgn), siendo n la talla de la instancia.
c) El algoritmo es cuadr atico.
d) El algoritmo es c ubico.
e) El algoritmo es O(2
n
).
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.8.1. Algunos teoremas utiles
Estos teoremas, adem as de los teoremas 2.1 y 2.3, resultan utiles para el c alculo de costes de algoritmos:
Teorema 2.4 Si c es una constante positiva, c (1).
Teorema 2.5 Si k > 0,

0in
i
k
(n
k+1
).
Teorema 2.6 Si r > 1,

0in
r
i
(r
n
).
Teorema 2.7 n! (

n(n/e)
n
).
Teorema 2.8

1in
1
i
(logn).
Teorema 2.9

0in
1
2
i
(1).
Teorema 2.10 Si 0 x < 1,

0in
x
i

1
1x
.
Teorema 2.11 Si 0 x < 1,

0in
ix
i

x
(1x)
2
.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . PROBLEMAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2-13 Acota asint otica y superiormente el tiempo de ejecuci on de este algoritmo:
Apuntes de Algortmica 2-23
2.9 Expresiones del coste temporal 2004/09/24-15:52
1 def sums(n):
2 s = 0.0
3 for i in xrange(n):
4 for j in xrange(i):
5 s += 1/i
6 return s
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.9. Expresiones del coste temporal
El coste de un algoritmo expresado en n umero de pasos necesarios para resolver una instancia no siempre
es una simple funci on de la talla de la instancia. Puede haber numerosas (incluso innitas) instancias de
una misma talla y el algoritmo puede requerir la ejecuci on de un n umero de pasos que depende de los datos
concretos de cada instancia. No siempre podemos, pues, caracterizar el coste temporal con una simple
funci on de la talla de las instancias.
2.9.1. Costes en el mejor y peor de los casos
El coste en el peor de los casos es una funci on de n con el mayor n umero de pasos necesarios para resolver
cualquier instancia de talla n. Suele presentarse en t erminos de su cota asint otica superior (notaci on ((orden
de))). El coste en el mejor de los casos es una funci on de n con el menor n umero de pasos necesarios para
resolver cualquier instancia de talla n. Suele presentarse en t erminos de su cota asint otica inferior (notaci on
((omega))).
Al buscar un elemento en un vector con el m etodo binario hemos visto que el coste en el peor de los
casos es proporcional a lgn. Podramos decir que el coste en el peor de los casos es (lgn), pero es
frecuente ofrecer unicamente su cota asint otica superior y decir que es O(lgn). El coste en el mejor de los
casos es constante. Solemos proporcionar unicamente su cota inferior asint otica y decimos que el coste en
el mejor de los casos es (1).
Si decimos de un algoritmo que su coste temporal es (1) y O(lgn) estamos diciendo que su coste en el
mejor de los casos es constante y que en el peor de los casos est a acotado asint oticamente por una funci on
logartmica.
Si la cota asint otica inferior del coste en el mejor de los casos coincide con la cota asint otica superior
del coste en el peor de los casos, se usa la notaci on ((zeta)).
2.9.2. Coste promedio
El coste en el mejor y peor de los casos ofrece una informaci on descriptiva del comportamiento del al-
goritmo para valores crecientes de la talla de las instancias. Podemos ofrecer una descripci on m as rica
si presentamos, adem as, informaci on sobre su comportamiento esperado. El coste temporal promedio Coste promedio: Average
cost.
es el tiempo de ejecuci on promediado para todas las entradas posibles de una talla dada asumiendo una
distribuci on de probabilidad para las instancias de igual talla.
Si I
n
es el conjunto de instancias de talla n y denotamos con t(I) el n umero de pasos necesarios para
resolver la instancia I I
n
y con Pr(I) denotamos la probabilidad de que ocurra la instancia I, el coste
temporal promedio en funci on de n es

II
n
Pr(I)t(I).
Volvamos al problema de la b usqueda en un vector ordenado y supongamos que existe la misma prob-
abilidad de que el elemento buscado haga que sequential_search se detenga en la i- esima iteraci on, para i
entre 1 y n. El n umero promedio de pasos que comporta la ejecuci on de sequential_search es
3
1
n+1
+

1in
3i
1
n+1
=
3
n+1
+
3
n+1

1in
i
=
3
n+1
+
3
n+1
n (n+1)
2
=
3
2
n+
3
n+1
.
O sea, en promedio da, aproximadamente, la mitad de pasos que en el peor de los casos.
2-24 Apuntes de Algortmica
c 2003, 2004 A. Marzal, M.J. Castro y P. Aibar 2 Analisis de algoritmos
Si la probabilidad de que busquemos uno u otro valor es diferente, el coste promedio puede ser tambi en
diferente. Supongamos que la probabilidad de que busquemos un valor que hace que el algoritmo se detenga
en la k- esima iteraci on es Pr(k) =1/2
k
, para k entre 1 y n, y que Pr(0) =1/2
n
(n otese que
0kn
Pr(k) =1).
El n umero promedio de pasos en funci on de n es, en ese caso,
3
1
2
n
+

1in
3i
1
2
i
= 3
1
2
n
+3

1in
i
2
i
= 3
_
1
2
n
+

1in
i
2
i
_
= 6.
El n umero medio de pasos es, pues, una cantidad constante.
Como se puede comprobar, el an alisis del coste temporal promedio resulta generalmente m as trabajoso
que el del coste en el mejor o peor de los casos y es dependiente de las asunciones que hagamos sobre
la distribuci on de las instancias (y no siempre es f acil averiguar la distribuci on de los datos reales en la
explotaci on del algoritmo).
2.9.3. Coste amortizado
En algunos casos, la estimaci on de coste para el peor de los casos de una determinada operaci on resulta
excesivamente pesimista, pues es posible que las circunstancias que hacen posible el elevado coste sean
raras y alcanzables unicamente tras efectuar ciertas operaciones previas. Cuando esto ocurra, presentare-
mos an alisis de coste de otra naturaleza: el denominado coste amortizado, que es el coste por operaci on Coste amortizado:
Amortized cost.
efectuada cuando se lleva a cabo una serie determinada de operaciones consecutivas.
No se debe confundir coste amortizado con el coste promedio: el coste amortizado es el coste total de
una serie de operaciones (en el peor de los casos) dividido por el n umero de operaciones efectuadas, sin
asumir ninguna distribuci on de probabilidad sobre las instancias de cada talla.
Un contador binario
Consideremos un ejemplo: una rutina para incrementar un contador codicado en binario natural expresado
como una secuencia de ceros y unos en un vector de talla n. Un vector como [0,0,0,1,0,0,1,1], por
ejemplo, representa el n umero binario 10011. El n umero que resulta de sumarle uno es 10100, que se
representa con [0,0,0,1,0,1,0,0]. Esta rutina Python efect ua el incremento del contador (sin tener en
cuenta posibles desbordamientos):
1 def increment(counter):
2 i = len(counter)-1
3 while i >= 0 and counter[i] == 1:
4 counter[i] = 0
5 i -= 1
6 if i >= 0:
7 counter[i] = 1
El coste en el peor de los casos es O(n): si todo el vector contiene n unos, el bucle lo recorre por completo
y modica todos sus bits.
Qu e ocurre si efectuamos n incrementos consecutivos sobre un contador que empieza con valor cero?
Parece l ogico pensar que el coste temporal total ser a O(n
2
). Sin embargo, efectuar esas n operaciones
requiere, en total, tiempo O(n) y no O(n
2
). C omo es posible? N otese que el bit menos signicativo
cambia de valor con cada llamada a increment, pero el segundo bit menos signicativo s olo cambia de valor
una de cada dos llamadas; el tercer bit s olo lo hace una de cada cuatro llamadas; y as sucesivamente. En
general, el bit i- esimo (de derecha a izquierda) s olo cambia su valor
_
n/2
i
_
veces cuando efectuamos n
incrementos seguidos sobre un contador originalmente nulo. El bit i- esimo para i mayor que lgn no se
modica nunca, as que el coste de los n incrementos es

0ilgn
_
n
2
i
_
< n

i0
1
2
i
= 2n,
o sea, O(n).
Si el coste de n incrementos es 2n, el coste promedio de cada uno es O(1). Decimos que el coste
amortizado de n incrementos sobre un contador inicialmente nulo es constante.
Apuntes de Algortmica 2-25
2.10 Coste espacial 2004/09/24-15:52
Redimensionamiento de vectores con holgura
Consideremos un ejemplo diferente: un vector din amico que debe redimensionarse para a nadir por el nal,
uno a uno, m elementos.
Plante emonos qu e ocurre al efectuar una sola adici on en un vector con n elementos. Es una operaci on
que requiere reservar n +1 celdas de memoria, copiar el valor de la n celdas originales, copiar al nal el
nuevo valor y liberar la memoria en la que residan los n valores originales. Se trata, pues, de una operaci on
ejecutable en tiempo O(n) y efectuar m adiciones requiere tiempo O(m(m+n)) = O(m
2
+mn).
Es posible reducir el coste temporal a s olo O(m+n) si ((malgastamos)) algo de memoria. Cuando
necesitemos que el vector almacene n elementos, reservaremos memoria para una cantidad de celdas, N,
mayor o igual que n. El valor N ser a la capacidad del vector, frente a n, que es su tama no. Si nos piden
a nadir una celda al vector y el tama no es menor que la capacidad, la adici on se puede ejecutar en tiempo
O(1). Si la capacidad es igual al tama no, habr a que aumentar antes la capacidad del vector en un cantidad
ja o proporcional a la capacidad actual.
Si el aumento de capacidad se produce multiplicando por 2 la capacidad anterior, estaremos efectuando
una operaci on de coste temporal O(2N), pero las siguientes N adiciones tendr an un coste temporal O(1).
Si consideramos las N operaciones de adici on consecutivas en su conjunto, su coste total ser a O(3N),
que dividido por N arroja una cantidad de tiempo constante por operaci on. Esta argumentaci on puede
extenderse a la adici on de m elementos a un vector de tama no n, con un coste temporal total O(n+m). Si
el tama no original, n, es por ejemplo 0 o 1, el coste de amortizado de cada una de las m adiciones es O(1).
Puede parecer que la memoria desperdiciada es muy grande, pero nunca es m as del doble que el tama no
del vector, una cantidad proporcional a dicho tama no.
Esta t ecnica se conoce como doblado, pero no es necesario que la constante por la que multiplicamos Doblado: Doubling.
la capacidad sea igual a 2: cualquier constante mayor que 1 vale.
Supongamos que cada vez que rebasamos la capacidad de un vector se solicita memoria para cN nuevos
elementos, siendo c > 1 y N la capacidad actual. Si efectuamos m adiciones al vector cuando su capacidad
y tama no es 1, se producir a una serie de reservas de memoria que har an que la capacidad del vector siga la
sucesi on c
0
, c, c
2
, . . . , c
k
, hasta que c
k1
< m c
k
.
De las m operaciones de adici on, mk tendr an coste temporal O(1) y las restantes k operaciones
tendr an, en total, un coste temporal del orden de

0ik
c
i
=
c
k+1
1
c 1
.
Si consideramos (el orden de) la suma del coste de la mk operaciones de coste constante con el de las
restantes k operaciones, tenemos
O
_
mk +
c
k+1
1
c 1
_
= O
_
c
k
k +
c
k+1
1
c 1
_
= O(c
k
).
El n umero total de operaciones, m, est a comprendido entre c
k1
y c
k
. El coste amortizado de cada una
de ellas es, pues, constante.
2.10. Coste espacial
El coste espacial de un algoritmo es la cantidad de memoria que necesita para resolver instancias de un
problema en funci on de la talla. La memoria que consume un algoritmo, al implementarse con un programa
y ejecutarse sobre un ordenador, puede dividirse en diferentes categoras:
Una parte de tama no jo:
El espacio necesario para el c odigo del programa.
El espacio necesario para almacenar variables simples y constantes.
Y una parte de tama no variable:
El espacio necesario para almacenar estructuras de datos complejas (vectores, matrices, etc),
que puede depender de la talla del problema.
El espacio necesario para la pila de llamadas a funci on. Las variables locales suelen residir en
dicha pila, aunque ello s olo resulta realmente necesario en el caso de los programas recursivos.
El n umero de tramas de activaci on en la pila depende de la talla del problema en el caso de los
programas recursivos.
2-26 Apuntes de Algortmica
c 2003, 2004 A. Marzal, M.J. Castro y P. Aibar 2 Analisis de algoritmos
Dado que nos basta con efectuar an alisis de coste espacial asint oticos, nos centraremos en la estimaci on
de la parte variable, es decir, la cantidad de memoria que depende de la talla del problema. No tendremos
en cuenta el espacio necesario para especicar la instancia del problema, s olo las variables que hemos de
utilizar para resolverla. Por otra parte, nos limitaremos a contar el n umero de celdas b asicas de memoria
necesarias, es decir, cada variable escalar o elemento simple de vector contar a como una sola celda, sin
tener en cuenta el n umero mnimo de bits con el que podramos representar los valores almacenados en
ellas.
Consideremos el m etodo naive_sequential_search: usamos dos variables auxiliares, i e index y la fun-
ci on no llama a ninguna otra (ni a s misma), as que el coste espacial es constante o, en notaci on asint otica
(1). Lo mismo ocurre con sequential_search y binary_search: el coste espacial es (1).
Analicemos recursive_binary_search, que es un m etodo recursivo. El n umero de variables escalares
locales utilizadas es constante, pero el m etodo es recursivo y cada activaci on de la funci on comporta la
reserva de memoria en la pila de llamadas a funci on. Cada una de las activaciones reserva tanto espacio
como requieran las variables locales (m as una cantidad de memoria de tama no jo). El n umero de llamadas
simult aneas en pila es menor o igual que 1+lgn, as que el espacio es (1) y O(lgn).
Cabe hacer una observaci on importante acerca del coste espacial: siempre est a acotado superiormente
por el coste temporal. Si un algoritmo necesita usar cierta cantidad de celdas de memoria, tendr a que visitar
cada una de las celdas de memoria al menos una vez (de lo contrario, no las necesitara).
2.11. Complejidad de problemas
Hasta el momento hemos considerado unicamente el coste temporal de algoritmos concretos. Un mismo
problema, como el de b usqueda en un vector ordenado, puede resolverse en tiempo O(n) u O(lgn). Es
O(lgn) la menor cota superior al coste temporal con el que podemos resolver ese problema? Obs ervese que
formulamos la pregunta del coste en relaci on al problema, no a un algoritmo. El campo de estudio que se
ocupa de la dicultad intrnseca de los problemas recibe el nombre de complejidad de problemas. Resulta
interesante saber si hay lmites al coste temporal con el que podemos resolver un problema determinado,
pues permite saber cuando hemos dise nado un algoritmo optimo, es decir, cuando tenemos la garanta de
que no se puede dise nar un algoritmo m as eciente.
Tomemos por caso el problema de la b usqueda en un vector ordenado. Un resultado importante nos
asegura que ning un algoritmo basado en la comparaci on de los elementos con el valor buscado puede
resolver el problema en tiempo con una cota superior asint otica inferior a O(lgn). De acuerdo con ello,
el m etodo binary_search (o su versi on recursiva) es un algoritmo optimo. La demostraci on se basa en el
estudio de los arboles de decisi on para b usqueda.
Un arbol de decisi on para un vector de tama no n es un arbol binario cuyas hojas est an etiquetadas
con n umeros entre 0 y n 1 (los ndices del vector). El arbol de decisi on asociado a un algoritmo es una
representaci on formal de las comparaciones que este efect ua y sigue estas reglas:
La raz est a etiquetada con el ndice de la primera entrada con la que se compara el valor buscado.
Si la etiqueta de un nodo es i, su hijo izquierdo est a etiquetado con el ndice del siguiente elemento
con que se efectuar a una comparaci on cuando el valor buscado es menor que a[i], y el hijo derecho,
con el ndice del elemento que compararemos a continuaci on si el valor buscado es mayor que a[i].
La gura 2.16 muestra los arboles de decisi on asociados a los algoritmos sequential_search y bina-
ry_search para vectores de 8 elementos.
El n umero de comparaciones efectuadas al buscar el valor que ocupa una determinada posici on es la
longitud del camino entre la raz y el nodo etiquetado con el ndice de dicha posici on. La profundidad del
arbol es la mayor cantidad de comparaciones que debe efectuar el algoritmo. Como cada nodo tiene a lo
sumo dos hijos, el n umero de nodos a distancia de la raz menor o igual que k es 1 +2 + +2
k1
. Si
tenemos n nodos, la profundidad mnima de un arbol de decisi on ser a 1+lgn. As pues, siempre hay una
ruta de la raz a alg un nodo de longitud 1+lgn.
2.11.1. Problemas difciles
La complejidad de ciertos problemas es exponencial. Tomemos por caso la enumeraci on de todas la per-
mutaciones de los elementos de un vector de tama no n. Hay n! permutaciones y mostrarlas (o almacenarlas)
requiere tiempo n n!. La aproximaci on de Stirling indica que n!

2n
_
n
e
_
n
, es decir, el coste temporal
de la enumeraci on es O(n

2n
_
n
e
_
n
).
Apuntes de Algortmica 2-27
2.11 Complejidad de problemas 2004/09/24-15:52
0
1
2
3
4
5
6
7 0
1
2
3
4
5
6
7
(a) (b)
Figura 2.16:

Arboles de decisi on asociados
al algoritmo de b usqueda secuencial (a) y al
algoritmo de b usqueda binaria (b).
Resulta prohibitivo utilizar un algoritmo que requiere tiempo exponencial cuando la talla del problema
es moderada o grande. Un problema que s olo se puede resolver en tiempo exponencial es un problema
intratable.
2.11.2. Problemas presumiblemente difciles
Hay una serie de problemas que resultan de inter es y para los que hay unos resultados curiosos. Nadie
sabe resolverlos en tiempo polin omico y nadie ha demostrado que requieran tiempo exponencial. Pero si
nos dan una soluci on, podemos comprobar, en tiempo polin omico, si es o no es v alida. Esto implica que
s sabramos resolverlos en tiempo polin omico en un ordenador no determinista, es decir, un ordenador
capaz de llevar en paralelo una cantidad de c alculos arbitrariamente grande: bastara con generar toda
posible soluci on en uno de los hilos de ejecuci on paralela y detenernos cuando uno de ellos compruebe
que ha alcanzado una soluci on. No es posible construir un computador capaz de semejante proeza, pero
tiene inter es considerar este tipo de dispositivos formales desde un punto de vista te orico. La familia de
los problemas con esta caracterstica se conoce por NP, siglas del t ermino ingl es ((Non-deterministically
Polynomial)). Estudiaremos con cierto detalle esta familia en el ultimo captulo.
El estudio de la familia NP centra su inter es sobre cierto tipo de problemas: los denominados proble-
mas de decisi on. Se trata de problemas cuya soluci on es siempre ((s)) o ((no)). Dentro de la familia NP
hay un subconjunto especial de problemas de decisi on: la familia de los problemas NP-completos. Los
problemas NP-completos son aquellos problemas NP tan difciles (en un sentido formal) como cualquier
otro problema de NP y son particularmente interesantes porque si se consiguiera resolver uno de ellos en
tiempo polin omico, todos los problemas de NP se resolveran tambi en en tiempo polin omico.
Baste decir, de momento, que cuando clasiquemos un problema como NP estaremos emitiendo un
juicio sobre la dicultad presumida del problema. Si lo clasicamos como NP-Completo, nuestra presun-
ci on de dicultad ser a m as rme.
A un hay otra familia de problemas de inter es atendiendo a su dicultad: los problemas NP-Difciles,
que son aquellos tan difciles, al menos, como los NP-completos, pero de los que no se ha demostrado que NP-difcil: NP-Hard.
sean NP.
2.11.3. Problemas irresolubles
Ciertos problemas ni siquiera son resolubles: los problemas indecidibles. El primer problema indecidible
conocido se conoce con el nombre de ((problema de la parada)), fue planteado por Alan Turing y se puede Problema de la parada:
Halting problem.
formular del siguiente modo: ((dado un programa, naliza su ejecuci on ante cualquier instancia?)).
Se puede demostrar que ning un algoritmo puede resolver el problema por reducci on al absurdo. Supong-
amos que existe una funci on, halt, a la que se suministran dos cadenas, una con un programa y otra con una
instancia del problema que resuelve, y devuelve ((cierto)) o ((falso)) en funci on de si dicho programa naliza
ante esa instancia o no. Consideremos esta funci on Python:
1 def trouble(s):
2 if halt(s, s) == False:
3 return True
2-28 Apuntes de Algortmica
c 2003, 2004 A. Marzal, M.J. Castro y P. Aibar 2 Analisis de algoritmos
4 while 1:
5 pass
Si halt dice que un programa naliza, trouble devuelve el valor ((falso)); en caso contrario, entra en un
bucle innito. Qu e ocurre si suministramos a trouble una cadena con c odigo de trouble? Se detiene su
ejecuci on? Consideremos las dos posibilidades:
Si se detiene es porque entonces halt devuelve False, lo que signica que no se detiene.
Y si no se detiene, es porque halt devuelve True, lo que signica que s se detiene.
As pues, es imposible denir una funci on halt capaz de decir siempre si un programa naliza o no.
La existencia de problemas indecidibles es un resultado negativo de gran importancia, pues pone lmites
a lo que se puede calcular con sistemas computadores (y si las personas somos sistemas computadores,
tambi en a lo que somos capaces de calcular!).
Apuntes de Algortmica 2-29

You might also like