* Como leer estos temas
* Introducción a los algoritmos
Como leer estos temas
No hay un orden fijo para leerlos. En general es común
que ya se conozcan algunos métodos de ordenación o de búsqueda,
siendo por tanto un buen punto para empezar. La sección recursividad
que aparece en esta sección de algoritmos está principalmente
centrada en su eliminación y puede no resultar interesante en principio.
Un posible comienzo sería el backtracking (o vuelta atrás)
por ser una base para tratar otros algoritmos más complejos, y terminar
con la programación dinámica, un tema de características
diferentes al resto.
De forma independiente pueden estudiarse los algoritmos sobre
grafos y los algoritmos matemáticos.
Pero si de verdad hay algo fundamental es la introducción
que se expone a continuación.
Introducción a los algoritmos
- ¿Qué es un algoritmo?
Una definición informal (no se considera aquí una
definición formal, aunque existe): conjunto finito de reglas que dan
una secuencia de operaciones para resolver todos los problemas de un tipo dado.
De forma más sencilla, podemos decir que un algoritmo es un conjunto
de pasos que nos permite obtener un dato. Además debe cumplir estas condiciones:
· Finitud: el algoritmo debe
acabar tras un número finito de pasos. Es más, es casi fundamental
que sea en un número razonable de pasos.
· Definibilidad: el algoritmo debe definirse
de forma precisa para cada paso, es decir, hay que evitar toda ambigüedad
al definir cada paso. Puesto que el lenguaje humano es impreciso, los algoritmos
se expresan mediante un lenguaje formal, ya sea matemático o de programación
para un computador.
· Entrada: el algoritmo tendrá cero
o más entradas, es decir, cantidades dadas antes de empezar el algoritmo.
Estas cantidades pertenecen además a conjuntos especificados de objetos.
Por ejemplo, pueden ser cadenas de caracteres, enteros, naturales, fraccionarios,
etc. Se trata siempre de cantidades representativas del mundo real expresadas
de tal forma que sean aptas para su interpretación por el computador.
· Salida: el algoritmo tiene una o más
salidas, en relación con las entradas.
· Efectividad: se entiende por esto que una
persona sea capaz de realizar el algoritmo de modo exacto y sin ayuda de una
máquina en un lapso de tiempo finito.
A menudo los algoritmos requieren una organización bastante compleja
de los datos, y es por tanto necesario un estudio previo de las estructuras
de datos fundamentales. Dichas estructuras pueden implementarse de diferentes
maneras, y es más, existen algoritmos para implementar dichas estructuras.
El uso de estructuras de datos adecuadas pueden hacer trivial el diseño
de un algoritmo, o un algoritmo muy complejo puede usar estructuras de datos
muy simples.
Uno de los algoritmos más antiguos conocidos es el algoritmo
de Euclides. El término algoritmo proviene del matemático Muhammad
ibn Musa al-Khwarizmi, que vivió aproximadamente entre los años
780 y 850 d.C. en la actual nación Iraní. El describió
la realización de operaciones elementales en el sistema de numeración
decimal. De al-Khwarizmi se obtuvo la derivación algoritmo.
- Clasificación de algoritmos
* Algoritmo determinista: en cada paso del algoritmo
se determina de forma única el siguiente paso.
* Algoritmo no determinista: deben decidir en cada paso de la ejecución
entre varias alternativas y agotarlas todas antes de encontrar la solución.
Todo algoritmo tiene una serie de características, entre
otras que requiere una serie de recursos, algo que es fundamental considerar
a la hora de implementarlos en una máquina. Estos recursos son principalmente:
· El tiempo: período transcurrido entre el inicio y la finalización
del algoritmo.
· La memoria: la cantidad (la medida varía según
la máquina) que necesita el algoritmo para su ejecución.
Obviamente, la capacidad y el diseño de la máquina pueden afectar
al diseño del algoritmo.
En general, la mayoría de los problemas tienen un parámetro
de entrada que es el número de datos que hay que tratar, esto es, N.
La cantidad de recursos del algoritmo es tratada como una función de
N. De esta manera puede establecerse un tiempo de ejecución del algoritmo
que suele ser proporcional a una de las siguientes funciones:
- 1 : Tiempo de ejecución constante. Significa
que la mayoría de las instrucciones se ejecutan una vez o muy pocas.
- logN : Tiempo de ejecución logarítmico.
Se puede considerar como una gran constante. La base del logaritmo (en informática
la más común es la base 2) cambia la constante, pero no demasiado.
El programa es más lento cuanto más crezca N, pero es inapreciable,
pues logN no se duplica hasta que N llegue a N2.
- N : Tiempo de ejecución lineal. Un caso en el
que N valga 40, tardará el doble que otro en que N valga 20. Un ejemplo
sería un algoritmo que lee N números enteros y devuelve la media
aritmética.
- N·logN : El tiempo de ejecución es N·logN.
Es común encontrarlo en algoritmos como Quick
Sort y otros del estilo divide y vencerás. Si N se duplica, el
tiempo de ejecución es ligeramente mayor del doble.
- N2 : Tiempo de ejecución cuadrático.
Suele ser habitual cuando se tratan pares de elementos de datos, como por
ejemplo un bucle anidado doble. Si N se duplica, el tiempo de ejecución
aumenta cuatro veces. El peor caso de entrada del algoritmo Quick Sort
se ejecuta en este tiempo.
- N3 : Tiempo de ejecución cúbico.
Como ejemplo se puede dar el de un bucle anidado triple. Si N se duplica,
el tiempo de ejecución se multiplica por ocho.
- 2N : Tiempo de ejecución exponencial.
No suelen ser muy útiles en la práctica por el elevadísimo
tiempo de ejecución. El problema de la mochila
resuelto por un algoritmo de fuerza bruta -simple vuelta atrás- es
un ejemplo. Si N se duplica, el tiempo de ejecución se eleva al cuadrado.
* Algoritmos polinomiales: aquellos que son proporcionales
a Nk. Son en general factibles.
* Algoritmos exponenciales: aquellos que son proporcionales a kN.
En general son infactibles salvo un tamaño de entrada muy reducido.
- Notación O-grande
En general, el tiempo de ejecución es proporcional, esto
es, multiplica por una constante a alguno de los tiempos de ejecución
anteriormente propuestos, además de la suma de algunos términos
más pequeños. Así, un algoritmo cuyo tiempo de ejecución
sea T = 3N2 + 6N se puede considerar proporcional a N2.
En este caso se diría que el algoritmo es del orden de N2,
y se escribe O(N2)
Los grafos definidos por matriz de adyacencia
ocupan un espacio O(N2), siendo N el número de vértices
de éste.
La notación O-grande ignora los factores constantes, es
decir, ignora si se hace una mejor o peor implementación del algoritmo,
además de ser independiente de los datos de entrada del algoritmo. Es
decir, la utilidad de aplicar esta notación a un algoritmo es encontrar
un límite superior del tiempo de ejecución, es decir, el peor
caso.
A veces ocurre que no hay que prestar demasiada atención
a esto. Conviene diferenciar entre el peor caso y el esperado. Por ejemplo,
el tiempo de ejecución del algoritmo Quick Sort es de O(N2).
Sin embargo, en la práctica este caso no se da casi nunca y la mayoría
de los casos son proporcionales a N·logN. Es por ello que se utiliza
esta última expresión para este método de ordenación.
Una definición rigurosa de esta notación es la
siguiente:
Una función g(N) pertenece a O(f(N)) si y sólo
si existen las constantes c0 y N0 tales que:
|g(N)| <= |c0·f(N)| , para todo N >= N0.
- Clasificación de problemas
Los problemas matemáticos se pueden dividir en primera
instancia en dos grupos:
* Problemas indecidibles: aquellos que no se pueden
resolver mediante un algoritmo.
* Problemas decidibles: aquellos que cuentan al menos con un algoritmo
para su cómputo.
Sin embargo, que un problema sea decidible no implica que se pueda encontrar
su solución, pues muchos problemas que disponen de algoritmos para su
resolución son inabordables para un computador por el elevado número
de operaciones que hay que realizar para resolverlos. Esto permite separar los
problemas decidibles en dos:
* intratables: aquellos para los que no es factible obtener su solución.
* tratables: aquellos para los que existe al menos un algoritmo
capaz de resolverlo en un tiempo razonable.
Los problemas pueden clasificarse también atendiendo a
su complejidad. Aquellos problemas para los que se conoce un algoritmo
polinómico que los resuelve se denominan clase P. Los algoritmos
que los resuelven son deterministas. Para otros problemas, sus mejores algoritmos
conocidos son no deterministas. Esta clase de problemas se denomina clase
NP. Por tanto, los problemas de la clase P son un subconjunto de
los de la clase NP, pues sólo cuentan con una alternativa en cada
paso.
|