Análisis de Algoritmos.

Se entiende de manera ordinaria que la complejidad de algoritmos es una combinación teórica que permite ser aplicada a los algoritmos
cuando dentro de los mismos se presenta cierto grado como su nombre lo indica
de complejidad. Es un concepto que para todos los programadores es fundamental
pero es muy usual que dicho concepto se conozca por completo.

Básicamente la complejidad de algoritmos comienza a estar
más orientada a los programación más que a las matemáticas ya que todos y cada
uno de los métodos y procedimiento que se llevan a cabo son estructurados
mediante una lógica esto permite a los programadores enfocarse prácticamente en
las necesidades de problema a resolver.

Dentro de las complejidad de algoritmos es importan evaluar
las mejor conocidas como funciones de referencia que son aquellas funciones
matemáticas que se representan en el siguiente orden.

* Función constante.
* Función logarítmica.
* Función lineal.
* Función cuadrática.
* Función exponencial
* Función factorial.

Se dice también que un conjunto de funciones que comparten
un mismo comportamiento, les denominaremos un orden de complejidad. Regularmente siempre estos conjuntos se denominan 0.

Dentro de los conjuntos podemos identificarlos de acuerdo a
su clase como se mencionó anteriormente en la lista, para este tipo de funciones y dentro de las propiedades matemáticas que existen es posible resolver manualmente este tipo de funciones orientándolas de acuerdo a los
factores o productos que los acompañen como puede ser el tipo de función cuales son los rangos que se van a tomar, si existen fórmulas que permitan un mayor desempeño dentro de la resolución.

Para la rama de la programación este tipo de detalles se
puede ver un tanto complicados por los factores que implican la función en este
caso matemática de la que se está hablando y los rangos o parámetros a evaluar.

Este tipo de problemas que se ven sencillos de un lado pero
complejos del otros es la manera más sencilla de ver como un problema se puede ver tan grande como queramos o tan pequeño como lo denotemos el control de este tipo de algoritmos complejos, ya en todo momento como seres pensantes y racionales podemos formular los algoritmos necesarios para la resolución de
situación en cuestiones que podríamos observar difíciles desde los diferentes puntos de vista.

Las aplicaciones principales de estos algoritmos son:

* Teoría General de Grafos.
* Optimización lineal y no lineal.
* Algoritmos on-line, knowledge
* Criptografía.
* Geometría algebraica.
* Restricciones de base de datos
* Encriptación de archivos
* Autómatas Finitos o Finitos no Deterministas.
* Máquina de Turing.

Es una parte importante de la teoría de complejidad computacional mas amplia, que provee estimaciones teóricas para los recursos que necesita cualquier algoritmo que resuelva un problema computacional dado.

El análisis de algoritmos nos permite medir la dificultad inherente de un problema y evaluar la eficiencia de un algoritmo.

Principales tareas de un algoritmo:

* Determinar que operaciones se emplean y su costo relativo
* Determinar conjuntos de datos para exhibir todos los patrones posibles de comportamiento
* Análisis a priori: se determina una función que acote el tiempo de computo del algoritmo
* Análisis a posteriori: estadísticas reales sobre tiempo y memoria

COMPLEJIDAD DE ALGORITMOS

Si un conjunto de instrucciones tiene todas las características de un algoritmo, excepto ser finito en tiempo se le denomina proceso computacional. Los sistemas operativos son el mejor ejemplo de proceso computacional, pues están diseñados para ejecutar tareas mientras las haya pendientes, y cuando éstas se terminan, el sistema operativo entra en un estado de espera, hasta que llegan más, pero nunca termina.

La función complejidad, f(n), donde n es el tamaño del problema, da una medida de la cantidad de recursos que un algoritmo necesitará al implantarse y ejecutarse en alguna computadora. Puesto que la cantidad de recursos que consume un algoritmo crece conforme el tamaño del problema se incrementa, la función complejidad es monótona creciente (f(n) > f(m) ⇔ n > m) con respecto al tamaño del problema. La memoria y el tiempo de procesador son los recursos sobre los cuales se concentra todo el interés en el análisis de un algoritmo, así pues distinguiremos dos clases de función complejidad:

a) Función complejidad espacial. Mide la cantidad de memoria que necesitará un algoritmo para resolver un problema de tamaño n.

b) Función complejidad temporal. Indica la cantidad de tiempo que re- quiere un algoritmo para resolver un problema de tamaño n; viene a ser una medida de la cantidad de CPU que requiere el algoritmo.

COMPLEJIDAD EN EL TIEMPO

Habitualmente todos los algoritmos contienen alguna sentencia condicional o selectiva, haciendo que las sentencias ejecutadas dependan de la condición lógica, esto hace que aparezca más de un valor para T(N), es por ello que debemos hablar de un rango de valores.

Cuando se habla del tiempo de ejecución de un algoritmo debe tenerse presente que el tiempo de ejecución exacto de un programa depende de varios factores:

1.- Los datos de entrada

2.- La calidad del condigo generado por el compilador

3.- La máquina donde se ejecuta el programa

4.- La complejidad del tiempo del algoritmo base del programa

TIEMPOS DE EJECUCIÓN

Una medida que suele ser útil conocer es el tiempo de ejecución de un algoritmo en función de N, lo que denominaremos T(N). Esta función se puede medir físicamente (ejecutando el programa, reloj en mano), o calcularse sobre el código contando instrucciones a ejecutar y multiplicando por el tiempo requerido por cada instrucción.

Las operaciones básicas son las que realiza el computador en tiempo acotado por una constante, por ejemplo:

• Operaciones aritméticas básicas

• Asignaciones de tipos predefinidos

• Saltos (llamadas a funciones, procedimientos y retorno)

• Acceso a estructuras indexadas básicas (vectores y matrices)

Para medir el tiempo de ejecución de un algoritmo existen varios métodos. Veamos algunos de ellos:

a) Benchmarking

La técnica de benchmark considera una colección de entradas típicas representativas de una carga de trabajo para un programa.

b) Profiling

Consiste en asociar a cada instrucción de un programa un número que representa la fracción del tiempo total tomada para ejecutar esa instrucción particular. Una de las técnicas más conocidas (e informal) es la Regla 90-10, que afirma que el 90% del tiempo de ejecución se invierte en el 10% del código.

c) Análisis

Consiste en agrupar las entradas de acuerdo a su tamaño, y estimar el tiempo de ejecución del programa en entradas de ese tamaño, T(n). Esta es la técnica que se estudiará en el curso.

De este modo, el tiempo de ejecución puede ser definido como una función de la entrada. Denotaremos T(n) como el tiempo de ejecución de un algoritmo para una entrada de tamaño n.