Enviado por rolfpintoEn la ciencia de la computación los algoritmos son más importantes que los LP o que las computadoras; la solución de un problema haciendo uso de las computadoras requiere por una parte un algoritmo o método de resolución y por otra un programa o codificación del algoritmo en un LP. Ambos componentes tienen importancia; pero la del algoritmo es absolutamente indispensable; sabemos que un algoritmo es una secuencia de pasos para resolver un problema, sus características son:
Debemos saber que una solución es un conjunto único, pero no es el único conjunto de pasos que entregan la solución, existen muchas alternativas de solución y estas alternativas pueden diferir por:
Ahora que sabemos que existen muchas alternativas de solución para un problema, debemos seleccionar el algoritmo más eficiente, el mejor conjunto de pasos, el que tarde menos en ejecutarse, que tenga menos líneas de código.
Esta selección puede ser ejecutada a simple vista con sólo observar la cantidad de líneas del programa, pero cuando el programa crece se requiere una medición más exacta y apropiada, para esto se realizan ciertas operaciones matemáticas que establecen la eficiencia teórica del programa, al estudio de estos casos se denomina Complejidad Algorítmica.
S1;
for(x = 0; x < N; x++)
S2;
Demanda: T(N) = t1 + t2 * N
Donde t1 es el tiempo que lleva ejecutar la serie S1 de sentencias, y t2 es el que lleva la serie S2.
Tmin(N) ≤ T(N) ≤ Tmax(N)
g É O(f(n)), g esta incluido en f(n)
O(f(n))= { g | $ k Î Â +, $ n0 Î À tales que, n n0, g(n) £ kf(n) }
O(f(n)) esta formado por aquellas funciones g(n) que crecen a un ritmo menor o igual que el de f(n).
De las funciones g que forman este conjunto O(f(n)) se dice que están dominadas asintóticamente por f, en el sentido de que para N suficientemente grande, y salvo una constante multiplicativa k, f(n) es una cota superior de g(n).
Ejemplo: Se puede comprobar que la función g(n) = 3n3 + 2n2, es de O(n3)
Aplicando la definición dada anteriormente:
g(n) = 3n3 + 2n2
f(n) = n3
n0 = 0
k = 5
Se tiene: 3n3 + 2n2 £ 5n3, n 0
|
n |
g( ) |
kf( ) |
|
0 |
0 |
0 |
|
1 |
5 |
5 |
|
2 |
32 |
40 |
|
3 |
99 |
135 |
|
O(1) |
Orden constante |
|
O(log n) |
Orden logarítmico |
|
O(n) |
Orden lineal |
|
O(n log n) |
Orden cuasi-lineal |
|
O(n2) |
Orden cuadrático |
|
O(n3) |
Orden cúbico |
|
O(na) |
Orden polinómico |
|
O(2n) |
Orden exponencial |
|
O(n!) |
Orden factorial |

Algoritmos Polinomiales: Aquellos que son proporcionales a na. Son en general factibles o aplicables, los problemas basados en estos algoritmos son solucionables.
Algoritmos Exponenciales: Aquellos que son proporcionales a kn, k En general no son factibles salvo un tamaño de entrada n exageradamente pequeño, pero generalmente pertenecen a un universo de problemas de los cuales el cómputo se hace imposible. N
Reglas de la Notación Asintótica
Si T1(n) y T2(n) son las funciones que expresan los tiempos de ejecución de dos fragmentos de un programa, y se acotan de forma que se tiene:
T1(n) = O(f1(n)) y T2(n) = O(f2(n))
Se puede decir que:
T1(n) + T2(n) = O(max(f1(n), f2(n)))
Si T1(n) y T2(n) son las funciones que expresan los tiempos de ejecución de dos fragmentos de un programa, y se acotan de forma que se tiene:
T1(n) = O(f1(n)) y T2(n)=O(f2(n))
Se puede decir que:
T1(n) T2(n) = O(f1(n) f2(n))
Bien; para demostrar la importancia de la elaboración de algoritmos eficientes, se plantea el siguiente problema:
|
n |
TIEMPO |
|
10 |
» 1 décima de segundo |
|
20 |
» 2 minutos |
|
30 |
> 1 día |
|
40 |
> 3 años |
|
50 |
= 3 570 años |
|
100 |
= 4,019,693,684,133,150 milenios |
|
n |
TIEMPO |
|
10 |
= 1 décima de segundo |
|
20 |
= 8 décimas de segundo |
|
100 |
= 1.7 minutos |
|
200 |
= 13.3 minutos |
|
1000 |
» 1 día |
ESTIMACIÓN DE LA COMPLEJIDAD EN ALGORITMOS NO RECURSIVOS
Asignaciones y expresiones simples (=)
El tiempo de ejecución de toda instrucción de asignación simple, de la evaluación de una expresión formada por términos simples o de toda constante es O(1).
Secuencias de instrucciones (;)
El tiempo de ejecución de una secuencia de instrucciones es igual a la suma de sus tiempos de ejecución individuales. Para una secuencia de dos instrucciones S1 y S2 el tiempo de ejecución esta dado por la suma de los tiempos de ejecución de S1 y S2:
T(S1 ; S2) = T(S1) + T(S2)
Aplicando la regla de la suma:
O(T(S1 ; S2)) = max(O( T(S1), T(S2) ))
Instrucciones condicionales (IF, SWITCH-CASE)
El tiempo de ejecución para una instrucción condicional IF-THEN es el tiempo necesario para evaluar la condición, más el requerido para el conjunto de instrucciones que se ejecutan cuando se cumple la condición lógica.
T(IF-THEN) = T(condición) + T(rama THEN)
Aplicando la regla de la suma:
O(T(IF-THEN)) = max(O( T(condición), T(rama THEN) ))
El tiempo de ejecución para una instrucción condicional de tipo IF-THEN-ELSE resulta de evaluar la condición, más el máximo valor del conjunto de instrucciones de las ramas THEN y ELSE.
T(IF-THEN-ELSE) = T(condición) + max(T(rama THEN), T(rama ELSE))
Aplicando la regla de la suma, su orden esta dada por la siguiente expresión:
O(T(IF-THEN-ELSE)) = O( T(condición)) + max(O(T(rama THEN)), O(T(rama ELSE)) )
Aplicando la regla de la suma:
O(T(IF-THEN-ELSE)) = max(O( T(condición)), max(O(T(rama THEN)), O(T(rama ELSE)) ))
El tiempo de ejecución de un condicional múltiple (SWITCH-CASE) es el tiempo necesario para evaluar la condición, más el mayor de los tiempos de las secuencias a ejecutar en cada valor condicional.
Instrucciones de iteración (FOR, WHILE-DO, DO-WHILE)
El tiempo de ejecución de un bucle FOR es el producto del número de iteraciones por la complejidad de las instrucciones del cuerpo del mismo bucle.
Para los ciclos del tipo WHILE-DO y DO-WHILE se sigue la regla anterior, pero se considera la evaluación del número de iteraciones para el peor caso posible. Si existen ciclos anidados, se realiza el análisis de adentro hacia fuera, considerando el tiempo de ejecución de un ciclo interior y la suma del resto de proposiciones como el tiempo de ejecución de una iteración del ciclo exterior.
Llamadas a procedimientos
El tiempo de ejecución esta dado por, el tiempo requerido para ejecutar el cuerpo del procedimiento llamado. Si un procedimiento hace llamadas a otros procedimientos "no recursivos", es posible calcular el tiempo de ejecución de cada procedimiento llamado, uno a la vez, partiendo de aquellos que no llaman a ninguno.
int factorial(int n) O(1)
{
int fact = 1; O(1)
for(int i = n; i > 0; i--) O(n)
fact = fact * i; O(1)
return fact; O(1)
}
Su complejidad es lineal O(n), debido a que se tiene un bucle FOR cuyo número de iteraciones es n.
int y, z, k = 10; O(1)
for(int i = 0; i < k; i++) k * O(1)
{
y = y + i; O(1)
z = z + k; O(1)
}
Su complejidad es constante; es decir, O(1), debido a que se tiene un bucle for independiente de n.
for(int i = 0; i < n; i++) O(n)
{
for(int z = 0; z < n; z++) O(n)
{
if(vector[z] > vector[z + 1]) O(1)
{
aux = vector[z]; O(1)
vector[z] = vector[z + 1]; O(1)
vector[z + 1] = aux; O(1)
}
}
}
Tenemos O(n) * O(n) * O(1) = O(n2), complejidad cuadrática.
for(int i = 0; i < n; i++) O(n)
{
for(int z = n; z < i; z--) O(n)
{
if(vector[z] < vector[z - 1]) O(1)
{
aux = vector[z]; O(1)
vector[z] = vector[z - 1]; O(1)
vector[z - 1] = aux; O(1)
}
}
}
Tenemos que el bucle exterior se ejecuta n veces Þ O(n), el bucle interno se ejecuta n + … + 3 + 2 + 1 veces respectivamente, o sea (n * (n+1))/2 Þ O(n).
O(n) * O(n) * O(1) = O(n2), complejidad cuadrática.
int c = 1; O(1)
while(c < n) O(log n)
{
if(vector[c] < vector[n]) O(1)
{
aux = vector[n]; O(1)
vector[n] = vector[c]; O(1)
vector[c] = aux; O(1)
}
c = c * 2;
}
Para este ejemplo al principio el valor de c es 1, al cabo de x iteraciones será 2x Þ el número de iteraciones es tal que n £ 2x donde x es el entero inmediato superior de n; x = log2 n iteraciones, Þ para este caso es:
O(1) * O(log n) * O(1) = O(log n), complejidad logarítmica.
int c = n; O(1)
while(c > 1) O(log n)
{
vector[c] = c; O(1)
c = c / 2; O(1)
}
Para este ejemplo al principio el valor de c es igual a n, al cabo de x iteraciones será n*2-x Þ el número de iteraciones es tal que n*2-x £ n; un razonamiento análogo nos lleva a log2 n iteraciones, Þ para este caso es:
O(1) * O(log n) * O(1) = O(log n), complejidad logarítmica.
int c, x; O(1)
for(int i= 0; i < n; i++) O(n)
{
c = i; O(1)
while(c > 0) O(log n)
{
x = x % c; O(1)
c = c / 2; O(1)
}
x = x + 2; O(1)
}
Tenemos un bucle interno de orden O(log n) que se ejecuta O(log n) veces, luego el conjunto de ordenes es de orden:
O(1) * O(n) * O(log n) = O(n log n), complejidad cuasi-lineal.
Autor:
Rolf Manolo Pinto López
Ingeniero de Sistemas
Ingrese el e-mail y contraseña con el que está registrado en Monografias.com
Trabajos relacionados
Ver mas trabajos de Programacion |
|
Nota al lector: es posible que esta página no contenga todos los componentes del trabajo original (pies de página, avanzadas formulas matemáticas, esquemas o tablas complejas, etc.). Recuerde que para ver el trabajo en su versión original completa, puede descargarlo desde el menú superior.
Todos los documentos disponibles en este sitio expresan los puntos de vista de sus respectivos autores y no de Monografias.com. El objetivo de Monografias.com es poner el conocimiento a disposición de toda su comunidad. Queda bajo la responsabilidad de cada lector el eventual uso que se le de a esta información. Asimismo, es obligatoria la cita del autor del contenido y de Monografias.com como fuentes de información.