LENGUAJES DE PROGRAMACIÓN Solución al Trabajo Práctico - Junio de 2016 EJERCICIO 1 La distancia entre dos puntos en el plano, pi = {xi , yi } y pj = {xj , yj }, se calcula de la forma siguiente: di,j = q (xi − xj )2 + (yi − yj )2 donde las coordenadas xi , yi , xj e yj son números reales. Escriba un programa en C++ que realice las acciones siguientes: 1. Solicitar al usuario que éste introduzca por consola los valores de las coordenadas x1 , y1 , . . . , x5 , y5 de 5 puntos, y leer de la consola dichos valores. 2. Sean p1 = {x1 , y1 }, . . . , p5 = {x5 , y5} los cinco puntos leídos de la consola. Calcular la suma de la distancia entre puntos consecutivos, es decir; D= 4 X di,i+1 i=1 y mostrar dicho valor en la consola. Esta distancia es la longitud del camino p1 → p2 → p3 → p4 → p5 . 3. Para los cinco puntos leídos de la consola, calcular qué camino o caminos tienen la longitud mínima, satisfaciendo que comienzan en p1 , finalizan LENGUAJES DE PROGRAMACIÓN en p5 , y pasan una única vez, en cualquier orden, por los puntos p2 , p3 y p4 . Mostrar el valor de dicha distancia mínima en la consola, así como el correspondiente camino o caminos. 4. Terminar. Solución al Ejercicio 1 #include <iostream> #include <cmath> #include <sstream> #include <limits> const int N = 5; // Número de puntos double x[N], y[N]; // Coordenadas x,y de los puntos double distancia(int i, int j) { return std::sqrt( std::pow(x[i]-x[j],2.0) + std::pow(y[i]-y[j],2.0) ); } Código 1.1: Solución al Ejercicio 1 (parte 1 de 2). 2 Dpto. de Informática y Automática, UNED SOLUCIÓN AL TRABAJO PRÁCTICO - JUNIO DE 2016 int main() { // Entrada por consola std::cout << "Introduzca las coordenadas:\n"; for (unsigned int i = 0; i < N; i++) { std::cout << "Punto " << i+1 << "\nX: "; std::cin >> x[i]; std::cout << "Y: "; std::cin >> y[i]; } // Camino p1 -> p2 -> p3 -> p4 -> p5 double D = 0; // Longitud del camino for (unsigned int i = 0; i < N-1; i++) D += distancia(i, i+1); std::cout << "Longitud camino p1->p2->p3->p4->p5: " << D << std::endl; } // Camino/s de longitud minima std::stringstream ss; D = std::numeric_limits<double>::max(); for (unsigned int i1 = 1; i1 < N-1; i1++) { double D1 = distancia(0, i1); for (unsigned int i2 = 1; i2 < N-1; i2++) { if ( i2 != i1 ) { double D2 = D1 + distancia(i1, i2); for (unsigned int i3 = 1; i3 < N-1; i3++) { if ( i3 != i1 && i3 != i2 ) { double D3 = D2 + distancia(i2,i3) + distancia(i3,N-1); if ( D > D3 ) { ss.clear(); ss.str(std::string()); ss << "Distancia minima: " << D3 << "\nCamino: 1->" << i1+1 << "->" << i2+1 << "->" << i3+1 << "->" << N; D = D3; } else if ( D == D3 ) { ss << "\nCamino: 1->" << i1+1 << "->" << i2+1 << "->" << i3+1 << "->" << N; } } } } } } std::cout << ss.str() << std::endl; return 0; Código 1.2: Solución al Ejercicio 1 (parte 2 de 2). Dpto. de Informática y Automática, UNED 3 LENGUAJES DE PROGRAMACIÓN EJERCICIO 2 En la Figura 1.1 se muestra un objeto de masa M que puede deslizar con rozamiento sobre el suelo y que se encuentra unido a una pared inmóvil mediante un muelle. Muelle Objeto de masa M Pared Suelo Figura 1.1: Sistema mecánico. La evolución en el tiempo de la velocidad v y la posición x del objeto viene descrita por las ecuaciones siguientes: M· dv = −K · (x − L0 ) − b · v dt dx = v dt donde K es la constante de proporcionalidad del muelle, L0 es su elongación natural, y b es la constante de rozamiento entre el objeto y el suelo. Se supone que el valor de cada una de estas magnitudes, así como la masa del objeto, permanecen constantes en el tiempo. Aplicando el método de integración de Euler explícito a las dos ecuaciones anteriores se obtiene: M· vi+1 − vi = −K · (xi − L0 ) − b · vi h xi+1 − xi = vi h donde xi , vi representan la posición y la velocidad del objeto en el instante de tiempo t = i · h, y donde h es el tamaño del paso de integración. 4 Dpto. de Informática y Automática, UNED SOLUCIÓN AL TRABAJO PRÁCTICO - JUNIO DE 2016 Manipulando las expresiones anteriores puede obtenerse la posición xi+1 y la velocidad vi+1 en el instante (i + 1) · h, a partir de la posición xi y la velocidad vi en el instante i · h, conocidas las magnitudes constantes K, L0 , M y b, y el valor del paso de integración h. b·h K·h · (xi − L0 ) − · vi M M = xi + h · vi vi+1 = vi − (1.1) xi+1 (1.2) para i = 0, . . . , N − 1. Escriba un programa en C++ que realice las acciones siguientes: 1. Declarar dos constantes, una int llamada N y otra double llamada h, y asignarles respectivamente los valores 1000 y 0.01. 2. Solicitar al usuario que éste introduzca por consola los valores de las magnitudes K, L0 , M y b, las cuales deben ser números reales mayores que cero. Si no se satisface esta condición, el programa debe mostrar un mensaje de error y terminar. 3. Solicitar al usuario que éste introduzca por consola el valor inicial de la posición, x0 . El valor de x0 debe ser un número real en el intervalo [0.8 · L0 , 1.2 · L0 ]. Si no se satisface esta condición, el programa debe mostrar un mensaje de error y terminar. 4. Calcular xi y vi , para i = 1, . . . , N, empleando para ello las Ecs. (1.1) y (1.2). Se considera que la velocidad inicial siempre vale cero, es decir, v0 = 0. Los valores xi y vi , con i = 0, . . . , N, deben almacenarse en sendos arrays de N + 1 componentes de tipo double. 5. Grabar en un fichero de texto llamado sistMecanico.txt los valores ti , xi , vi con i = 0, . . . , N. El fichero deberá tener tres columnas (tiempo, posición y velocidad) y N + 1 filas. Los valores de la posición y la velocidad se mostrarán en formato fijo, con 5 dígitos detrás del punto decimal. El tiempo se mostrará en formato fijo, con 2 dígitos detrás del punto decimal. 6. Terminar. Dpto. de Informática y Automática, UNED 5 LENGUAJES DE PROGRAMACIÓN Solución al Ejercicio 2 #include <iostream> #include <fstream> #include <iomanip> const int N = 1000; const double h = 0.01; const char nombreFich[] = "sistMecanico.txt"; int main() { // Entrada por consola std::cout << "Introduzca K, L0, M y b: "; double K, L0, M, b; std::cin >> K >> L0 >> M >> b; // Comprobación de las entradas if ( K <= 0 || L0 <= 0 || M <= 0 || b <= 0 ) { std::cout << "Entrada no valida" << std::endl; return 0; } // Valor inicial de la posicion std::cout << "Introduzca x0: "; double x[N+1]; std::cin >> x[0]; if ( x[0] < 0.8*L0 || x[0] > 1.2*L0 ) { std::cout << "Entrada no valida" << std::endl; return 0; } // Evolucion de la posicion y velocidad double v[N+1]; v[0] = 0; // Velocidad inicial siempre vale cero for (unsigned int i=0; i<N; i++) { v[i+1] = v[i] - K*h/M*(x[i] - L0) - b*h/M*v[i]; x[i+1] = x[i] + h*v[i]; } // Escritura resultados en fichero std::ofstream fileOut(nombreFich, std::ios::out | std::ios::trunc); if ( !fileOut ) { std::cout << "Error al abrir fichero" << std::endl; return 1; } double t = 0; for ( unsigned int i=0; i<=N; i++) { fileOut << std::fixed << std::setprecision(2) << t << "\t" << std::setprecision(5) << x[i] << "\t" << v[i] << std::endl; t += h; } fileOut.close(); return 0; } Código 1.3: Solución al Ejercicio 2. 6 Dpto. de Informática y Automática, UNED SOLUCIÓN AL TRABAJO PRÁCTICO - JUNIO DE 2016 EJERCICIO 3 El método descrito a continuación es uno de los más sencillos y rápidos para generar observaciones de una variable aleatoria discreta X. 1. Obtener una observación independiente u de una distribución U(0, 1). 2. Calcular: (1.3) i = ⌈u · n⌉ La función ⌈x⌉, denominada ceil, devuelve el menor número entero que es mayor o igual que x. Por ejemplo, ⌈3.1⌉ y ⌈3.8⌉ valen 4. El valor entero n es el número de componentes de un array predeterminado (a1 , . . . , an ), que se calcula a partir de la función de probabilidad de la variable aleatoria X. 3. Devolver ai . Esto es, el componente i-ésimo del array (a1 , . . . , an ). A continuación se muestra un ejemplo. Supongamos que se desea generar observaciones de una variable aleatoria discreta X, cuyo conjunto de valores posibles es {1, 2, . . . , 10}, y cuya función de probabilidad es: fX (x) = x 55 con x = 1, . . . , 10 Es decir, la probabilidad de que la variable valga 1 es 3 la de que valga 3 es 55 y así sucesivamente. 1 , 55 (1.4) la de que valga 2 es 2 , 55 Las observaciones pueden generarse aplicando el algoritmo anterior, empleando el siguiente array de n = 55 elementos: 1, 2, 2, 3, 3, 3, 4, 4, 4, 4, . . . , 10, . . . , 10 (1.5) Obsérvese que el primer componente del array tiene valor 1, los dos siguientes valor 2, los tres siguientes valor 3, y así sucesivamente. Los 10 últimos componentes del array tienen valor 10. Dpto. de Informática y Automática, UNED 7 LENGUAJES DE PROGRAMACIÓN Escriba un programa en C++ que genere 10000 observaciones independientes de la variable aleatoria X, cuya función de probabilidad se muestra en (1.4). A continuación, el programa debe calcular la probabilidad empírica a partir de esos datos y compararla con la teórica. El programa debe realizar las acciones siguientes: 1. Declarar dos constantes int llamadas N y n, y asignarles los valores 10000 y 55 respectivamente. 2. Declarar un array llamado a con n componentes int. Empleando bucles for, asignar valor a los componentes del array tal como se muestra en (1.5). 3. En un fichero de texto llamado obsX.txt, escribir N = 10000 observaciones aleatorias independientes de la variable aleatoria discreta X, cuya probabilidad fX (x) es (1.4), empleando para ello el algoritmo descrito anteriormente. Para obtener u1 , . . . , uN puede emplear el generador de números pseudoaleatorios que desee. Por ejemplo, la función rand(). 4. Leer los datos del fichero obsX.txt y calcular la probabilidad con la que aparece cada uno de los diez posibles valores de X: {1, 2, . . . , 10}. 5. Mostrar en la consola, para cada uno de los 10 posibles valores de X: – la probabilidad empírica calculada de los datos del fichero, – la correspondiente probabilidad teórica (véase (1.4)) y – la diferencia entre ambas. Las probabilidades y sus diferencias se mostrarán en formato fijo, con 4 dígitos detrás del punto decimal. 6. Terminar. 8 Dpto. de Informática y Automática, UNED SOLUCIÓN AL TRABAJO PRÁCTICO - JUNIO DE 2016 Solución al Ejercicio 3 #include <iostream> #include <iomanip> #include <fstream> #include <string> #include <cstdlib> #include <cmath> #include <time.h> // rand(), RAND_MAX // ceil() // time const std::string nombreFich = "obsX.txt"; const int n = 55; const int N = 10000; int main() { // Declaración y asignación de valores al array a int i = 0; int a[n]; for (unsigned int k = 1; k <= 10; k++) { for (unsigned int j = 1; j <= k; j++) { a[i] = k; i++; } } // Apertura del fichero para escritura std::ofstream fileOut(nombreFich.c_str(), std::ios::out | std::ios::trunc); if ( !fileOut ) { std::cout << "Error al abrir fichero" << nombreFich << std::endl; return 1; } // Escritura en fichero de N observaciones independientes de X srand (time(NULL)); // Inicializa la semilla de rand() for (unsigned int k = 0; k < N; k++) { double u = ((double) rand() / RAND_MAX); int i = std::ceil(u*n); fileOut << a[i-1] << std::endl; } fileOut.close(); Código 1.4: Solución al Ejercicio 3 (parte 1 de 2). Dpto. de Informática y Automática, UNED 9 LENGUAJES DE PROGRAMACIÓN } // Apertura del fichero para lectura std::ifstream inFich(nombreFich.c_str(), std::ios::in); if (!inFich) { std::cout << "ERROR al abrir fichero " << nombreFich << std::endl; return 1; } // Lectura del fichero y cálculo de la probabilidad int nX[10] = { }; // Número de veces leído cada valor de X int numTotalValores = 0; while ( !inFich.eof() ) { int num; inFich >> num; if (inFich.eof()) break; numTotalValores++; nX[num-1]++; } inFich.close(); // Escritura de resultados en la consola for (unsigned int k=0; k<10; k++) { double p_calc = (double) nX[k] / numTotalValores; double p_teorica = (double) (k+1) / n; std::cout << "x = " << k+1 << "\t" << std::fixed << std::setprecision(4) << "p(calculada) = " << p_calc << "\t" << "p(teorica) = " << p_teorica << "\t" << "diferencia = " << p_teorica - p_calc << std::endl; } return 0; Código 1.5: Solución al Ejercicio 3 (parte 2 de 2). 10 Dpto. de Informática y Automática, UNED SOLUCIÓN AL TRABAJO PRÁCTICO - JUNIO DE 2016 EJERCICIO 4 Se propone realizar un programa en C++ que obtenga el trazo dibujado por una tiza en su movimiento sobre una pizarra. Las acciones a realizar por la tiza se leerán de un fichero de texto. Éstas pueden ser: apoyarse sobre la pizarra, levantarse de la pizarra y avanzar en la dirección indicada. A continuación se explica todo ello con detalle. La pizarra es una región plana cuadrada, recubierta por un mallado de N × N celdas cuadradas, donde N = 41. La celda (0, 0) se encuentra en el extremo inferior izquierdo de la pizarra y la celda (40, 40) en el extremo superior derecho. La tiza se encuentra inicialmente apoyada sobre la pizarra y situada en la celda (Ix , Iy ), donde Ix = Iy = 21. La tiza puede moverse en cuatro direcciones: hacia arriba, hacia abajo, hacia la izquierda y hacia la derecha. Si la tiza se mueve estando apoyada sobre la pizarra, entonces dibuja en la pizarra. Si por el contrario el movimiento se produce mientras la tiza no está apoyada sobre la pizarra, entonces ese movimiento no produce dibujo alguno. El movimiento de la tiza se describe mediante una secuencia arbitraria de los seis comandos siguientes: APOYA_TIZA LEVANTA_TIZA AVANCE_N AVANCE_S AVANCE_E AVANCE_O Apoya la tiza sobre la pizarra Levanta la tiza de la pizarra La tiza se desplaza una celda hacia arriba La tiza se desplaza una celda hacia abajo La tiza se desplaza una celda hacia la derecha La tiza se desplaza una celda hacia la izquierda La secuencia de comandos está escrita en un fichero de texto llamado comand.txt. Los comandos pueden estar separados entre sí por espacios en blanco y saltos de línea. El programa debe abrir dicho fichero e ir leyéndolo palabra a palabra. Si la palabra leída coincide con uno de los comandos, el programa debe realizar la acción indicada por el comando. Si no coincide, el programa debe mostrar en la consola un mensaje y terminar. El mensaje debe ser lo más descriptivo posible, indicando qué texto produce el error. La estructura de datos empleada en el programa para representar la pizarra y los trazos dibujados en ella deberá ser un array bidimensional N × N de bool. Cada componente está asociado con una de las celdas. Un componente del array vale Dpto. de Informática y Automática, UNED 11 LENGUAJES DE PROGRAMACIÓN true cuando la celda asociada ha sido visitada por la tiza, estando ésta apoyada sobre la pizarra. En caso contrario vale false. Una vez ejecutados los comandos escritos en comand.txt, el programa debe escribir en un fichero de texto llamado result.txt información relativa al valor del array bidimensional y a continuación debe terminar. La escritura en el fichero result.txt debe hacerse de la manera siguiente: – Cada fila del array debe escribirse en una línea del fichero. Las filas deben escribirse en orden decreciente, de manera que la última fila en ser escrita sea la cero. – Cada componente del array bidimensional dará lugar a la escritura de un punto o un asterisco: • Si vale false, se escribirá un punto. • Si vale true, se escribirá un asterisco. De esta forma, los trazos dibujados por la tiza quedarán representados mediante asteriscos. Las celdas que no han sido dibujadas por la tiza se representarán mediante puntos. El programa debe ir vigilando que la tiza no salga de la pizarra. Cuando se ejecute un comando que haga que la tiza salga de la pizarra (ya sea estando la tiza levantada o apoyada), el programa debe escribir en la consola un aviso indicándolo, escribir el fichero result.txt a partir del valor actual del array y terminar. A fin de ilustrar cuál debe ser el funcionamiento del programa, se muestran a continuación dos ejemplos. En cada caso se indica el contenido del fichero comand.txt, que es proporcionado por el usuario y leído por el programa, y del fichero result.txt, que es generado por el programa. 12 Dpto. de Informática y Automática, UNED SOLUCIÓN AL TRABAJO PRÁCTICO - JUNIO DE 2016 Ejemplo 1 Contenido del fichero comand.txt: AVANCE_N AVANCE_E AVANCE_E AVANCE_E AVANCE_E AVANCE_E AVANCE_N AVANCE_E AVANCE_E AVANCE_E AVANCE_E AVANCE_E AVANCE_N AVANCE_E AVANCE_E AVANCE_E AVANCE_E AVANCE_E AVANCE_N AVANCE_E AVANCE_E AVANCE_E AVANCE_E AVANCE_E AVANCE_N AVANCE_E AVANCE_E AVANCE_E AVANCE_E AVANCE_E Contenido del fichero result.txt: ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... .....................******************** .....................*................... .....................* ................... .....................*................... .....................*................... .....................* ................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... El programa debe escribir un mensaje en la consola indicando que la tiza ha salido de la pizarra. Dpto. de Informática y Automática, UNED 13 LENGUAJES DE PROGRAMACIÓN Ejemplo 2 Contenido del fichero comand.txt: AVANCE_E AVANCE_E AVANCE_E AVANCE_E AVANCE_E AVANCE_N AVANCE_N AVANCE_N AVANCE_N AVANCE_N LEVANTA_TIZA AVANCE_N AVANCE_N AVANCE_N AVANCE_N AVANCE_N AVANCE_O AVANCE_O AVANCE_O AVANCE_O AVANCE_O APOYA_TIZA AVANCE_N AVANCE_N AVANCE_N AVANCE_N AVANCE_N AVANCE_E AVANCE_E AVANCE_E AVANCE_E AVANCE_E AVANCE_E AVANCE_E AVANCE_E AVANCE_E AVANCE_E AVANCE_S AVANCE_S AVANCE_S AVANCE_S AVANCE_S LEVANTA_TIZA AVANCE_E AVANCE_E AVANCE_N APOYA_TIZA LEVANTA_TIZA AVANCE_E AVANCE_E AVANCE_N APOYA_TIZA LEVANTA_TIZA AVANCE_E AVANCE_E AVANCE_N APOYA_TIZA AVANCE_S AVANCE_S AVANCE_S AVANCE_S AVANCE_S Contenido del fichero result.txt: ......................................... ......................................... ......................................... ......................................... .....................***********......... .....................* .........*......... .....................*.........* .....*... .....................*.........*...*.* ... .....................* .........*.*...*... .....................*.........*.....*... .....................................*... .....................................*... ......................................... ......................................... .......................... .............. ..........................* .............. ..........................* .............. * ..........................*.............. ..........................* .............. .....................****** .............. ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... ......................................... En este caso el programa no debe mostrar ningún mensaje de error o aviso en la consola. 14 Dpto. de Informática y Automática, UNED SOLUCIÓN AL TRABAJO PRÁCTICO - JUNIO DE 2016 Solución al Ejercicio 4 #include <iostream> #include <fstream> #include <string> const std::string nombreFichI = "comand.txt"; const std::string nombreFichO = "result.txt"; const int N = 41; const int Ix = 21; const int Iy = 21; struct Tiza { int x; int y; bool apoyada; Tiza () : x(Ix), y(Iy), apoyada(true) {}; }; int main() { bool pizarra[N][N] = {}; // Declaracion e inicializacion de la pizarra Tiza tiza; // Estructura que describe el estado de la tiza // Posicion inicial de la tiza pizarra[tiza.x][tiza.y] = tiza.apoyada; // Apertura del fichero de comandos para lectura std::ifstream inFich(nombreFichI.c_str(), std::ios::in); if (!inFich) { std::cout << "ERROR al abrir fichero " << nombreFichI << std::endl; return 1; } Código 1.6: Solución al Ejercicio 4 (parte 1 de 3). Dpto. de Informática y Automática, UNED 15 LENGUAJES DE PROGRAMACIÓN // Lectura del fichero bool tizaFueraPizarra = false; while ( !tizaFueraPizarra && !inFich.eof() ) { std::string comando; inFich >> comando; if (inFich.eof()) break; // Ejecucion del comando if ( comando == "APOYA_TIZA" ) { tiza.apoyada = true; pizarra[tiza.x][tiza.y] = true; } else if ( comando == "LEVANTA_TIZA" ) { tiza.apoyada = false; } else if ( comando == "AVANCE_E" ) { tiza.x++; if ( tiza.x >= N ) { tizaFueraPizarra = true; } else if ( tiza.apoyada ) { pizarra[tiza.x][tiza.y] = true; } } else if ( comando == "AVANCE_O" ) { tiza.x--; if ( tiza.x < 0 ) { tizaFueraPizarra = true; } else if ( tiza.apoyada ) { pizarra[tiza.x][tiza.y] = true; } } else if ( comando == "AVANCE_N" ) { tiza.y++; if ( tiza.y >= N ) { tizaFueraPizarra = true; } else if ( tiza.apoyada ) { pizarra[tiza.x][tiza.y] = true; } } else if ( comando == "AVANCE_S" ) { tiza.y--; if ( tiza.y < 0 ) { tizaFueraPizarra = true; } else if ( tiza.apoyada ) { pizarra[tiza.x][tiza.y] = true; } } else { std::cout << "ERROR. Comando no valido: " << comando << std::endl; return 0; } } inFich.close(); Código 1.7: Solución al Ejercicio 4 (parte 2 de 3). 16 Dpto. de Informática y Automática, UNED SOLUCIÓN AL TRABAJO PRÁCTICO - JUNIO DE 2016 } // Mensaje si la tiza ha salido de la pizarra if ( tizaFueraPizarra ) std::cout << "AVISO. La tiza ha salido fuera de la pizarra" << std::endl; // Apertura del fichero para escritura std::ofstream fileOut(nombreFichO.c_str(), std::ios::out | std::ios::trunc); if ( !fileOut ) { std::cout << "Error al abrir fichero" << nombreFichO << std::endl; return 1; } // Escritura en el fichero for (int j = N-1; j >= 0; j--) { for (unsigned int i = 0; i < N; i++) { if ( pizarra[i][j] ) { fileOut << "*"; } else { fileOut << "."; } } fileOut << std::endl; } fileOut.close(); return 0; Código 1.8: Solución al Ejercicio 4 (parte 3 de 3). Dpto. de Informática y Automática, UNED 17