Universidad Simón Bolívar Departamento de Computación y Tecnología de la Información CI2611 – Algoritmos y Estructuras I Septiembre-Diciembre 2009 Carnet: Nombre: Examen II (40 puntos) Antes de empezar, revise bien el examen, el cual consta de 4 (CUATRO) preguntas. Pregunta 0 Pregunta 1 Pregunta 2 Pregunta 3 Total 15 puntos 10 puntos 5 puntos 10 puntos 40 puntos Pregunta 0 - 15 puntos Continuando con el estilo de las evaluaciones anteriores (el quiz y el examen I), se desea construir un programa que realice un cierto análisis financiero a una serie de empresas. Se trabajará con la información de p empresas, todas ellas pertenecientes a un mismo consorcio, lo cual significa que las p empresas conforman colectivamente una gran macro-empresa. Como dato de entrada contaremos con la ganancia o pérdida mensual de los últimos q meses de las p empresas en cuestión, información que vendrá dada en una matriz v, esto es, un arreglo bidimensional, con p filas y q columnas. También se tendrá como otro dato de entrada el capital y de todo el consorcio empresarial al inicio de esos q meses, esto es, el capital inicial de la gran macro-empresa conformada por las p pequeñas empresas. En cuanto al análisis a realizar, se debe determinar si para alguna de las p empresas su máxima ganancia en el período de q meses dado fue exactamente igual a 0,4 % del capital inicial de todo el consorcio. Por otra parte, el capital y de todo el consorcio debe ser actualizado a su valor final luego de los q meses en cuestión. La especificación formal con la que debemos trabajar este problema algorítmico es la siguiente: [[ const p , q : int ; v : array [ 0..p ) × [ 0..q ) of real ; var y : real ; f : boolean ; p>0 ∧ q>0 ∧ y=A RealizarAnálisisFinancieroActualizandoCapital ( f ≡ ( ∃ i : 0 6 i < p : ( max j : 0 6 j < q : v[i][j] ) = 0,004 ∗ A ) ) ∧ P (y = A + ( i, j : 0 6 i < p ∧ 0 6 j < q : v[i][j] ) ) ]] Note que A es una variable de especificación y no una variable de programa. En relación con el operador matemático max se tienen las dos siguientes observaciones: (i) debe ser considerado un operador sin elemento neutro, por lo cual sus cuantificaciones no están bien definidas cuando el rango es vacío; y (ii) sólo puede ser utilizado en especificaciones (aserciones) pero no en instrucciones del programa. Se ha decidido además que su programa debe primero resolver el subproblema de determinar si alguna de las empresas tuvo la máxima ganancia estipulada y calcular el balance acumulado de ganancias y pérdidas. Una vez resuelto este subproblema se debe completar una solución al problema completo llamado RealizarAnálisisFinancieroActualizandoCapital. La aserción intermedia correspondiente a resolver el primer subproblema es la siguiente, en la cual se hace referencia a una nueva variable t de tipo real que debe ser declarada como parte del programa solución: ( f ≡ ( ∃ i : 0 6 i < p : ( max j : 0 6 j < q : v[i][j] ) = 0,004 ∗ A ) ) ∧ P (t = ( i, j : 0 6 i < p ∧ 0 6 j < q : v[i][j] ) ) . Por último, este primer subproblema debe ser resuelto con una iteración diseñada según el siguiente invariante, resultante de haber aplicado la técnica de reemplazo de una expresión constante por una nueva variable, que en este caso hemos llamado k y es de tipo int: 06k6p ∧ ( f ≡ ( ∃ i : 0 6 i < k : ( max j : 0 6 j < q : v[i][j] ) = 0,004 ∗ A ) ) ∧ P (t = ( i, j : 0 6 i < k ∧ 0 6 j < q : v[i][j] ) ) . Construya entonces un programa que resuelva el problema RealizarAnálisisFinancieroActualizandoCapital de acuerdo con las instrucciones dadas. Para la iteración a construir con el invariante ya dado, indique también la cota variante de la iteración. Si su solución requiere de otras iteraciones, indique también el invariante y la cota variante de ellas. Nota: Su programa debe acoplarse a la solución parcial propuesta. La aserción intermedia dada y el invariante dado deben ser respetados. Otra nota: No puede utilizar recursión. Pregunta 1 - 10 puntos Se desea utilizar recursión para construir un programa que imprima los dígitos decimales de un entero no-negativo. La secuencia completa de dígitos deberá ser impresa entre llaves angulares (“h” y “i”), los dígitos deberán ser impresos separados por comas (“,”-s), y el orden de impresión de los dígitos deberá ser en orden decreciente de significación. Por ejemplo: si la entrada es el entero 2009, la salida a imprimir será el string “h2, 0, 0, 9i”, y si la entrada es el entero 715, la salida a imprimir será el string “h7, 1, 5i”. Note el orden decreciente de significación de los dígitos en ambos casos, lo cual quiere decir que el primer dígito impreso fue el más significativo, 2 en el caso de 2009 y 7 en el caso de 715, y el último dígito impreso fue el menos significativo, 9 en el caso de 2009 y 5 en el caso de 715. Debe evitarse imprimir dígitos no-significativos, por lo que secuencias de ceros en el extremo izquierdo no deben aparecer en la salida. Por ejemplo: para la entrada 2009, no se debe imprimir “h0, 0, 0, 2, 0, 0, 9i” ni “h0, 2, 0, 0, 9i”. La única excepción en la que un cero será impreso en el extremo izquierdo de la secuencia de salida es cuando el entero de entrada sea el entero 0, en cuyo caso la salida deberá ser la secuencia unitaria “h0i”. Esto es, para el entero de entrada 0 consideraremos al dígito cero como su (único) dígito significativo. De esta manera, las secuencias de dígitos decimales de salida nunca son vacías, nunca empiezan por cero para entradas estrictamente positivas, y son únicas para cada posible entrada. La especificación del programa a construir es la siguiente: [[ const n : int ; n>0 ImprimirSecuenciaDígitosDecimales En la salida se encuentra impresa la secuencia no-vacía de dígitos decimales significativos de n, en orden decreciente de significación, separados por “,”-s y con un “h” al inicio y un “i” al final. ]] Para resolver el problema, se ha decidido que Ud. debe apoyarse en un procedimiento auxiliar recursivo con la siguiente especificación: proc imprimirDigs ( in x : int ) Pre : x > 0 Post : En la salida se encuentra impresa la secuencia no-vacía de dígitos decimales significativos de x, en orden decreciente de significación y separados por “,”-s. Note las diferencias entre la especificación de este procedimiento auxiliar y la especificación del problema original completo: (i) el procedimiento no acepta al cero como entrada mientras el problema original sí, y (ii) el procedimiento no debe imprimir las llaves angulares mientras que en el problema original éstas sí deben ser impresas. Construya entonces un procedimiento recursivo que satisfaga la especificación dada para imprimirDigs, indicando la cota variante correspondiente que asegure terminación, y luego construya un programa principal que resuelva el problema original ImprimirSecuenciaDígitosDecimales haciendo uso de este procedimiento. Para realizar la impresión, suponga que cuenta con dos procedimientos ya construidos printStr y printInt que permiten imprimir, respectivamente, un string dado o un entero dado. Esto es, la llamada printStr (s) imprime el string s y la llamada printInt (a) imprime el entero a. Por ejemplo: printStr (“h”) imprime una llave angular izquierda, esto es, imprime el string “h”, y printInt (25) imprime el entero 25, esto es, imprime el string “25”. Nota: Su procedimiento y su programa deben acoplarse a las especificaciones dadas. Además, no puede utilizar estructuras de datos auxiliares, esto es, arreglos auxiliares. Otra nota: No puede utilizar iteración. Pregunta 2 - 5 puntos Teniendo como contexto a la siguiente declaración de variables var p , q , n : int ; v : array [ 0..n ) of real ; demuestre la tripleta de Hoare 0 6 p < n ∧ 0 6 q < n ∧ v[p] 6 v[q] v[p] 6 v[q] v[p] := (v[p] + v[q]) / 2 . Pregunta 3 - 10 puntos Suponga que se cuenta con un procedimiento ya construido cuyo encabezado es proc calcularLogaritmo (in a, b : int ; out c : real ) Pre : a > 2 ∧ b > 1 Post : c = loga b , el cual Ud. puede utilizar para resolver el siguiente problema: [[ const p , q : int ; var y : real ; 26p6q CalcularExpresiónInteresante Q y = ( i : p 6 i < q : logi (i + 7) ) ]] . Se tiene además la siguiente solución parcial al problema CalcularExpresiónInteresante: [[ var k : int ; y, k := ? , ? ( ) Q Invariante : 2 6 p 6 k 6 q ∧ y = ( i : k 6 i < q : logi (i + 7) ) Cota variante : ; do k 6= ? ? → [[ var tmp : real ; k := ? ; ? ; y := ? ]] od ]] Ud. debe completar este programa rellenando las incógnitas ? , y realizar sólo una parte de la demostración de correctitud de su programa, según se indica a continuación: (a) Rellene las incógnitas ? de forma tal que el resultado sea una solución correcta. (b) De acuerdo con las condiciones que deben cumplirse según el Teorema de Invariancia para que la solución dada sea correcta, demuestre (sólo) que el cuerpo de la iteración mantiene el invariante. Nota: No puede cambiar nada de la solución parcial propuesta, excepto rellenar las incógnitas. Otra nota: El procedimiento calcularLogaritmo ya está previamente construido y se sabe que funciona correctamente.