Está en la página 1de 4

Universidad Nacional Micaela Bastidas de Apurímac

Caso Práctico

Complejidad de los algoritmos

Hace unos años presencié una discusión entre dos programadores sobre la “eficiencia
de los programas“. Ellos discutían sobre la manera de especificar el problema en la
herramienta CASE que utilizaban, para que el programa generado fuera más “eficiente”.
La discusión era acalorada y tenían visiones muy diferentes del asunto (yo, al no
conocer ni de cerca la herramienta en cuestión, no entendía ni media palabra de sus
argumentos).
Hasta que en un momento no pude contenerme y decidí meter el dedo en el ventilador:
A ver, una pregunta, ¿cuándo un programa es más eficiente que otro? – les dije. Para mi
asombro (aunque debería haberlo intuído), ambos me respondieron al unísono: ¡Cuando
es más corto!.
No soñaría por un instante que la mayoría de los programadores viven en tal ignorancia,
pero sin embargo muchos no tienen demasiada claridad acerca de este asunto. Más
aún, ni siquiera son conscientes de la enorme diferencia que puede haber, a la hora de
la ejecución, entre dos programas equivalentes pero de diferente complejidad temporal.
Pensando en esto, recordé un ejemplo utilizado por dos de mis mejores profes en un
artículo llamado “La perplejidad como recurso didáctico“.
Este ejemplo muestra cómo un cambio aparentemente sutil en un algoritmo puede
impactar tremendamente en el rendimiento del programa resultante.

El problema

Supongamos que tenemos que escribir un programa para calcular potencias naturales
de 2. Para ello podemos usar las operaciones aritméticas de suma y producto.

Una solución posible sería calcular:

 20 = 1
 2n= 2 * 2n– 1, para n > 0
Una alternativa sería considerar el caso “n > 0″ de forma diferente: sumando dos veces
2n– 1en vez de multiplicando por 2. Esto es:
 20 = 1
 2n= 2n– 1+ 2n– 1, para n > 0
Puede verse claramente que ambas soluciones son matemáticamente equivalentes.

A continuación, el código en lenguaje pascal de las dos soluciones (pow1 y pow2,


respectivamente). Por simplicidad se ha declarado el exponente a calcular como una
constante igual a 10:

Curso: Algorítmica III Docente: Ing. Erech Ordoñez Ramos


Universidad Nacional Micaela Bastidas de Apurímac

pow1.pas

program pow1;
const exp = 10;
function pow(n: integer): integer;
begin
if n = 0 then
pow := 1
else
pow := 2 * pow(n-1);
end;
begin
writeln(pow(exp));
end.

pow2.pas

program pow2;
const exp = 10;
function pow(n: integer): integer;
begin
if n = 0 then
pow := 1
else
pow := pow(n-1) + pow(n-1);
end;
begin
writeln(pow(exp));
end.

Complejidad temporal

Sin entrar en demasiados detalles técnicos (ver los enlaces al final del artículo),
analizaremos la eficiencia de cada uno de estos programas.
Primero vamos a suponer que el costo en tiempo de realizar las llamadas recursivas es
despreciable y que el costo de realizar una suma o una multiplicación es una unidad de
tiempo (luego veremos que esto no afecta el resultado que nos interesa destacar).

En el caso de pow1 vemos que la primera llamada a pow se hace asignando a n el


valor inicial exp, y luego en cada llamada recursiva este valor se decrementa en 1. Esto
significa que para calcular 2n se harán n llamadas recursivas (multiplicando en cada
caso el valor resultante por 2), lo cual insumirá n unidades de tiempo.
En el caso de pow2 la situación es diferente: en cada llamada a pow se hacen dos
nuevas llamadas recursivas, para luego sumar los resultados. Esto significa que para
calcular 2n se harán 2n llamadas recursivas (sumando en cada caso los valores
resultantes), lo cual insumirá 2n unidades de tiempo.

Curso: Algorítmica III Docente: Ing. Erech Ordoñez Ramos


Universidad Nacional Micaela Bastidas de Apurímac

De este análisis se desprende que pow1 requiere de un tiempo del orden de n (lineal),
en tanto que pow2 requiere de un tiempo del orden de 2n (exponencial).

¿Y todo esto qué significa?

En términos prácticos todo esto significa, ni más ni menos, que pow1 es un programa
que resuelve el problema de forma razonable (aunque podría ser mucho más eficiente),
en tanto que pow2 es (al menos si pensamos en valores grandes de n) una porquería.
Supongamos que tenemos una computadora capaz de ejecutar 1.000 operaciones por
segundo. La siguiente tabla muestra aproximadamente cuánto tardaría la ejecución de
cada uno de nuestros programas para distintos valores de n.

n pow1 pow2
10 0,01 seg 1 seg
20 0,02 seg 17 min
30 0,03 seg 12 días
40 0,04 seg 34 años
50 0,05 seg 35.700 años
100 1 seg 3.000 millones de veces
la edad del universo

Si bien 1.000 operaciones por segundo no parece una velocidad razonable para una
computadora de estos tiempos, podríamos multiplicarla por varios millones y aún no
podríamos calcular 2100 usando el programa pow2.
Lo más notable es que no importa si la computadora que utilicemos es “2 veces”, “10
veces” o “100 veces” más rápida: el salto en las diferencias de tiempo, pasado
determinado valor de n se mantendrá de todas formas.
Amén de que se trata de un ejemplo arbitrario (pero contundente), vale para mostrar la
gran importancia que puede tener sobre la eficiencia de un programa una pequeña
modificación sobre el algoritmo utilizado. Lamentablemente no es poco común ver
programas que se ejecutan razonablemente bien con una pequeña cantidad de datos,
pero cuyo funcionamiento se vuelve intolerable cuando estos crecen.

Cada vez que pienso en esto, me pregunto qué será de aquellos dos programadores y
los usuarios de sus sistemas…

Referencia:

http://blog.smaldone.com.ar/2006/10/13/la-complejidad-de-los-algoritmos/

Curso: Algorítmica III Docente: Ing. Erech Ordoñez Ramos


Universidad Nacional Micaela Bastidas de Apurímac

Actividades

1. En base al problema anterior hallar el tiempo de ejecución a priori de cada


algoritmos y sus respectivas complejidades.

2. Implementar los algoritmos en java y calcular los tiempos de ejecución a


posteriori en una computadora en particular con características definidas.

Características del Computador:

Velocidad de procesamiento: __________________________________


Numero de núcleos: ___________________________________________
Memoria instalada: ____________________________________________
Sistema Operativo: ____________________________________________

N pow1 pow2
20
25 Tabla: Tiempo de ejecución en segundos
30
35 Comentarios finales respecto al experimento:
37
39
40
41
42
43
44
45
_________________________________________________________________________________________
_________________________________________________________________________________________
_________________________________________________________________________________________
_________________________________________________________________________________________
_________________________________________________________________________________________
_________________________________________________________________________________________
_________________________________________________________________________________________

Curso: Algorítmica III Docente: Ing. Erech Ordoñez Ramos

También podría gustarte