Análisis de algoritmos - Itsp

Anuncio
Análisis de algoritmos
Mtr. Ing. Nancy López
Análisis de algoritmos
• Un algoritmo se utiliza para resolver un
problema.
• Normalmente se presentan infinidad de
algoritmos.
• Para que el algoritmo se considere correcto
debe funcionar correctamente en todos los
casos.
• A medida que aumentan las dificultades la
eficiencia del algoritmo cambia.
Análisis de algoritmos
Variables que intervienen:
• Tiempo: necesitamos resolver un problema en
un tiempo limitado.
• Cantidad de operaciones elementales que
debe realizar un algoritmo.
• Tamaño de los elementos que se pasan como
parámetros.
Análisis de algoritmos
• Para demostrar que un algoritmo es incorrecto
únicamente basta con mostrar que UN caso no
funciona.
• Para demostrar la validez del algoritmo es necesario
definir su Dominio (ejemplo División).
• Todo dispositivo de cálculo tiene un límite. (parámetros
son demasiados grandes o, nos quedamos sin espacio)
• Un problema puede tener varios algoritmos que sean
los adecuados.
• Si tenemos que resolver 1 o 2 casos se considera que
cualquiera de ellos sería eficiente.
• Preferentemente nos inclinaríamos por el que sea más
fácil de programar.
Análisis de algoritmos
Qué algoritmos elegir para resolver un problema?
• Que sean fáciles de entender, codificar y depurar
• Que usen eficientemente los recursos del
sistema: que usen poca memoria y que se
ejecuten con la mayor rapidez posible
• Ambos factores en general se contraponen…
• Nos concentraremos ahora en el segundo factor y
en particular en el análisis del tiempo de
ejecución
Análisis de algoritmos
• Para seleccionar un algoritmo podemos ver 2
diferentes formas.
• La empírica: consiste en ir probando las
diferentes técnicas, con base en la experiencia.
• La teórica: consiste en ver la cantidad de casos a
considerar, teniendo en cuenta el tiempo a
computar los casos y la memoria de
almacenamiento y dado esto se verá su tiempo
de ejecución. La eficiencia estará directamente
vinculada al tiempo de ejecución.
Análisis de algoritmos
• También existe un enfoque híbrido. Siendo
éste una mezcla de los 2, en estos casos la
experiencia previa es de suma importancia.
• Otra cosa a tener en cuenta, será el lenguaje
del programador, y el compilador con el cual
contaremos. Serán variables que pueden
incidir directamente en el tiempo de eficiencia
más allá de la máquina en sí.
Análisis de algoritmos
• Sean "g(n)" diferentes funciones que
determinan el uso de recursos. Habrá
funciones "g" de todos los colores.
Identificaremos "familias" de funciones,
usando como criterio de agrupación su
comportamiento asintótico.
Análisis de algoritmos
• Para cada algoritmo las entradas las
consideraremos de un tamaño N.
• Por ejemplo para un vector se podría decir
que es su tamaño, para un Grafo los nodos y
las líneas, y si trabajásemos con una base de
datos, la cantidad de claves primarias por las
que tiene que pasar al realizar una consulta.
Análisis de algoritmos
• Cada algoritmo cuenta con un T(N) que es el tiempo
que lleva el mismo.
• Por ejemplo si tuviésemos:
S1;
for (int i= 0; i < N; i++)
S2;
Siendo S1= 1er serie de sentencias y S2 = 2da sentencias.
El cálculo sería el siguiente: T(N)= t1 + t2*N siendo t1 el
tiempo que lleve ejecutar la serie "S1" de sentencias, y
t2 el que lleve la serie "S2".
Análisis de algoritmos
• Prácticamente todos los programas reales
incluyen alguna sentencia condicional, haciendo
que las sentencias efectivamente ejecutadas
dependan de los datos concretos que se le
presenten. Esto hace que más que un valor T(N)
debamos hablar de un rango de valores
• Tmin(N) <= T(N) <= Tmax(N)
• Los extremos son habitualmente conocidos como
"caso peor" y "caso mejor". Entre ambos se
hallará algún "caso promedio" o más frecuente.
Análisis de algoritmos
• Por una parte necesitamos analizar la potencia de
los algoritmos independientemente de la
potencia de la máquina que los ejecute e incluso
de la habilidad del programador que los
codifique.
• Por otra, este análisis nos interesa especialmente
cuando el algoritmo se aplica a problemas
grandes. Casi siempre los problemas pequeños se
pueden resolver de cualquier forma, apareciendo
las limitaciones al atacar problemas grandes.
Análisis de algoritmos
• Cualquier técnica de ingeniería, si funciona, acaba
aplicándose al problema más grande que sea
posible: las tecnologías de éxito, antes o después,
acaban llevándose al límite de sus posibilidades.
• Las consideraciones anteriores nos llevan a
estudiar el comportamiento de un algoritmo
cuando se fuerza el tamaño del problema al que
se aplica. Matemáticamente hablando, cuando N
tiende a infinito. Es decir, su comportamiento
asintótico.
Análisis de algoritmos
• Si podemos garantizar que un programa sólo
va a trabajar sobre datos pequeños (valores
bajos de N), el orden de complejidad del
algoritmo que usemos suele ser irrelevante.
Análisis de algoritmos
• A un conjunto de funciones que comparten un
mismo comportamiento asintótico le
denominaremos un orden de complejidad.
Habitualmente estos conjuntos se denominan
O, existiendo una infinidad de ellos.
Análisis de algoritmos
• Para cada uno de estos conjuntos se suele
identificar un miembro f(n) que se utiliza
como representante de la clase, hablándose
del conjunto de funciones "g" que son del
orden de "f(n)", denotándose como
g IN O(f(n))
Análisis de algoritmos
• Con frecuencia nos encontraremos con que no
es necesario conocer el comportamiento
exacto, sino que basta conocer una cota
superior, es decir, alguna función que se
comporte "aún peor".
• O(f(n)) está formado por aquellas funciones
g(n) que crecen a un ritmo menor o igual que
el de f(n).
Análisis de algoritmos
• 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).
Análisis de algoritmos
• Se dice que O(f(n)) define un "orden de
complejidad". Tomamos el f(n) más sencillo:
O(1)
orden constante
O(log n) orden logarítmico
O(n)
orden lineal
O(n log n)
orden lineal logarítmico
O(n2)
orden cuadrático
O(na)
orden polinomial (a > 2)
O(an)
orden exponencial (a > 2)
O(n!)
orden factorial
Análisis de algoritmos
• Para compararlos entre sí, supongamos que
todos ellos requieren 1 hora de ordenador
para resolver un problema de tamaño N=100.
• ¿Qué ocurre si disponemos del doble de
tiempo? Nótese que esto es lo mismo que
disponer del mismo tiempo en un ordenador
el doble de potente, y que el ritmo actual de
progreso del hardware es exactamente ese:
Análisis de algoritmos
Ejemplos:
Bubble Sort O(n²) - Árbol Binario O(n log n)
Análisis de algoritmos
Técnicas de diseño
Fuerza Bruta
• Se debe evitar.
• No es un esquema algorítmico sino un
calificativo.
• Consiste en tomar la primer solución en la que
pensemos, sin reflexionarla.
• Puede servir como una primera aproximación
a la solución final.
Técnicas de diseño
Fuerza Bruta. Ejemplos
• Algunos algoritmos de búsqueda de un
elemento en un vector. Uno de ellos realiza
una búsqueda secuencial con complejidad
lineal sobre el tamaño del vector y puede
usarse con cualquier vector. Otro algoritmo
realiza un búsqueda dicotómica o binaria, con
complejidad logarítmica, y solo se puede usar
cuando el vector esté ordenado.
Técnicas de diseño
• La primera solución es la más tentadora, lo
ideal sería comprobar si el algoritmo está
ordenado y aplicar en consecuencia.
Técnicas de diseño
Divide y vencerás
• Consiste en descomponer un problema en
sub-problemas, resolver independientemente
los sub-problemas para luego combinar sus
soluciones y obtener la solución del problema
original. Esta técnica se puede aplicar con
éxito a problemas como la multiplicación de
matrices, la ordenación de vectores, la
búsqueda en estructuras ordenadas, etc.
Divide y vencerás. Ejemplo. Búsqueda de una
palabra en un diccionario
Como ejemplo sencillo de aplicación de esta
estrategia puede considerarse la búsqueda de
una palabra en un diccionario de acuerdo con el
siguiente criterio.
Se abre el diccionario por la página central
(quedando dividido en dos mitades) y se
comprueba si la palabra aparece allí o si es
léxicográficamente anterior o posterior. Si no ha
encontrado y es anterior se procede a buscarla
en la primera mitad, si es posterior, se buscará en
la segunda mitad. El procedimiento se repite
sucesivamente hasta encontrar la palabra o
decidir que no aparece.
Técnicas de diseño
Método voraz
• Este método trata de producir el mejor resultado
a partir de un conjunto de opciones candidatas.
• Para ello, se va procediendo paso a paso
realizándose la mejor elección (usando una
función objetivo que respeta un conjunto de
restricciones ) de entre las posibles.
• Puede emplearse en problemas de optimización,
como en la búsqueda de caminos mínimos sobre
grafos, la planificación en el orden de la ejecución
de algunos programas en un computador, etc.
Método Voraz. Ejemplo. Dar un cambio utilizando el
menor número de monedas
Considérese ahora el problema de la devolución del
cambio al realizar una compra (por ejemplo, en un
cajero automático).
Suponiendo que se disponga de cantidad suficiente de
ciertos tipos diferentes de monedas de curso legal, se
trata de dar como cambio la menor cantidad posible
usando estos tipos de monedas.
La estrategia voraz aplicada comienza devolviendo,
cuando se pueda, la moneda de mayor valor (es decir,
mientras el valor de dicha moneda sea mayor o igual
al cambio que resta por dar), continua aplicándose el
mismo criterio para la segunda moneda más valiosa, y
así sucesivamente.
El proceso finaliza cuando se ha devuelto todo el cambio.
Técnicas de diseño
• Si un programa se va a ejecutar muy pocas
veces, los costes de codificación y depuración
son los que más importan, relegando la
complejidad a un papel secundario.
• Si a un programa se le prevé larga vida, hay
que pensar que le tocará mantenerlo a otra
persona y, por tanto, conviene tener en cuenta
su legibilidad, incluso a costa de la
complejidad de los algoritmos empleados.
Técnicas de diseño
Consideraciones:
• Corrección, el algoritmo debe funcionar. Nunca se debe
olvidar que la característica más simple e importante de un
algoritmo es que funcione. Pude aparecer obvio, pero resulta
difícil de asegurar en algoritmos complejos.
• Eficiencia, el algoritmo no debe desaprovechar recursos. La
eficiencia de un algoritmo se mide por los recursos que este
consume. En particular, se habla de la memoria y del tiempo
de ejecución. A pesar de que con la reducción de los costes
del hardware es posible diseñar computadores más rápidos y
con más memoria, no hay que desperdiciar estos recursos y
tratar de desarrollar algoritmos más eficientes.
Técnicas de diseño
• Claridad, el algoritmo debe estar bien
documentado. La documentación ayuda a
comprender el funcionamiento de los
algoritmos. Ciertos detalles o algunas partes
especiales de los mismos pueden olvidarse
fácilmente o quedar a oscuras si no están
adecuadamente comentadas.
Resumiendo, lo ideal es que nuestro algoritmo
resulte correcto, eficiente, claro, fiable y fácil de
mantener.
Técnicas de diseño
Conclusiones
●
Antes de realizar un programa conviene elegir un buen
algoritmo, donde por bueno entendemos que utilice pocos
recursos, siendo usualmente lo más importante el tiempo
que lleve ejecutarse y la cantidad de espacio en memoria
que requiera. Es engañoso pensar que todos los algoritmos
son "más o menos iguales" y confiar en nuestra habilidad
como programadores para convertir un mal algoritmo en un
producto eficaz. Es asimismo engañoso confiar en la
creciente potencia de las máquinas y el abaratamiento de
las mismas como remedio de todos los problemas que
puedan aparecer.
Algoritmos de ordenamiento
Definición
Entrada: Una secuencia de n números <a1, a2, …, an>, usualmente
en la forma de un arreglo de n elementos.
Salida: Una permutación <a’1, a’2, …, a’n> de la secuencia de
entrada tal que a’1 ≤ a’2 ≤ … ≤ a’n.
Algoritmos de ordenamiento
• Los algoritmos de ordenación se clasifican de
acuerdo con la cantidad de trabajo necesaria
para ordenar una secuencia de n elementos:
¿Cuántas comparaciones de elementos y
cuántos movimientos de elementos de un
lugar a otro son necesarios?
Algoritmos de ordenamiento
Ordenamiento por inserción
• Este es uno de los métodos más sencillos. Consta
de tomar uno por uno los elementos de un
arreglo y correrlo hacia su posición con respecto
a los anteriormente ordenados. Así empieza con
el segundo elemento y lo ordena con respecto al
primero. Luego sigue con el tercero y lo coloca en
su posición ordenada con respecto a los dos
anteriores, así sucesivamente hasta recorrer
todas las posiciones del arreglo.
insercionDirecta(A[],n)
{
entero i,j,v
para i=0; i<n; i++
{
v=A[i]
j=i-1
mientras j>=0 y A[j]>v
{
A[j+1]=A[j]
j—
}
A[j+1] = v
}
Algoritmos de ordenamiento
Análisis del algoritmo:
• Número de comparaciones: n(n-1)/2 lo que
implica un T(n) = O(n2).
• La ordenación por inserción utiliza
aproximadamente n2/4 comparaciones y n2/8
intercambios en el caso medio y dos veces
más en el peor de los casos.
• La ordenación por inserción es lineal para los
archivos casi ordenados.
Algoritmos de ordenamiento
Ordenamiento por selección
• El método de ordenamiento por selección
consiste en encontrar el menor de todos los
elementos del arreglo e intercambiarlo con el
que está en la primera posición. Luego el
segundo más pequeño, y así sucesivamente
hasta ordenar todo el arreglo.
Algoritmos de ordenamiento
seleccionSort( A[], n)
{
enteros min, i, j, aux;
para (i=0; i<n-1; i++)
{
min=i;
para (j=i+1; j<n; j++)
{
Si ( A[min] > A[j] )
min=j;
}
aux=A[min];
A[min]=A[i];
A[i]=aux;
}
}
Algoritmos de ordenamiento
Análisis del algoritmo:
• La ordenación por selección utiliza
aproximadamente n2/2 comparaciones y n
intercambios, por lo cual T(n) = O(n2).
Algoritmos de ordenamiento
Ordenamiento por burbuja (Bubble Sort)
• El algoritmo bubblesort, también conocido como
ordenamiento burbuja, funciona de la siguiente
manera:
• Se recorre el arreglo intercambiando los
elementos adyacentes que estén desordenados.
Se recorre el arreglo tantas veces hasta que ya no
haya cambios que realizar.
• Prácticamente lo que hace es tomar el elemento
mayor y lo va corriendo de posición en posición
hasta ponerlo en su lugar.
Algoritmos de ordenamiento
Burbuja
Para (i=0; i<TAM; i++)
for(j=0; j<TAM-1; j++)
if( clave j < clave j+1 ) //o clave j > clave j+1
{
//intercambio
}
Algoritmos de ordenamiento
Análisis del algoritmo:
• La ordenación de burbuja tanto en el caso
medio como en el peor de los casos utiliza
aproximadamente n2/2 comparaciones y n2/2
intercambios, por lo cual
• T(n) = O(n2).
Algoritmos de ordenamiento
• Ordenamiento Rápido (Quicksort)
• Vimos que en un algoritmo de ordenamiento
por intercambio, son necesarios intercambios
de elementos en sublistas hasta que no son
posibles más. En el burbujeo, son comparados
ítems correlativos en cada paso de la lista. Por
lo tanto, para ubicar un ítem en su correcta
posición, pueden ser necesarios varios
intercambios.
Algoritmos de ordenamiento
• La idea básica del algoritmo es elegir un elemento llamado
pivote, y ejecutar una secuencia de intercambios tal que
todos los elementos menores que el pivote queden a la
izquierda y todos los mayores a la derecha.
• Lo único que requiere este proceso es que todos los
elementos a la izquierda sean menores que el pivote y que
todos los de la derecha sean mayores luego de cada paso, no
importando el orden entre ellos, siendo precisamente esta
flexibilidad la que hace eficiente al proceso.
• Hacemos dos búsquedas, una desde la izquierda y otra desde
la derecha, comparando el pivote con los elementos que
vamos recorriendo, buscando los menores o iguales y los
mayores respectivamente.
Algoritmos de ordenamiento
QuickSort (A[], izq, der)
Si i <= j
Enteros i, j, x, aux
{
i=izq
aux=A[i]
j=der
A[i]=A[j]
x= A[(izq+der)/2]
A[j]=aux
hacer
i++
{
j-Mientras (A[i]<x) && (j <=
}
der)
} mientras i <= j
i++
Si izq < j
Mientras (x < A[j]) && (j>
quickSort(A, izq, j)
izq)
si i<der
j-quickSort (A, i, der)
Algoritmos de ordenamiento
Análisis del algoritmo:
• Depende de si la partición es o no balanceada,
lo que a su vez depende de cómo se elige los
pivotes.
• En promedio para todos los elementos de
entrada de tamaño n, el método hace O(n log
n) comparaciones, el cual es relativamente
eficiente.
Algoritmos de ordenamiento
• Ordenamiento Heap Sort
• El ordenamiento por montículos (Heap sort) es un
algoritmo de ordenación no recursivo, no estable, con
complejidad computacional O(n log n).
• Este algoritmo consiste en almacenar todos los elementos
del vector a ordenar en un montículo (heap), y luego
extraer el nodo que queda como nodo raíz del montículo
(cima) en sucesivas iteraciones obteniendo el conjunto
ordenado. Basa su funcionamiento en una propiedad de los
montículos, por la cual, la cima contiene siempre el menor
elemento (o el mayor, según se haya definido el montículo)
de todos los almacenados en él.
•
•
•
•
Procedimiento Heap Sort
Heap Sort consiste esencialmente en:
Convertir el arreglo en un heap.
Construir un arreglo ordenado de atrás hacia adelante
(mayor a menor) repitiendo los siguientes pasos:
– Sacar el valor máximo en el heap (el de la posición 1)
– Poner ese valor en el arreglo ordenado.
– Reconstruir el heap con un elemento menos.
• Utilizar el mismo arreglo para el heap y el arreglo
ordenado.
Algoritmos de ordenamiento
heapSort (A[],n)
para k=n ; k>0 ; k-{
para i=1; i<= k; i++
{
item=A[i]
j=i/2
mientras j > 0 && A[j] < ítem
{
A[i]=A[j]
i=j
j=j/2
}
A[i]= item
}
temp=A[1]
A[1]=A[k]
A[k]=temp
}
El padre de i es: i div 2.
Los hijos de i son: 2 * i y 2 * i + 1.
Comparación
Algoritmos de búsqueda
Búsqueda secuencial
• Busca un elemento de una lista utilizando un
valor destino llamado clave. También llamada
búsqueda lineal, se exploran los elementos en
secuencia, uno después del otro.
• El método de búsqueda lineal funcionará bien
con arreglos pequeños o no ordenados. La
complejidad es pobre: O(n).
Algoritmos de búsqueda
Búsqueda dicotómica o binaria
• Sólo se puede utilizar en arreglos ya
ordenados. Usa el método de divide y
vencerás.
• Procedimiento: el procedimiento es el ya visto
en la búsqueda de una palabra en un
diccionario.
• El orden de complejidad es O(log n)
Algoritmos de búsqueda
• Búsqueda binaria
int busquedaBinaria(vector, TAM, dato)
centro,inf=0,sup=TAM-1
mientras(inf<=sup){
centro=(sup+inf)/2
si(vector[centro]==dato)
devolver centro
sino
si(dato < vector [centro] )
sup=centro-1
sino{ inf=centro+1
devolver -1
Descargar