1. Desarrollo de Programas iterativos usando invariante

Slides:



Advertisements
Presentaciones similares
UN MUNDO LLENO DE PROBLEMAS
Advertisements

Diseño y análisis de algoritmos
Tema I. LAS FRACCIONES 3º de la ESO Sara Muñoz.1ºBACH Ciencias.
FACTORIZACIÓN LU Bachilleres:
Estructuras de Decisión I IF - THEN - ELSE
ALGORITMOS DE ORDENAMIENTO
MATRIZ DE CHEQUEO DE PARIDAD
Estructuras de control
Análisis Matemático III
PROGRAMACION DE ESTRUCTURAS DE DATOS
Ecuaciones diferenciales de 1er orden :
PARADIGMA Es un esquema de pensamiento que nos lleva a concebir las cosas de una manera determinada. el término paradigma puede indicar el concepto de esquema.
CI TEORIA semana 8 Subprogramas o funciones Definición de funciones.
FUNCIONES Y PROCEDIMIENTOS
APLICACIONES DE PILAS Estructuras de Datos.
Ordenación, Clasificación
Tema 6: Clases Antonio J. Sierra.
TÉCNICO EN PROGRAMACIÓN DE SOFTWARE Instructor: Ingeniero Adrián Guerrero Taborda
Ingeniero Anyelo Quintero
Potencias de exponente natural mayor que 1
Potencias de exponente natural mayor que 1
GRAFOS HUGO ARAYA CARRASCO.
EXPONENTES Y RADICALES
Semana 5 Subprogramas..
Teoria de grafos.-clase 4
Resolución de Problemas Método Simplex
Programación Lineal Unidad 1 Parte 3.
DIVISIÓN DE POLINOMIOS 1
UNIDAD 2:Crear, abrir y cerrar una base de datos Hacer clic sobre la opción Nuevo de la pestaña Archivo. Se mostrarán las distintas opciones para nuevos.

Capítulo 1 “Elementos de Programación”
Estructuras de Datos Arreglos.
Sistemas de numeración
2. Diagramas de Estado. ¿Qué es y para qué se usa? Una técnica para desarrollar programas Espacialmente adecuado para programas que tienen que analizar.
Material de apoyo Unidad 4 Estructura de datos
Divide y vencerás 1. Método general.
Aplicación de estructuras de datos
Ordenación y Búsqueda.
Recurrencia Programación II de enero de 2009.
EXPRESIONES Y SENTENCIAS
4.  Métodos matemáticos.
Computación I Primer Semestre 2006 Capítulo IV Ciclos y Colecciones (con un sabor a algoritmos)
Estructuras de Control.
Operaciones con números complejos
Análisis y Diseño de Algoritmos
Algoritmos de Ordenamiento y Complejidad
Profesor: Luis Miguel Iglesias Albarrán
Análisis de Algoritmos
2. Strings, Archivos y Arreglos
INTRODUCCION A LA PROGRAMACION
Fundamentos de Programación
FACTORIZACION.
ESTRUCTURAS REPETITIVAS
“CURSO PROPEDÉUTICO PARA EL MEJORAMIENTO DEL PENSAMIENTO MATEMÁTICO”
Estadística Reporte Ejecutivo
Estructuras de Decisión

MATRIZ INVERSA.
“Análisis y Diseño de Algoritmos” 2a Entrega, Programa 6 Centro de Investigación y Estudios Avanzados del IPN Samuel Garrido Daniel 15-Abril-2004 México,
6. Sistemas de ecuaciones diferenciales lineales
PROGRAMACIÓN Grupo de Modelamiento de Sistemas
Mini-video 2 de 5 Materia: Límites de funciones Continuidad de funciones Prácticas con Introducción a Funciones de una variable.
Tema: 1 Divisibilidad con números naturales 1 Matemáticas 1º
UNIVERSIDAD TECNOLÓGICA DE PANAMÁ Facultad de Ingeniería de Sistemas Computacionales Programa de Lic. en Informática Educativa Computación.
3.6 Manejo de E/S Estructuras En la creación de soluciones para algunos problemas surge la necesidad de agrupar datos de diferente tipo o de manejar datos.
2. Arreglos e invariantes. Arreglos Un arreglo es una coleccion ordenada de elementos del mismo tipo – Tipos de datos primitivos (int, …) – Referencias.
Ciclos condicionales y exactos Estructura de control de ciclos
2. Arreglos e invariantes
1. Desarrollo de Programas iterativos usando invariante
Transcripción de la presentación:

1. Desarrollo de Programas iterativos usando invariante

La instrucción de iteración La forma más general de una instrucción iterativa es : while( condición )    instrucción; La instrucción se ejecuta en forma reiterada mientras la condición sea verdadera. Variaciones: while( condición ) {     instrucción 1; . . . instrucción N; } while(true) { if (condición) break; for (instrucción de inicialización; condición; instrucción N) { instrucción 1; instrucción N-1;

El invariante de una iteración Invariante: Alguna condición (conjunto) que describe lo que un ciclo hace. Permanece invariante durante el ciclo: al entrar, durante cada iteración y al final. Debería representar la estrategia que se usa para resolver el problema. Se usa para: -       A partir del invariante desarrollar el trozo de programa -       Demostrar que el trozo programa hace lo que queremos -       Explicar un trozo de programa Al escribir un ciclo se debe a) establecer la validez inicial del invariante, a través de una inicialización adecuada. B) El objetivo del ciclo es llegar a un estado final F. c) En cada iteración se debe, además, preservar la validez del invariante.

Ejemplo: invariante para desarrollar Usar un invariante para desarrollar un ciclo que permita encontrar el máximo de un número variable de datos, almacenados en un arreglo a[1], ..., a[n]. Estrategia: revisar todos los elementos a[1] hasta a[n] y registrar el mayor visto hasta ahora en m Invariante: m = max(a[1] … a[k]), 1 <= k<=n a1 a2 ai an an-1 . . . k

Condiciones iniciales y finales a) El invariante se debe cumplir antes de entrar al ciclo, pero no hemos revisado nada aún. Podemos decir que el primero es el mayor de los revisados hasta ahora m = a[1]; k = 1; a1 a2 ai an an-1 . . . k b) Para conseguir el objetivo, k debe valer n, por lo que debemos parar el ciclo cuando k = n, o continuarlo mientras k != n, o k < n a1 a2 ai an an-1 . . . k

Avanzar y restablecer el invariante c) Para avanzar hay que tratar de que k llegue a n. Esto se hace incrementando k. Esto puede significar que el invariante no se cumple pues el nuevo a[k] podría ser mayor que m. k++; Para restablecer el invariante podemos incluir en el ciclo las siguientes instrucciones (después de incrementar k) if (a[k] < m) m = a[k]; Ahora solo basta ensamblar a), b) y c) para tener el trozo de programa que hace lo que queremos a1 a2 . . . ai ai . . . an-1 an k

¿Cómo escribir un ciclo y asegurarse que hace lo que uno quiere? 1.    Encontrar un invariante adecuado. Para esto, a menudo es conveniente "relajar" la meta (estado final) al que se desea llegar. Por ejemplo, si se desea obtener: // m == max(a[1]...a[n]) se puede re-escribir esta condición separándola en dos condiciones que se puedan satisfacer independientemente: // m == max(a[1]...a[k]) && k==n A continuación se relaja la exigencia de esta condición, haciendo que se cumpla la primera parte, pero dejando que la segunda se satisfaga con "k<=n". 2.    Escribir la inicialización, la cual debe asegurar que el invariante se cumpla antes de empezar a iterar. 3.    Encontrar la condición de término. Esto se obtiene de comparar "qué le falta" al invariante para ser igual al estado final. 4.    Escribir el cuerpo del ciclo, el cual debe: o    conseguir que el proceso avance, de modo que termine algún día, y o    preservar el invariante. Al efectuar un avance en el proceso, los valores de las variables cambian, con el resultado que a menudo se deja de satisfacer el invariante. Por lo tanto, el resto del cuerpo del ciclo se suele dedicar a tratar de recuperar la validez del invariante.

Ejemplo 1: ordenación por selección Este algoritmo se basa en hacer pasadas sucesivas sobre los datos. En cada pasada, se encuentra el máximo del arreglo, y se lo lleva al extremo derecho. Una vez hecho esto, ese elemento deja de ser considerado, porque se encuentra ya en su posición definitiva. Esto conduce al siguiente invariante: a[i] < a[i+1] para i = k hasta n-1 a[j] < a [i] con j = 0 hasta k-1, todos desordenados En palabras: "Los elementos desde k hasta n-1 ya están ordenados y son mayores que los primeros k".

Condiciones iniciales y finales Condiciones iniciales: al principio no se puede decir nada, por lo que se hace k = n, lo que dice que implica: a[i] < a[i+1] para i = n hasta n-1 (no existe elemento) a[j] < a [n] con j = 0 hasta n-1, todos desordenados (a[n] no existe) ¿Más comprensible?: al principio poner el mayor del arreglo en el lugar n-1 y hacer k = n-1 b) Condición final: k = 1 (o mientras k >= 2) a[i] < a[i+1] para i = 1 hasta n-1 a[j] < a [1] con j = 0 hasta 0, todos desordenados

Ord. X selección: avanzar y restablecer invariante // Ordenar a[0], ..., a[n-1] por selección  k = n; // inicialmente los n primeros están desordenados while( k>1 )  { --k; //acercar k a la meta, se rompe el invariante     Llevar el max de a[0], ..., a[k] hacia a[k]; //esto restablece el invariante   } Donde Llevar el max de a[0], ..., a[k] hacia a[k] => i = 0; // a[i] es el max hasta el momento for( j=1; j<=k; ++j )     if( a[j]>a[i] )         i = j;// ahora intercambiamos a[i] con a[k] t = a[i]; a[i] = a[k]; a[k] = t;

Ejemplo 2: ordenación por inserción Este algoritmo va construyendo un trozo ordenado del arreglo al extremo izquierdo, y en cada iteración le agrega un nuevo elemento a ese grupo. Invariante: a[i] < a[i+1] para i = 0 hasta k-1 a[k] hasta a[n-1] desordenados En palabras: "Los elementos desde 0 hasta k-1 ya están ordenados y los siguientes desordenados".

Condiciones iniciales y finales Condiciones iniciales: al principio se puede decir que el primer elemento a[0] está ordenado por definición, por lo que se hace k = 1, lo que implica: a[i] < a[i+1] para i = 0 hasta 1-1 a[1] hasta a[n-1] desordenados b) Condición final: k = n (o mientras k < n) a[i] < a[i+1] para i = 0 hasta n-1 a[n] hasta a[n-1] desordenados (no existe!)

Avanzar y restablecer invariante // Ordenar a[0], ..., a[n-1] por inserción  k = 0; // inicialmente el primer elemento esta ordenado while( k < n )  { ++k; //acercar k a la meta, se rompe el invariante Insertar a[k-1] entre a[0],...,a[k-2]; //esto restablece el invariante   } Donde Insertar a[k-1] entre a[0],...,a[k-2] => for( j=k-1; j>0 && a[j-1]>a[j]; --j )  {     // intercambiar a[j-1] con a[j]     t = a[j];    a[j] = a[j-1];    a[j-1] = t;

Optimización Al seguir el proceso de la inserción, se puede observar que la variable t toma siempre el mismo valor: el del elemento que se está insertando. Por lo tanto, se puede optimizar el programa realizando una única asignación a t antes de comenzar el ciclo. Otra observación es que la mayoría de las asignaciones a a[j-1] son inútiles, porque esa variable va a ser sobre-escrita en la iteración siguiente. Luego, se puede evitar esas asignaciones, reemplazándolas por una sola al final del ciclo: Insertar a[k-1] entre a[0],...,a[k-2] => // versión optimizada t = a[k]; for( j=k; j>0 && a[j-1]>t; --j )     a[j] = a[j-1]; a[j] = t;

Versión final optimizada (y algo variada) // Ordenar a[0],...,a[n-1] por inserción k = 1; // inicialmente primer elemento ordenado while( k < n )  {     // Insertar a[k] entre a[0],...,a[k-1]     t = a[k];     for( j=k; j>0 && a[j-1]>t; --j )         a[j] = a[j-1];     a[j] = t;     ++k;  }

Ejemplo 2: ordenación por selección Este algoritmo se basa en hacer pasadas sucesivas sobre los datos. En cada pasada, se encuentra el máximo del arreglo, y se lo lleva al extremo derecho. Una vez hecho esto, ese elemento deja de ser considerado, porque se encuentra ya en su posición definitiva. Esto conduce al siguiente invariante: a[i] < a[i+1] para i = k hasta n-1 a[j] < a [i] con j = 0 hasta k-1, todos desordenados En palabras: "Los elementos desde k hasta n-1 ya están ordenados y son mayores que los primeros k".

Condiciones iniciales y finales Condiciones iniciales: al principio no se puede decir nada, por lo que se hace k = n, lo que dice que implica: a[i] < a[i+1] para i = n hasta n-1 (no existe elemento) a[j] < a [n] con j = 0 hasta n-1, todos desordenados (a[n] no existe) ¿Más comprensible?: al principio poner el mayor del arreglo en el lugar n-1 y hacer k = n-1 b) Condición final: k = 1 (o mientras k >= 2) a[i] < a[i+1] para i = 1 hasta n-1 a[j] < a [1] con j = 0 hasta 0, todos desordenados

Ord. X selección: avanzar y restablecer invariante // Ordenar a[0], ..., a[n-1] por selección  k = n; // inicialmente los n primeros están desordenados while( k>1 )  { --k; //acercar k a la meta, se rompe el invariante     Llevar el max de a[0], ..., a[k] hacia a[k]; //esto restablece el invariante   } Donde Llevar el max de a[0], ..., a[k] hacia a[k] => i = 0; // a[i] es el max hasta el momento for( j=1; j<=k; ++j )     if( a[j]>a[i] )         i = j;// ahora intercambiamos a[i] con a[k] t = a[i]; a[i] = a[k]; a[k] = t;

Ejemplo 3: ordenación por burbuja Este método se basa en hacer pasadas de izquierda a derecha sobre los datos, intercambiando pares de elementos adyacentes que estén fuera de orden. Al final de cada pasada, en forma natural el máximo estará en la posición de más a la derecha (que es su posición final) y puede por lo tanto ser excluido en pasadas sucesivas. Esto conduce al siguiente invariante (idéntico al de ordenación por selección): a[i] < a[i+1] para i = k hasta n-1 a[j] < a [i] con j = 0 hasta k-1, todos desordenados

Ord. X burbuja: avanzar y restablecer invariante // Ordenar a[0], ..., a[n-1] por la burbuja (borrador) k = n; while( k>1 )  {     Hacer una pasada sobre a[0], ..., a[k-1];     Disminuir k;   } Donde Hacer una pasada sobre a[0], ..., a[k-1] => for( j=0; j<=k-2; ++j )    if( a[j]>a[j+1] ){ // Intercambiar a[j] con a[j+1]        t = a[j]; a[j] = a[j+1]; a[j+1] = t;    } y Disminuir k =>--k;

Ord. X burbuja: eficiencia Disminuir k => --k; Si el archivo está inicialmente ordenado, el programa igual hace n pasadas, cuando después de la primera ya podría haberse dado cuenta que el archivo ya estaba ordenado. Para aprovechar cualquier posible orden que pueda haber en el archivo, se puede hacer que el programa anote ("recuerde") el lugar en donde se produjo el último intercambio. Si la variable i se define de manera que el último intercambio en una pasada dada fue entre a[i-1] y a[i], entonces todos los elementos desde a[i] en adelante están ya ordenados (de lo contrario habría habido intercambios más hacia la derecha), y por lo tanto k se puede disminuir haciendo que sea igual a i: Hacer una pasada sobre a[0], ..., a[k-1] => i=0; for( j=0; j<=k-2; ++j )   if( a[j]>a[j+1] )   {    t = a[j];  a[j] = a[j+1]; a[j+1] = t;        i = j+1; //Recordar el lugar del último intercambio   } Disminuir k => k=i;

Ejemplo 4: Xn Cuando n es entero se puede programar una función más eficiente que la que usa java, basada en el cálculo de una serie. Incluso una solución simple sería más eficiente: public static double power(double x, int n) { // Algoritmo simple y = 1; for( j=n; j>0; --j )     y = y*x; return y; } Este algoritmo evidentemente toma tiempo O(n), y su invariante se puede escribir como         y * xj == xn

Aprovechando el invariante para la eficiencia Es posible encontrar un algoritmo sustancialmente más eficiente de la siguiente manera. Primero se desvinculan las dos ocurrencias de x en el invariante: y = 1; z = x; for( j=n; j>0; --j )     y = y*z; con invariante y * zj == xn Esto podría parecer ocioso, pero permite hacer una optimización al observar que está permitido modificar la variable z al inicio del ciclo siempre que se mantenga la validez del invariante. En particular, si j resulta ser par, podemos elevar z al cuadrado si al mismo tiempo dividimos j por 2. De esta manera, el invariante sigue igual, pero j disminuye mucho más rápido.

Algoritmo eficiente no recursivo Mejor todavía, si esto se puede hacer una vez, entonces se puede hacer muchas veces siempre que j siga siendo par: y = 1; z = x; for( j=n; j>0; --j ) // Invariante: y * zj == xn  {     while( j es par )      {        z = z*z;         j = j/2;       }     y = y*z;  } La detección que j es par se puede implementar como j es par => j&1 == 0   Este algoritmo demora tiempo O(log n), lo cual se debe a que j sólo se puede dividir log n veces por 2 antes de llegar a 1. Es cierto que j sólo se divide cuando es par, pero si es impar en una iteración del for, está garantizado que será par a la siguiente.

Ejemplo 4: Explicar un algoritmo difícil Se tiene un arreglo con n elementos (a[0] hasta a[n-1]) blanco y negros desordenados. Dejar los elementos intercalados. Si hay mas de una clase que de otra dejarlos todos al final. Ejemplo, si inicialmente se tiene dejarlos invariante ??????????? i j

Ejemplo 4: Explicar un algoritmo difícil (cont.) Inicialmente se hace i = 0; j = 1; Condicion final: j = n => continuación: while (j < n) Restablecer el invariante i j j i ?????????? i j Si a[j] mismo color que a[i] no se hace nada, si no, se intercambia con a[i+1]; j++

Ejercicio en clases Se tiene un arreglo a[0] . . . a[n-1] con enteros y se quiere particionarlo de modo que queden todos los menores que cero a la izquierda y los iguales o mayores que cero a la derecha. En palabras: los elementos anteriores a a[i] son menores que cero, los elementos desde a[i] hasta a[j-1] son mayores que cero y los elementos desde a[j] en adelante son desconocidos < 0 >=0 desconocidos i j