lunes, 31 de marzo de 2014

Examen parcial de Programación de Sistemas y Concurrencia. Curso 2011/2012.

Al igual que ayer os dejaba el ejercicio del parcial del curso pasado, hoy os dejo mi solución al ejercicio de C del primer parcial de Programación de Sistemas y Concurrencia del curso 2011/2012. Como ayer, digo que mi solución puede no ser correcta pero al menos podéis tomarla como guía y hacerle las modificaciones que veáis necesarias. Yo he usado la recursividad en los métodos que nos piden ya que no se me ocurría la forma iterativa de hacerlo, así que si encontráis diferentes formas de hacerlo podéis comentar ;). Aquí tenéis el enunciado para descargar.

Un árbol binario de búsqueda es un árbol binario en el que para cualquier nodo el subárbol izquierdo
(si no está vacío) contiene valores menores que el valor que contiene dicho nodo, y el subárbol derecho
(si no está vacío) contiene valores mayores. Así, por ejemplo, para la lista de números introducidos por
este orden:
21, 5, 78, 1, 7, 45, 90,4,6,15,77,80,99
Se crearía un árbol binario de búsqueda con la siguiente distribución:
Definir el tipo TArbol e implementar las siguientes operaciones:
void CrearABB (TArbol *arb)
Este procedimiento construye un árbol binario de búsqueda vacio.
void InsertarEnABB (TArbol *arb, int elem)
Este procedimiento inserta elem en un árbol binario de búsqueda. Después de la inserción el árbol
DEBE seguir siendo un árbol binario de búsqueda.
void RecorrerABB (TArbol arb)
Dado un árbol binario de búsqueda, este procedimiento muestra en pantalla los elementos del árbol
ordenados de menor a mayor. Para el dibujo de la figura la salida sería:
1,4,5,6,7,15,21,45,77,78,80,90,99
void DestruirABB(TArbol *arb)
Este procedimiento libera la memoria de todos los nodos del árbol.

#include <stdio.h>
#include <stdlib.h>

typedef struct TNodo* TArbol;
struct TNodo {
 int valor;
 TArbol hijoizquierdo;
 TArbol hijoderecho;
};

void CrearABB (TArbol *arb){//Este procedimiento construye un árbol binario de búsqueda vacio.
 *arb = NULL;
}

void InsertarEnABB (TArbol *arb, int elem){
 //Este procedimiento inserta elem en un árbol binario de búsqueda. Después de la inserción el árbol
 //DEBE seguir siendo un árbol binario de búsqueda.
 TArbol ptr = *arb;
 TArbol nuevo = malloc(sizeof(struct TNodo));
 int insertado = 0;
 nuevo ->valor = elem;
 nuevo -> hijoderecho = NULL;
 nuevo -> hijoizquierdo = NULL;

 if(ptr == NULL){ //árbol vacío
  *arb = nuevo;
 }
 while(ptr != NULL && !insertado){
  if(ptr->valor < nuevo ->valor  ){

   if(ptr -> hijoderecho == NULL){
    ptr -> hijoderecho = nuevo;
    insertado = 1;
   }
   ptr = ptr -> hijoderecho;


  }else if (ptr->valor > nuevo ->valor ){
   if(ptr -> hijoizquierdo == NULL){
    ptr -> hijoizquierdo = nuevo;

    insertado = 1;
   }
   ptr = ptr -> hijoizquierdo;
  }
 }
}

void RecorrerABB (TArbol arb){
 /*Para el dibujo de la figura la salida sería:1,4,5,6,7,15,21,45,77,78,80,90,99 */
  if(arb -> hijoizquierdo != NULL){
   RecorrerABB(arb ->hijoizquierdo);
  }

  printf(" %d ", arb -> valor);

  if(arb -> hijoderecho != NULL){
   RecorrerABB(arb -> hijoderecho);
  }

}


void DestruirABB(TArbol *arb){
 //Este procedimiento libera la memoria de todos los nodos del árbol.

 if((*arb) -> hijoizquierdo != NULL){
  DestruirABB(&((*arb) ->hijoizquierdo));
 }
 if((*arb) -> hijoderecho != NULL){
  DestruirABB(&((*arb) -> hijoderecho));
 }

 free(*arb);

}

Para ver que el árbol se forma correctamente he implementado un método en el que se representa el árbol pero girado 90º, pues así era más sencillo. Tenemos que pasarle como argumentos el árbol y el número de nodos de éste. Os lo dejo por si queréis probarlo.

void verABB(TArbol arbol, int n){
     if(arbol!=NULL){

     verABB(arbol->hijoderecho, n+1);
     int i;

     for(i=0; i<n; i++)
           printf("    ");

     printf("%d\n", arbol->valor);

     verABB(arbol->hijoizquierdo, n+1);
    }
}
Por último sólo quedaría hacer un main para probar los distintos métodos y el ejercicio quedaría terminado ;). Recomiendo probar con los nodos que da el ejercicio para ver que queda como la figura. Un saludo :)

domingo, 30 de marzo de 2014

Examen parcial de Programación de Sistemas y Concurrencia. Curso 2012/2013.

Os dejo mi solución al ejercicio de C del primer parcial de la asignatura de segundo de Informática Programación de Sistemas y Concurrencia del curso pasado, 2012/2013. Repito que es mi solución y puede no ser correcta, así que se aceptan sugerencias de mejora como siempre ;). Os dejo un enlace para que podáis descargarlo pero, como siempre, os dejo mi código aquí también.
Primero el enunciado del ejercicio, aunque subo el pdf para descargar.



Se desea simular el almacenamiento en memoria de un planificador de tareas basado en prioridades. El planificador gestiona la lista de tareas del sistema que están en estado ejecutable, es decir, que pueden ejecutarse en cuanto les llegue el turno. Cada tarea se representa con un identificador (char *id) que es una cadena de caracteres, y un número (int pri) que representa la prioridad de la tarea. El planificador ordena las tareas en función de su prioridad teniendo en cuenta que cuanto más grande sea pri la tarea tiene más prioridad. Además, en el caso de haya tareas de igual prioridad, la ordenación depende del momento en que llegan a la estructura. Como se puede ver en la figura, la tarea t2 está después de la tarea t1 y esto se deberá a que t2 habrá pasado al estado de “lista para ejecutar” después que t1:



Implementar las siguientes operaciones:

void crear(T_Planificador *planif);
Inicializa el planificador creando un planificador vacío.

void insertar_tarea(T_Planificador *planif,int pri,char *id);
Inserta una nueva tarea id de prioridad pri en el planificador planif. La lista está ordenada por prioridad y en el caso de que exista una tarea con la misma prioridad se almacenará por orden de llegada. El identificador de tarea es único.

void mostrar (T_Planificador planificador);
Muestra el estado del planificador.

void eliminar_tarea(T_Planificador *planif,char *id,unsigned *ok);
Dado un planificador, elimina una tarea id que está preparada para ejecución. En el caso de que no exista dicha tarea, se devolverá 0 en el parámetro ok. OK valdrá 1 en el caso de que se haya realizado el borrado.

void planificar(T_Planificador *planif);
Extrae de la estructura la tarea que le corresponde ejecutarse.

void destruir(T_Planificador *planif);
Destruye toda la estructura eliminando y liberando la memoria de todos los nodos.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct TNodo * T_Planificador;
struct TNodo {
 char *id;
 int pri;
 T_Planificador sig;
};

void crear(T_Planificador *planif){
 // Inicializa el planificador creando un planificador vacío.
 *planif = NULL;
}

void insertar_tarea(T_Planificador *planif,int pri,char *id){
 /*
  * Inserta una nueva tarea id de prioridad pri en el planificador planif. La lista está ordenada por
prioridad y en el caso de que exista una tarea con la misma prioridad se almacenará por orden de
llegada. El identificador de tarea es único.
  */
 T_Planificador ptr = *planif;
 T_Planificador ant = NULL;
 int insertado = 0;
 T_Planificador nuevo = malloc(sizeof(struct TNodo));
 nuevo ->id = id;
 nuevo->pri = pri;
 if(*planif == NULL){ // está vacío
  *planif = nuevo;
  nuevo->sig = NULL;
 }else{
 while(ptr != NULL && !insertado){
  if(ptr->pri > nuevo->pri){ //sigue por la lista, pues tiene menos prioridad
   ant = ptr;
   ptr = ptr -> sig;
  }else if (ptr->pri < nuevo->pri){
   if(ant != NULL){
    ant ->sig = nuevo;
    nuevo -> sig = ptr;
   }else{
    nuevo -> sig = ptr;
    *planif = nuevo;
   }
   insertado = 1;
  }else{
   nuevo -> sig = ptr -> sig;
   ptr -> sig = nuevo;
   insertado = 1;
  }
 }
 if(!insertado){ //por si es el último
   nuevo -> sig = NULL;
   ant -> sig = nuevo;
 }
 }

}

void mostrar (T_Planificador planificador){
 //Muestra el estado del planificador.
 printf("\nTareas del planificador por orden de ejecución (primera la que antes se ejecutará): \n");
 printf("Planificador ");
 while(planificador != NULL){
  printf("->  %d | %s  ",planificador->pri,  planificador -> id );
  planificador = planificador ->sig;
 }
}

void eliminar_tarea(T_Planificador *planif,char *id,unsigned *ok){
 /*
  * Dado un planificador, elimina una tarea id que está preparada para ejecución. En el caso de que
  no exista dicha tarea, se devolverá 0 en el parámetro ok. OK valdrá 1 en el caso de que se haya
 realizado el borrado.
  */
 T_Planificador ptr = *planif;
 T_Planificador ant = NULL;
 *ok = 0;
 while(!(*ok) && ptr != NULL){
  //int strcmp(const char *s1, const char *s2);
  if((strcmp(ptr ->id, id)) != 0   ){//sigo
   ant = ptr;
   ptr = ptr -> sig;
  }else{ //la hemos encontrado.
   if(ant != NULL){
    ant -> sig = ptr -> sig;

   }else{// es la primera
    *planif = ptr-> sig;
   }
   free(ptr);
   *ok = 1;

  }
 }


}
void planificar(T_Planificador *planif){
 //Extrae de la estructura la tarea que le corresponde ejecutarse.
 *planif = (*planif) -> sig;

}

void destruir(T_Planificador *planif){
 //Destruye toda la estructura eliminando y liberando la memoria de todos los nodos.

 T_Planificador ptr = *planif;
 while(ptr != NULL){
  *planif = ptr -> sig;
  free(ptr);
  ptr = *planif;
 }


}



int main(void) {
 //Podemos probar con los diferentes procedimientos para ver si funciona correctamente :)
 T_Planificador planificador;
 crear(&planificador);
 insertar_tarea(&planificador, 4, "t8");
 insertar_tarea(&planificador, 8, "t1");
 insertar_tarea(&planificador, 3, "t7");
 insertar_tarea(&planificador, 8, "t2");
 insertar_tarea(&planificador, 1, "t3");
 mostrar(planificador);
 planificar(&planificador);
 mostrar(planificador);
 planificar(&planificador);
 mostrar(planificador);
 destruir(&planificador);

 return EXIT_SUCCESS;
}

viernes, 28 de marzo de 2014

Ejercicios C++: sentencias de selección y bucles.

Os dejo unos ejercicios resueltos sencillitos de C++ para practicar tanto las sentencias de selección como los bucles.


Diseñar un algoritmo que lea por teclado dos números enteros y determine si uno es divisor del otro.
#include <iostream>
using namespace std;

int main() {
 int a, b;
 cout << "Introduce un entero: ";
 cin >> a;
 cout << "Introduce otro entero: ";
 cin >> b;
 if(a!=0 && b!=0){
 if(a%b == 0){
  cout << b << " es divisor de "<< a <<".";
 }else if(b%a == 0){
  cout << a << " es divisor de "<< b <<".";
 }else{
  cout << "Ninguno es divisor del otro.";
 }
 }
 return 0;
}
Diseñar un algoritmo para determinar si un número entero (leído por teclado) es múltiplo de 3, 4 o 5.
#include <iostream>
using namespace std;

int main() {
 int num;
 cout << "Introduce un entero: ";
 cin >> num;
 for(int i = 3; i <= 5; i++){
  if(num%i == 0){
   cout << num << " SI es múltiplo de "<< i << endl;
  }else{
   cout << num << " NO es múltiplo de "<< i << endl;
  }
 }
 return 0;
}
Diseñar un algoritmo para convertir un ángulo expresado en grados / minutos / segundos a
radianes.
#include <iostream>
using namespace std;

const double pi = 3.14159265358979323846;

int main() {
 double grados, minutos, segundos, radianes;
 cout << "Convertir ángulo a radianes." << endl;
 cout << "Introduce grados, minutos y segundos: ";
 cin >> grados >>minutos >> segundos;

 segundos = segundos / 60;
 minutos = minutos + segundos;
 minutos = minutos / 60;
 grados = grados + minutos;

 radianes = grados * pi / 180;
 cout << "Ángulo en radianes: "<< radianes;

 return 0;
}
Escribir un algoritmo para calcular el precio de una partida de artículos aplicando un descuento
creciente con la cantidad comprada según la siguiente tabla (la cantidad comprada se lee por
teclado):
                        Número de unidades         Precio unitario
                                     1                              100
                                     2                               95
                                     3                               90
                                 4 o más                         85

#include <iostream>
using namespace std;

int main() {
 int uni, precio;
 do{
 cout << "Introduce el número de unidades a comprar: ";
 cin >> uni;
 }while(uni < 1);

 switch(uni){
 case 1: precio = 100;
 break;
 case 2: precio = 2*95;
 break;
 case 3: precio = 3*90;
 break;
 default: precio = uni * 85;
 break;
 }

 cout << "El precio de la compra es "<< precio;



 return 0;
}
Confecciona un bucle que lea de teclado un texto carácter a carácter hasta localizar un punto, y que al final dé como salida el número de comas encontradas, y el número de caracteres leídos.
#include <iostream>
using namespace std;

int main() {
 int comas=0, car=0;
 char texto;
 cout << "Introduce un texto terminado en punto." << endl; // prints !!!Hello World!!!
 cin >> texto;
 while(texto != '.'){
  if(texto == ','){
   comas++;
  }
  car++;
  cin >> texto;
 }
 cout << endl;
 cout << "Número de comas: "<< comas << endl;
 cout << "Número de caracteres: "<< car;
 return 0;
}

Diseña un algoritmo que determine si la cadena abc aparece en una sucesión de caracteres
cuyo final viene dado por un punto.
#include <iostream>
using namespace std;

int main() {
 char cadena;
 bool si = false;

 cout << "Introduce cadena de caracteres terminada en punto." << endl; // prints !!!Hello World!!!
 cin.get(cadena);

 while((cadena != '.') && (!si)){
  if(cadena == 'a'){
   cin.get(cadena);
   if(cadena == 'b'){
    cin.get(cadena);
    if(cadena == 'c'){
     si = true;
     cin.get(cadena);
    }
   }
  }else{
   cin.get(cadena);
  }
 }
 if(si){
  cout << "Aparece la cadena 'abc' ";
 }else{
  cout << "No aparece la cadena 'abc'";
 }
 return 0;
}
Diseña un algoritmo que lea un número n por teclado y calcule el n-ésimo número de la serie
de Fibonacci. Los dos primeros números de esta serie son el cero y el uno, y a partir de éstos
cada número se calcula realizando la suma de los dos anteriores.
#include <iostream>
using namespace std;

int main() {
 int n, a, b, c;
 cout << "Cálculo del n-ésimo número de la serie de Fibonacci." << endl; // prints !!!Hello World!!!
 cout << "Introduce n: ";
 cin >> n;

 int i = 1;
 a = 0;
 b= 1;
 c = 0;

 if((n == 0) ||(n == 1)){
  c = n;
 }else{

  while(i<= (n -1)){
   c = a +b;
   a = b;
   b = c;

   i++;
  }
 }
 cout << "Dicho elemento es: " << c;

 return 0;
}
Escribe un algoritmo que encuentre el mayor, el menor y la media aritmética de una colección
de N números leídos por el teclado donde N es el primero de los números.
#include <iostream>
using namespace std;

int main() {
 int n, i, nm, mayor, menor;
 double suma = 0;
 i = 0;
 cout << "Introduce n: ";
 cin >> n;

 cin >> nm;
 suma = suma + nm;
 menor = mayor = nm;

 while(i < n-1){
  cin >> nm;
  suma = suma + nm;

  if(nm > mayor){
   mayor = nm;
  }
  if(nm < menor){
   menor = nm;
  }

  i++;


 }
 cout << "Media: "<< suma/n << endl ;
 cout << "Mayor: " << mayor << endl;
 cout << "Menor: " << menor << endl;

 return 0;
}


Escribe un algoritmo que lea un lista de números enteros terminada en 0, y que encuentre y
escriba en la pantalla la posición de la primera y de la última ocurrencia del número 12 dentro
de la lista. Si el número 12 no está en la lista, el algoritmo debería escribir 0. Por ejemplo, si el
octavo número de la lista es el único 12, entonces 8 sería la primera y la última posición de las
ocurrencias de 12.
#include<iostream>
using namespace std;

int main() {
 int num, prim= 0, ult= 0;
 int contador = 0;
 cout << "Cadena de números terminada en 0: ";
 cin >> num;
 while(num != 0){
  contador ++;

  if(num == 12){
   if(prim == 0){
    prim = contador;
    ult = contador;
   }else{
    ult = contador;
   }
  }
  cin >> num;

 }

 cout << "Primer 12: " << prim << endl;
 cout << "Último 12: " << ult << endl;
 return 0;
}

Hasta aquí la entrada, en la próxima de este lenguaje ya veremos los procedimientos y las funciones. Un saludo (:

miércoles, 26 de marzo de 2014

Examen Programación orientada a Objetos Sept 2012 Asignar Plazas

Buenas gente. Os traigo otro examen de Programación Orientada a Objetos (Java de primero, del segundo cuatrimestre. No voy a copiar los códigos porque haríamos esto interminable; por eso os pido que si, por cualquier razón, los enlaces no funcionan, aviséis y los resubiré lo antes posible ;)

Aquí tenéis las clases necesarias para implementarlo todo:
Con plaza
Con plaza familiar
Sin plaza
Sin plaza familiar
Solicitudes

Programa Principal
Programa Principal GUI

Enunciado en pdf

Las soluciones os las pongo linkeadas en los apartados, para mayor comodidad ;)

EDICION: Faltaban dos clases. Os las pongo a continuación. Gracias por avisar ;)
VistaAsignacion.java
PanelAsignacion.java

Se desea llevar a cabo la gestión informatizada de la asignación de plazas en un colegio de educación infantil y primaria. Para ello, se creará un proyecto prAsignacionPlazas con las clases siguientes:

1) (0.25 ptos.) La clase AsignacionException se utilizará para tratar diferentes situaciones excepcionales tal y como se indica más adelante. Se trata de una excepción no comprobada.

2) (1.5 ptos.) La clase Solicitante debe mantener información sobre un solicitante de plaza. En concreto, el NIF del tutor legal del solicitante (String), el nombre del solicitante (String), los puntos (double) del solicitante después de ser baremado, y una indicación (boolean) de si ese solicitante tiene plaza o no. También dispondrá del constructor y los métodos necesarios para:
- a. (0.25 ptos.) Construir un objeto de la clase a partir del NIF, nombre y puntos del solicitante. La representación de un solicitante (String toString()) viene dada por esos tres datos, separados por espacios en blanco.
- b. (0.25 ptos.) Obtener el NIF (String getNIF()), obtener el nombre (String getNombre()), obtener los puntos (String getPuntos()), ver si se le ha asignado una plaza (boolean getTienePlaza()) y asignarle o quitarle una plaza al solicitante (void setTienePlaza(boolean plaza)).
- c. (0.5 ptos.) Dos objetos de la clase Solicitante son iguales si coinciden sus nombres y sus puntos. No se tendrán en cuenta las diferencias por mayúsculas o minúsculas.
- d. (0.5 ptos.) Un objeto de la clase Solicitante s1 es menor que otro s2 cuando el número de puntos de s1 sea mayor que el número de puntos de s2. Si el número de puntos es igual, entonces s1 será menor cuando su nombre sea menor alfabéticamente (independientemente de minúsculas y mayúsculas) que el nombre de s2. 

3) (3.50 ptos.) La clase Colegio almacenará la información de los solicitantes en una estructura solicitudes de tipo SortedMap>, donde los posibles valores del campo clave de la estructura solicitudes serán “I3”, “I4” o “I5”, para hacer referencia a los cursos de infantil de 3, 4 ó 5 años, y “P1”, “P2”, “P3”,…,”P6”, para hacer referencia a los cursos de primaria entre 1º y 6º (no tiene por qué haber solicitudes para cada curso escolar). También dispondrá del constructor y los métodos necesarios para: 
- a. (1.75 ptos.) Construir un objeto de la clase a partir del nombre del fichero (String) que contiene la información necesaria para crear la aplicación solicitudes. Los datos para probar el correcto funcionamiento del examen son los que aparecen en el fichero solicitudes.txt proporcionado en el campus virtual. Una parte de este fichero se muestra a continuación para ver su formato. Para cada curso para el que haya solicitudes se indica: (1) una línea con el nombre del curso (ej. I3 en la primera línea del fichero) y el número de alumnos en ese curso (ej. 3 en la primera línea del fichero), y (2) una línea con los datos de cada solicitante incluyendo NIF del tutor, número de puntos y nombre del solicitante (ej. si para el curso I3 se indicaba que había 3 solicitudes, en el fichero habrá 3 líneas, una para cada solicitante). I3 3 111111110A 25 María Martín Arrebola 111111111B 26.5 Antonio Gómez Muñoz 111111112C 30 Antonio Pérez Gallardo I4 1 222222220A 14 Eva López García Cualquier error de formato detectado (falta algún dato del solicitante, o bien el dato no es del tipo correcto), provocará el lanzamiento de una excepción del tipo AsignacionException. 
- b. (1 pto.) Hacer la asignación de plazas (void asignarPlazas(Map plazas)). Este método recibe una aplicación donde se indica el número de plazas libres para cada curso escolar (ver programa Principal.java proporcionado en el campus virtual). Si para un determinado curso escolar no existe una entrada en la aplicación es que el número de plazas libres para ese curso es 0. Para cada curso en el que haya plazas libres habrá que modificar la aplicación solicitudes para indicar para cada solicitante si tiene o no tiene plaza. La asignación de plazas se realizará de la forma siguiente: Se le asignará plaza primero a los solicitantes que tengan más puntos. En caso de empate se le asignará plaza primero al que su nombre sea alfabéticamente menor. 
- c. (0.25 puntos) Hacer que todos los solicitantes queden sin plazas (void limpiar()). 
- d. (0.5 ptos.) Mostrar asignaciones de plazas con orden natural. Dos métodos, uno para mostrar sobre un PrintWriter la información de los solicitantes organizados por curso (void mostrarSolicitantes (boolean conPlaza, PrintWriter pw)) y otro para almacenarla en un fichero (void mostrarSolicitantes (boolean conPlaza, String nombreFichero)). Si el parámetro conPlaza es true, se mostrarán los solicitantes a los que se les ha podido asignar plaza. Si es false, se mostrarán los solicitantes a los que no se les ha podido asignar plaza. El formato de salida es como el mostrado en los ficheros conPlaza.txt y sinPlaza.txt proporcionados en el campus virtual. 

4) (2.75 ptos.) La clase ColegioFamiliar se comporta como la clase Colegio. La diferencia radica en que si dos hermanos piden plaza en el mismo colegio, en el mismo o en distinto curso, según la asignación de plazas que se realiza en la clase Colegio puede ocurrir que uno de los hermanos consiga la plaza y el otro no. Para evitar esto, en esta clase la asignación de plazas tiene en cuenta el hecho de que dos o más solicitantes pueden ser hermanos (son solicitantes que tienen el mismo NIF de su tutor) y siempre se cumplirá que o todos los hermanos consiguen plaza o ninguno de ellos la consigue. Para ello, dispondrá del constructor y los métodos necesarios para: 
- a. (0.25 ptos.) Construir un objeto de la clase a partir del nombre del fichero (String) que contiene la información necesaria para crear la aplicación solicitudes. El formato del fichero es el mismo que el usado para crear un objeto de la clase Colegio.  
- b. (2.5 ptos.) Una redefinición del método public void asignarPlazas(Map plazas) que se comporte de la siguiente forma: antes de asignar una plaza a un solicitante hay que localizar a todos sus hermanos (incluyendo al solicitante) y comprobar si sería posible asignarles plaza a todos (no se tendrá en cuenta la puntuación de los hermanos del solicitante, sólo si hay plaza o no disponible para todos ellos). Si es posible, se le asigna la plaza a todos ellos; en otro caso, no se le asigna a ninguno. Para ello, se deben implementar los siguientes métodos auxiliares: 
   1. <SortedMap> buscarHermanos ( String nif) que recibe un NIF y devuelve una aplicación donde el campo clave es el nombre de los cursos y el campo valor es la lista de hermanos que han solicitado plaza en ese curso (todos los hermanos tienen el mismo NIF de su tutor).    2. boolean hayPlazas(<SortedMap> hermanos, Map plazas), que recibe dos valores: el primero es la aplicación con la información de los hermanos obtenida con el método anterior; el segundo es otra aplicación donde el campo clave son los cursos y los valores son el número de plazas disponibles en ese curso. El método devuelve true si es posible asignarle una plaza a cada uno de los hermanos indicados en el primer parámetro, y false en caso contrario.   3. void asignarPlazaHermanos (<SortedMap> hermanos, Map plazas), que recibe también la información de los hermanos y las plazas disponibles en cada curso y, suponiendo como precondición que hay plaza para todos los hermanos hace las asignaciones de plazas, reduciendo el número de plazas disponibles de acuerdo a las asignaciones realizadas. 

5) (2 ptos.) La clase ControladorAsignacion controla e interactúa con el modelo (compuesto por las clases anteriormente descritas) y la vista (se proporcionan en el campus virtual la interfaz VistaAsignacion y la clase PanelAsignacion). El constructor debe habilitar la parte de inicialización de la vista (introducción del fichero de solicitudes, y los botones de iniciar la asignación) y mostrar un mensaje en la parte baja de la misma indicando al usuario que introduzca el nombre del fichero y pulse el botón iniciar que desee. El resto de la vista estará deshabilitado. La pulsación de uno de los botones de iniciar hará que se cree un objeto de la clase Colegio o de la clase ColegioFamiliar, se deshabilite la zona de inicialización, se habilite el resto de la vista y se muestre un mensaje al usuario invitándolo a realizar la asignación. Cada vez que el usuario pulse el botón “Realizar Asignación” se procederá a realizar la asignación a todos los solicitantes y todos los cursos. El resultado se mostrará en el área de texto y se escribirá en el fichero de salida especificado. El botón “Finalizar” deshabilita esta zona de proceso, limpia las zonas de texto y habilita la parte de inicialización de la vista para poder empezar de nuevo con otros ficheros de entrada. Si al pulsar un determinado botón, no se han introducido los datos necesarios en los campos de texto, se debe mostrar un mensaje de error en la parte baja de la vista. Las clases principales con ejemplos de uso de las clases anteriormente descritas (SSin GUIs y con GUIs) están disponibles en el campus virtual. 


Y esto es todo. Espero que comentéis con cualquier duda o problema con que os encontréis.

Saludos ;)

lunes, 24 de marzo de 2014

Listas enlazadas en C (II) .

Tal y como adelanté hace días, aquí una implementación más completa de listas enlazadas en C con métodos distintos a los que subí en esta entrada.
Os dejo el header y la implementación para que podáis descargarlas, aunque igualmente os lo pongo a continuación. Como veis también se incluye algo de manejo de ficheros, por lo que si no tenéis ni idea del tema os recomiendo ver la entrada de ayer en la que también se utilizan. Por último añadir que si veis algún fallo o tenéis alguna duda no tenéis más que decirlo. Un saludo (:


#ifndef LINKEDLIST_H_
#define LINKEDLIST_H_


struct _node;
typedef struct _node * Linked_List;
typedef struct _node{
 int value;
 Linked_List next;
} Node;


Linked_List create(); // Crea una lista enlazada vacía
void destroy(Linked_List * ptrL); // Libera la memoria de una lista
int is_empty(Linked_List l); // Devuelve verdadero si la lista está vacía
int contains(Linked_List l, int v); // Devuelve verdadero si la lista contiene el elemento v
int length(Linked_List l); // Devuelve el número de elementos de la lista
int insert(Linked_List * ptrL, int pos, int v); // Inserta v en la posición pos de la lista (*prtL).
            //Si ok, devuelve verdadero; si pos no está entre 0 y
            //length +1, entonces devuelve falso
int borrar(Linked_List * ptrL, int pos); // Borra el elemento pos de la lista, devolviendo verdadero
          // o falso según la operación se pueda realizar.
int getElement(Linked_List l, int pos); // Devuelve el elemento de la posición pos. Si esa posición
          // no existe, el comportamiento de la función no está definido.
Linked_List readFromFile(char * filename); // Asumiendo que filename contiene N líneas, donde cada línea
           // es un entero, lee el fichero y almacena su contenido
           //una lista que es devuelta como resultado. En caso de alguna
           //situación de error, la función devolverá NULL.
int writeToFile(Linked_List list, char * filename); // Escribe el contenido de la lista list en el fichero
             // denominado filaneme, almacenando cada elemento de la
             //lista una línea del fichero.  La función devuelve
             //verdadero o falso según se haya realizado con éxito o no


#endif /* LINKEDLIST_H_ */

Nota: No os asustéis por la extensión del código, pues gran parte son comentarios explicando el funcionamiento de los métodos.

#include <stdio.h>
#include <stdlib.h>
#include "ListasEnlazadas2.h"

Linked_List create(){
 Linked_List lista = NULL;
 return lista;
}

void destroy(Linked_List * ptrL){
 Linked_List auxiliar;
 while (*ptrL != NULL) {
  auxiliar=(*ptrL)->next;
  free(*ptrL);
  *ptrL=auxiliar;
 }
 /*
  Linked_List ptr = *ptrL;
   while(ptr != NULL){
     *ptrL = ptr->next;
     free(ptr);
     ptr = *ptrL;
   }
  */
}

int is_empty(Linked_List l){
 int bool = (l == NULL);
 return bool;
}

int contains(Linked_List l, int v){ // Devuelve verdadero si la lista contiene el elemento v
 Linked_List ptr = l;
 int bool = 0;
 while (ptr != NULL) {
  if(ptr-> value == v){
   bool = 1;
   break;
  }
  ptr = ptr->next;
 }
 return bool;
}
int length(Linked_List l){
 // Devuelve el número de elementos de la lista
 int contador = 0;
 while(l != NULL ){
  l = l -> next;
  contador++;
 }
 return contador;
}
int insert(Linked_List * ptrL, int pos, int v){
 // Inserta v en la posición pos de la lista (*prtL).
 //Si ok, devuelve verdadero; si pos no está entre 0 y
 //length +1, entonces devuelve falso
 Linked_List auxiliar;
 auxiliar = *ptrL;
 if(pos < 0 || pos > length(auxiliar)+1){
  return 0;
 }else{
  Linked_List  nodoanterior, ainsertar;
   int contador =0;
   nodoanterior = NULL;
   ainsertar=malloc(sizeof(struct _node));
   ainsertar -> value = v;

   while(auxiliar != NULL && contador < pos){
     nodoanterior = auxiliar;
     auxiliar = auxiliar -> next;

     contador++;
   }


   if(nodoanterior == NULL){ //tiene que ser el primero
    ainsertar->next = auxiliar; //lo meto al principio
    *ptrL = ainsertar;
   }else{
    ainsertar ->next = auxiliar;
    nodoanterior-> next = ainsertar;
   }
  return 1;
 }



}
int borrar(Linked_List * ptrL, int pos){
 // Borra el elemento pos de la lista, devolviendo verdadero
 // o falso según la operación se pueda realizar.
 int bool = 0;
 int contador = 0;
 Linked_List auxiliar, anterior, aux2;
 auxiliar = *ptrL;
 anterior = NULL;

 while(auxiliar != NULL && contador < pos){
  anterior = auxiliar;
  auxiliar = auxiliar -> next;

  contador++;
 }

 if(auxiliar!= NULL && contador == pos){
  bool = 1;
  if(anterior != NULL){ // no es el primer nodo
   anterior->next = auxiliar->next;
   free( auxiliar);
  }else{
   aux2 = auxiliar -> next;
   free(*ptrL);
   *ptrL = aux2;
  }

 }
 return bool;
}
int getElement(Linked_List l, int pos){
 // Devuelve el elemento de la posición pos. Si esa posición
 // no existe, el comportamiento de la función no está definido.
 int elemento; // Sale warning por no inicializar, pero no lo hacemos por no definir el comportamiento.
 int contador = 0;

 while(l != NULL && contador < pos){
  l = l -> next;
  contador++;
 }

 if(l != NULL && contador == pos){
  elemento = l -> value;
 }
 return elemento;
}
Linked_List readFromFile(char * filename){
 // Asumiendo que filename contiene N líneas, donde cada línea
 // es un entero, lee el fichero y almacena su contenido
 //una lista que es devuelta como resultado. En caso de alguna
 //situación de error, la función devolverá NULL.
 Linked_List l = create();
 FILE * f;
 int leido;
 int contador =0;
 if((f = fopen(filename, "r" )) != NULL){
  while (feof (f) == 0) {
   fscanf( f, " %d", &leido);
      insert(&l, contador, leido);
      contador ++;
  }
 }
 fclose (f);
 return l;
}
int writeToFile(Linked_List list, char * filename){
 // Escribe el contenido de la lista list en el fichero
 // denominado filaneme, almacenando cada elemento de la
 //lista una línea del fichero.  La función devuelve
 //verdadero o falso según se haya realizado con éxito o no
 int bool = 0;
 FILE * f;
 if((f = fopen(filename, "w")) != NULL){
  bool = 1;
  fprintf(f, "[");
  while(list != NULL){
   fprintf(f, "%d", list -> value);
   list = list->next;
   if (list != NULL) {
    fprintf(f, ", ");
   }
  }
  fprintf(f, "]");
 }
 return bool;
}
void mostrar (Linked_List lista){// Mostrar los elementos de la lista
 Linked_List ptr = lista;
 printf("[");
 while (ptr != NULL) {
  printf("%d", ptr->value);
  ptr = ptr->next;
  if (ptr != NULL) {
   printf(", ");
  }
 }
 printf("]\n");
}



int main(void) {
 Linked_List lista = readFromFile("text.txt");
 mostrar(lista);
 insert(&lista, 2, 5);
 mostrar(lista);
 writeToFile(lista, "salida.txt");
 // Probar todos los métodos como el usuario vea conveniente;)

 return EXIT_SUCCESS;
}


domingo, 23 de marzo de 2014

Algoritmo de Desencriptado en C

Aquí os traigo otros de los ejercicios en C que nos han puesto. Un algoritmo que desencripte. Aquí dos archivos encriptados para probar que desencripte: ARCHIVO1 ARCHIVO2

El enunciado es el siguiente:

Un algoritmo de cifrado por bloques opera con bloques de 64 bits y una clave de 128
bits siguiendo el siguiente procedimiento.

Para cada bloque de 64 bits (unsigned long v[2]), sea la clave k (unsigned long
k[4]), y delta una constante igual a 0x9e3779b9:

 Inicializar sum a 0xC6EF3720

Repetir 32 veces:
 Restar a v[1] la aplicación del operador XOR (^) a
(v[0] desplazado a la izquierda 4 bits +k[2]),
(v[0] + sum) y
 (v[0] desplazado a la derecha 5 bits)+k[3]

Restar a v[0] la aplicación del operador XOR (^) a:
(v[1] desplazado a la izquierda 4 bits + k[0])
(v[1]+ sum) y
(v[1] desplazado a la derecha 5 bits)+k[1]

 Restar a sum el valor de delta.

Al final de las 32 iteraciones, se tendrá en v el valor desencriptado de los 64 bits.

Realizar un programa en C que cargue en memoria dinámica el contenido de un fichero
encriptado, realice su desencriptado siguiendo el procedimiento descrito y lo almacene
en un fichero de salida (recordar liberar la memoria dinámica al final). Tanto el nombre
del fichero de entrada como el del fichero de salida son indicados como argumentos del
programa en la línea de comandos.

Como el tamaño del fichero original no tiene porqué ser múltiplo de 8 y el algoritmo de
desencriptado trabaja con bloques de 8 bytes (64 bits) hay que tener en cuenta las
siguientes indicaciones:
- Al comienzo del fichero encriptado se encuentra almacenado (unsigned long)
el tamaño del fichero original desencriptado. Este tamaño no hay que
almacenarlo en memoria dinámica.
- Al hacer el encriptado, si el tamaño del fichero original no era múltiplo de 8, se
tiene al final un bloque incompleto para aplicar el cifrado, por lo que
artificialmente se completa hasta tener 8 bytes. Estos valores de relleno están
almacenados también en el fichero encriptado, y deben también ser
desencriptados, pero no deben escribirse en el fichero de salida.
Por ejemplo, si el fichero original ocupa 30 bytes, al hacer el encriptado se tuvieron que
utilizar 32 bytes, esto es, se pusieron 2 valores de relleno. La longitud total del fichero
encriptado es 4 (almacenamiento de la longitud del fichero encriptado) + 30 + 2
(posiciones de relleno), esto es 36 bytes. Al hacer el desencriptado, se hará de 32 bytes,
pero sólo se escriben 30 en el fichero de salida.

Para realizar el desencriptado se recomienda definir una función con la siguiente
cabecera:
void decrypt(unsigned long* v, unsigned long* k);

donde v es el array de 2 unsigned long que se va a desencriptar y k es la clave
consistente en un array de 4 unsigned long con los siguientes valores: {128, 129,
130, 131}.

Se recomienda utilizar las siguientes funciones de C: fopen, fread, fwrite, fclose,
malloc, free y memcpy.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void decrypt(unsigned long* v, unsigned long* k){
 unsigned long sum = 0xC6EF3720;
 unsigned long delta = 0x9E3779B9;
 unsigned i;
 for(i=1; i <= 32; i++){
  v[1] = v[1]- (((v[0] << 4)+k[2])^(v[0] + sum)^((v[0] >> 5)+k[3]));
  v[0] = v[0] - (((v[1] << 4)+ k[0]) ^(v[1]+sum)^((v[1] >> 5)+ k[1]));
  sum = sum - delta;
 }
}

int main(int argc,char *argv[]) {
 FILE * f;
 FILE * salida;
 unsigned long tamsalida;
 unsigned long tamentrada;
 unsigned long v[2];
 unsigned long k[4] = {128, 129, 130, 131};
 //Si no usáramos argv...
 //char * ficheroEntrada = "imgno8.enc";
 //char * ficheroSalida = "salida.png";
 char * datos;
 int i, aux;


 // 1 - Leer el tamaño del fichero original que está al comienzo del fichero encriptado


 if((f = fopen(argv[1], "rb")) == NULL){
  printf("ERROR. El fichero no existe.");
  exit(-1);
 }

 fread(&tamsalida, sizeof(long), 1, f);

 //printf("Tamaño del fichero original: %ld \n", tamsalida);




 // 2 Calcular el tamaño de los datos
 if(tamsalida%8){
  tamentrada = tamsalida +(8-(tamsalida % 8));
 }else{//tamaño multiplo de ocho, no añadimos nada
  tamentrada = tamsalida;
 }
 //printf("Tamaño de los datos a desencriptar: %ld \n", tamentrada);


 // 3 - Almacenar datos en memoria

 datos = (char *) malloc(tamentrada * sizeof(char));

 fread(datos, sizeof(char), tamentrada, f);
 fclose(f);

 // 4 - Descrifrar y escribir
 char * ptr = datos;
 salida = fopen(argv[2], "wb");
 //BUCLE que meta lo que haya en v en el fichero salida. (para recorrer los datos usamos ptr)
 aux = tamsalida/8;

 for(i = 0; i < aux; i++){
  memcpy(v,ptr,8);
  decrypt(v,k);
  fwrite(v, sizeof(char), 8, salida);
  ptr = ptr + 8;
 }
 // fin del bucle
 if (tamsalida % 8 ) {

  memcpy(v,ptr,8);
  decrypt(v,k);
  fwrite(v,sizeof(char),tamsalida% 8,salida);
 }

 free(datos);

 fclose(salida);


 return EXIT_SUCCESS;
}


Os dejo también el código para descargar.

Un saludo (:

viernes, 21 de marzo de 2014

Función de evaluación para TicTacToe

Bueno. Pues resulta que en Sistemas Inteligentes nos mandaron implementar una función de evaluación (una heurística) para el tres en raya por el algoritmo Alfa-Beta. Ésta se usaría cuando limitaramos el tiempo de respuesta a cosas ínfimas como 1ms (pues de otra forma tendría tiempo para desarrollar un MiniMax y ésto no tendría sentido.

Ésta vez (a diferencia de las demás) publicaré todo el código previo únicamente para descargar. No para molestar sino porque son varias clases extensas.

Antes de nada debo avisar de que éste código tiene un dueño que no tiene nada que ver con el blog (excepto mi algoritmo). Por ésto mismo debo mencionar las librerías AIMA (Artificial Intelligence: A Modern Approach) que son las dueñas del código original. Aquí os pongo el proyecto y su .jar para importar.

Os cuento lo que pedía el profesor... Resulta que la clase que hace la búsqueda de forma iterativa (la clase IterativeDeepeningAlphaBetaSearch.java) tiene una función de evaluación que siempre devuelve 0.5, lo que hace que coja elementos al azar si no son terminales (me refiero a elementos pero recordad que estamos hablando de un árbol en Alfa-Beta).
Para ello dijo que lo que podíamos hacer era contar (en un estado determinado) las posibles soluciones (sin contar los empates) que podía haber y restarle a las del primero las del segundo para obtener el número "evaluación". El programa ya se encargará de usarlo como crea conveniente. Solo tenemos que buscar la función eval y arreglarla. Tenemos que implementarle algo así:

Espero que haya quedado claro... xP

Aquí tenéis la clase que hemos modificado para tener la solución.
Y aquí resuelto el código. Hemos modificado la clase IterativeDeepeningAlphaBetaSearch. Dentro de ésta hemos cambiado la función eval (solo el return) y hemos creado otras funciones que nos han ayudado a calcularlo todo. Espero que lo disfruteis pues me ha costado unas 15h sacarlo todo. (Lo que os he explicado es más o menos lo que me explicaron a mi. Ni como está formada la class STATE, ni la class PLAYER. Todo lo que descubrí lo hice por mi propia cuenta toqueteando).

Os cuento que la base de mi solución ha sido encontrar otra forma de dar la solución. Simplificando lo que hago es (tras ver que state era una string con forma de matriz que se iba actualizando con X y O) pasarla a 0, 1 y -1 y ver que me daba las mismas soluciones que como lo había pedido el profesor. Pongo foto explicativa:


Y ya sin más dilación, el trozo de código en el que he trabajado ;)


 protected double eval(STATE state, PLAYER player) {
  if (game.isTerminal(state)) {
   return game.getUtility(state, player);
  } else {
   maxDepthReached = true;
   return this.heuristic(state, player);
  }
 }
 
 private double heuristic(STATE state, PLAYER player) {
  
  System.out.println(state);
  
  int [][] tabla = tabla(state);
  for (int i = 0; i < 3; i++){
   for (int j = 0; j < 3; j++) {
    System.out.print(tabla[i][j] + " ");
   }
   System.out.println();
  }
  System.out.println();
  
  int heuristica = sumaColumnas(tabla)+sumaDiagonales(tabla)+sumaFilas(tabla);
  
  return heuristica;
 }
 
 private int[][] tabla(STATE state) {
  int[][] tb = {{5,5,5},{5,5,5},{5,5,5}}; //Por si hay algún fallo al introducir, que cante.
  Scanner sc = new Scanner(state.toString());
  sc.useDelimiter(" ");
  int acumulador = 0;
  while(sc.hasNext()) {
   String c = sc.next();
   
   switch (c) {
   case "-": case "\n-": tb[acumulador/3][acumulador%3] =  0;break;
   case "X": case "\nX": tb[acumulador/3][acumulador%3] =  1;break;
   case "O": case "\nO": tb[acumulador/3][acumulador%3] = -1;break;
   }
   
   acumulador++;
  }
  
  return tb;
 }

 
 /* Lo que hago es dar un valor de 1 al jugador que va primero y -1 al que va segundo.
  * El primero (por el algoritmo) intentará maximizar el resultado y el segundo, minimizarlo.
  * 
  * Por esto mismo cambio las X por 1 y las O por -1
  * Sumo el resultado de las diagonales, horizontales y verticales y me da lo mismo que si buscara
  * cuantas posibilidades de victoria y derrota tengo libres. ^^
  * 
  */
 
 private int sumaColumnas(int[][] table) {
  int suma = 0;
  for (int i = 0; i < 3; i++) {
   suma = suma + table[0][i];
   suma = suma + table[1][i];
   suma = suma + table[2][i];
  }
    
  return suma;
 }
 
 private int sumaFilas(int[][] table) {
  int suma = 0;
  for (int i = 0; i < 3; i++) {
   suma = suma + table[i][0];
   suma = suma + table[i][1];
   suma = suma + table[i][2];
  }
    
  return suma;
 }
 
 private int sumaDiagonales(int[][] table) {
  int suma = 0;
  for (int i = 0; i < 3; i++) {
   suma = suma + table[i][i]; //Diagonal normal
   suma = suma + table[i][i];
   suma = suma + table[i][i];
  }

   suma = suma + table[0][2]; //Diagonal inversa
   suma = suma + table[1][1];
   suma = suma + table[2][0];
  
    
  return suma;
 }
 

Si tenéis alguna duda no dudéis en preguntar. Espero seguir viéndoos por aquí.

Saludos ;)

miércoles, 19 de marzo de 2014

Encendido secuencial de LED en Arduino con procedimiento

Bueno, tras el post anterior de arduino, en este iremos algo más allá e implementaremos un procedimiento (secuencia) que encienda y apague leds. Luego llamaremos a éste procedimiento desde el loop principal. Los leds los conectaremos al GND y a los pines 5 a 8 (no olvidemos las resistencias o podremos quemarlos).
Aquí teneis un video hecho por mi de como sería:

El circuito deberá ser algo así:

NOTA: Parece que el azul está en AREF pero no es así, es un fallo de perspectiva. En realidad está en GND. Perdón ;)

El código a meter en el Arduino será el siguiente:
int tiempo=200;
int n;

void setup() { //comienza la configuracion
  for (n=5;n<9;n++) {
    pinMode (n, OUTPUT);
  }
}
//Este es el procedimiento.
void secuencia() {
  for (n=5;n<9;n++) {       //Para cada pin del 5 al 8
    digitalWrite (n, HIGH); //Encendemos
    delay (tiempo);         //Esperamos
    digitalWrite (n, LOW);  //Apagamos
    delay (tiempo);         //Esperamos
  }
}

void loop() {
  secuencia();
}

Espero que comencéis a notar la simplicidad que es el implementar en Arduino. Espero que os guste, a mi me encanta.

Saludos ;)

lunes, 17 de marzo de 2014

Encender y apagar led repetidamente en Arduino.

Bueno gente. Me voy a atrever a meter otro lenguaje más en el blog. Total, es de aprender a programar no?

En este caso vamos a usar un Arduino Uno. No necesitamos conectarle nada puesto que solo necesitariamos un led pero éste ya trae uno en el pin digital 13 (en la foto se ve arriba el número). De todos modos si teneis un led a mano conectadlo al pin 13 y al pin GND (con cuidado de no conectarlo del revés, de una forma funciona y de la otra no). No se va a romper tranquilos, solo que ahora los dos leds (el que has puesto y el que hay debajo del 13) se encenderán y apagarán a la vez.
Aquí tenéis el LED que trae de fábrica, para que sepáis cual es.

Y aquí donde tendríais que conectar el LED externo para que funcionara también.



AVISO: no useis vuestro propio led así "a pelo" con otro de los pines. El 13 incorpora una resistencia ya. Si usáis otro pin y no le ponéis resistencia podéis quemar el LED.

Como es el primer proyecto en Arduino explicaré por encima la temática que se usa para programar...

Primero se declaran variables, luego se abre un void setup() en el que se elegiremos que el pin 13 va a ser de salida y, por último, un void loop() el cual ya lleva implementado que es un bucle infinito. Por supuesto podeis crear nuevas funciones fuera de las mismas y llamarlas dentro como en cualquier otro lenguaje, pero hacerlo tras el loop() ;)

El código sería el siguiente:


//Creamos una variable que representará el pin que vamos a usar.
int LED = 13;
void setup() {  
//Avisamos que el pin 13 va a ser de salida.  
pinMode(LED, OUTPUT);  
//También hubiera valido poner pinMode(13, OUTPUT); pero estamos aqui para practicar, no?
}

void loop() {
  digitalWrite(LED, HIGH);  //Enciende el led
  delay(1000);              //Espera 1000 milisegundos
  digitalWrite(LED, LOW);   //Apaga el led
  delay(1000);              //Espera 1000 milisegundos y vuelve a empezar
}
No es necesario usar LED. Podemos usar todo el rato el número 13 y no cambiaría nada, pero cuando hacemos algo mas grandote, es más cómodo ir leyendo y escribiendo nombres que recordar que hacía cada número, con lo cual es una buena práctica :) Bueno eso es todo. Espero que os haya gustado y hayáis visto que Arduino es un lenguaje bastante intuitivo :) Saludos y hasta la próxima ^^

domingo, 16 de marzo de 2014

Ejercicios de C++: estructuras de control (iteración)

Seguimos avanzando con C++. Tras practicar las sentencias de selección  en la entrada del viernes, ahora vemos las estructuras de control iterativas en estos siete ejercicios.

Ejercicio 1
Escribe un programa que calcule la suma de los N primeros números enteros positivos (el número N se leerá por teclado). Implementa dicho programa utilizando cada una de las tres estructuras de iteración de C++: while, do-while y for.

#include <iostream>
using namespace std;

int main() {
 int num;
 unsigned suma;
 cout << "Programa que calcula la suma de los N primeros números enteros positivos." << endl;
 do{
  cout<<"Introduce N (>0): ";
  cin>>num;
 }while (num<0);



 //Mediante un for.
  suma  = 0;
  for(unsigned i = 1; i <= num; i++){
  suma = suma + i;
  }
  cout << "La suma es " << suma << endl;

 //Mediante while
  {
   unsigned i = 1;
   suma = 0;
   while(i <= num){
    suma = suma + i;
    i++;
   }
   cout << "La suma es " << suma << endl;
  }
 //Mediante do while.
  {
   unsigned i = 1;
   suma = 0;
   do{
    suma = suma +i;
    i++;
   }while(i <= num);
   cout << "La suma es " << suma << endl;
  }

 return 0;
}
Ejercicio 2
En una fábrica de coches se desea calcular el precio medio de un número de modelos de coche, leído desde teclado. Se pide dado un numero de modelos de coche, introducir el precio (en euros) de cada modelo de coche (para esto usaremos una estructura iterativa) y posteriormente calcular el precio medio de los modelos.
#include <iostream>
using namespace std;

int main() {
 int num;
 double suma, prec;
 suma=0.0;
 do{
  cout << "Introduzca el número de modelos de coche: ";
  cin >> num;
 }while(num <= 0);

 for(int i= 1; i <= num; i++){
  cout <<"Precio modelo "<<i<<":";
  cin >> prec;
  suma = suma + prec;
 }
 cout << "El valor medio de los " << num << " modelos de coche asciende a: " << suma/num <<" €";
 return 0;
}

Ejercicio 3
Diseña un programa en C++ que muestre por pantalla un tablero de ajedrez, donde las posiciones blancas serán mostradas con el carácter ‘B’ y las posiciones negras serán mostradas con el carácter ‘N’. Un tablero de ajedrez tiene 8 filas y 8 columnas.
#include <iostream>
using namespace std;

int main() {
 cout << "Tablero de ajedrez." << endl; // prints !!!Hello World!!!

 for(int i= 1; i <= 8; i++){
  for(int j = 1; j <= 8; j++){
   if ((i + j)%2 == 0){
        cout << " B ";
   }else {
        cout << " N ";
   }
  }
  cout << endl;
 }
 return 0;
}
Ejercicio 4
Escribe un programa que, dada una secuencia de caracteres terminada en un punto, nos devuelva la posición en la tabla ASCII asociada a cada uno de los caracteres leídos. Posteriormente y antes de finalizar mostraremos por pantalla el número total de caracteres leídos.
#include <iostream>
using namespace std;

int main() {
 char a;
 int num = 0;
 cout << "Escriba una secuencia de caracteres terminada en un punto." << endl; // prints !!!Hello World!!!
 cin >> a;

 while(a != '.'){
  cout << "Posición en la tabla ASCII de " << a << " es: " << int(a) << endl;
  num++;
  cin >> a;
 }
 cout << endl;
 cout << "Se han leído "<< num << " caracteres.";
 return 0;
}
Ejercicio 5
La constante matemática pi puede ser calculada con la siguiente fórmula:

Esta fórmula fue descubierta en el siglo XVII por un matemático inglés llamado J. Wallis. Escribe un programa que lea un valor entero, n, y a continuación calcule pi a partir de la anterior multiplicando las primeras n fracciones de la parte derecha de la fórmula. Para comprobar el correcto funcionamiento del programa, con el valor n=20, el valor de pi es aproximadamente 3.10352 (con n=300 el valor es 3.13898).

En este ejercicio me detengo un poco más, pues he estado buscando información sobre esta fórmula y la he visto expresada de maneras muy distintas, por lo que se me han ocurrido distintas maneras de hacerlo. La primera es la que hice en primer momento y es quizá la mas ruda. Con esta podemos llegar a unos valores muy aproximados de pi, pero no son los que aparecen en el enunciado. Sin embargo los valores obtenidos mediante el segundo programa sí son los que aparecen, pero para realizar este hay que pensar (o quizá buscar) algo más. Os dejo ambas soluciones.

#include <iostream>
using namespace std;

int main() {
 int n;
 cout << "Introduce n:";
 cin >> n;
 double pi =4, i = 2, j=3;
 int nmult = 0;

 while(nmult <= n){
  pi = pi * i/j;
  i = i + 2;
  nmult++;
  if(nmult <= n){
   pi = pi * i/j;
   j = j + 2;
   nmult++;
  }
 }
 cout << "Pi = "<< pi<< endl;
 return 0;
}
#include <iostream>
using namespace std;

int main() {
 int n;
 double pi=2.0, fraccion;
 do{
  cout<<"Introduce n: ";
  cin>>n;
 }while(n < 0);
 for (int i=1; i<=n; i++){
  fraccion = (4.0*i*i /(2*i-1))/(2*i+1);
  pi = pi* fraccion;
 }

 cout<<"Pi = "<< pi;
 return 0;
}
Ejercicio 6
Codifique un programa que se comporte como una calculadora simple que realice operaciones hasta que el usuario introduzca el carácter ‘&’. Para ello deberá tener las siguientes características:

  • Solo efectuará operaciones con dos operandos.
  • Operaciones permitidas: (+,-,*,/).
  • Se trabajará con operandos enteros.- Pedirá en primer lugar la operación a realizar, y a continuación los dos operandos.
  • Si el operador no se corresponde con alguno de los indicados se emitirá un mensaje de error.

#include <iostream>
using namespace std;

int main() {
 char op;
 int a, b;
 cout << "Programa que se comporta como una calculadora simple." << endl;

 do{
 cout << "Operación \t: ";
 cin >> op;

 if(op == '&'){
  cout << "FIN DEL PROGRAMA.";
 } else {
  if((op != '+') && (op!= '-') && (op != '*')&&(op != '/')){
   cout << "ERROR." << endl;
  }else{
   cout << "Operando 1 \t: ";
   cin >> a;
   cout << "Operando 2 \t: ";
   cin >> b;
   cout << "Resultado \t: ";
   switch (op){
    case '+': cout << b+a << endl;
    break;
    case '-': cout << a - b << endl;
    break;
    case '*': cout << a* b << endl;
    break;
    case '/': cout << a/b << endl;
    break;
   }
  }
 }
 }while(op != '&');
 
 return 0;
}
Ejercicio 7
Modifique el programa anterior para que en el caso de que el usuario introduzca una operación equivocada el sistema termine elevando una excepción “Operación no existente”.
#include <iostream>
using namespace std;

int main() {
 char op;
 int a, b;
 cout << "Programa que se comporta como una calculadora simple." << endl;

 do{
 cout << "Operación \t: ";
 cin >> op;

 if(op == '&'){
  cout << "FIN DEL PROGRAMA.";
 } else {
  if((op != '+') && (op!= '-') && (op != '*')&&(op != '/')){
   throw "Operación no existente";
  }else{
   cout << "Operando 1 \t: ";
     cin >> a;
     cout << "Operando 2 \t: ";
     cin >> b;
     cout << "Resultado \t: ";
     switch (op){
     case '+': cout << b+a << endl;
     break;
     case '-': cout << a - b << endl;
     break;
     case '*': cout << a* b << endl;
     break;
     case '/': cout << a/b << endl;
     break;
     }
  }
 }
 }while(op != '&');
 
 return 0;
}

Un saludo ;)

viernes, 14 de marzo de 2014

Ejercicios de C++: sentencias de selección.

A continuación unos ejercicios más de C++ para practicar las sentencias de selección. Si no sabes nada de este lenguaje es recomendable que te pases por esta entrada, aunque son bastante sencillos.

Ejercicio 1.
Escribe un programa que lea un número desde teclado y nos diga si es positivo o
negativo.
#include <iostream>
using namespace std;

int main() {
 double num;
 cout << "Introduce un número: "; // prints !!!Hello World!!!
 cin >> num;
 if(num < 0){
  cout << "El número es negativo.";
 }else if(num ==0){
  cout << "El número es 0.";
 }else {
  cout << "El número es positivo.";
 }
 return 0;
}
Ejercicio 2
Escribe un programa que lea tres números y que diga cuál de ellos es el mayor.
#include<iostream>
using namespace std;

int main() {
 float a, b, c, mayor;
 cout << "Programa que calcula el mayor de 3 números" << endl; // prints !!!Hello World!!!
 cout << "Introduce tres números: ";
 cin >> a >> b >> c;
 mayor = a;
 if(b > a){
  mayor = b;
 }
 if(c > mayor){
  mayor = c;
 }
 cout << "El número mayor introducido es " << mayor;
 return 0;
}
Ejercicio 3
Escribe un programa que lea un carácter del teclado y compruebe si el carácter es una
letra, en cuyo caso la salida debe ser “Es letra”, o si el carácter es “\”, en cuyo caso la salida
debe ser “Fin”. Si el carácter no es una letra ni “\” la salida debe ser “Error”.
#include <iostream>
using namespace std;

int main() {
 char a;
 cout << "Introduce un caracter: ";
 cin >> a;
 if(((a>='A') && (a<='Z')) || ((a>='a') && (a<='z'))){
  cout << "Es letra.";
 }else if(a == '\\'){
  cout << "Fin.";
 }else{
  cout << "Error.";
 }
 return 0;
}
Ejercicio 4
Escribe un programa que acepte fechas escritas en el formato numérico y nos dé la
misma fecha pero con el mes correspondiente indicado en letras. Utiliza la estructura de
selección switch. Ejemplo:
Dia: 15
Mes: 2
Año: 1978
Dará como salida:
Dia: 15
Mes: Febrero
Año: 1978
#include <iostream>
using namespace std;

int main() {
 int dia, mes, anyo;
 cout << "Día: " ;
 cin >> dia;
 cout << "Mes: ";
 cin >> mes;
 cout << "Año: ";
 cin >> anyo;
 cout << "Día: " << dia << endl;
 cout << "Mes: ";

 switch(mes){

 case 1: cout << "Enero" << endl;
    break;
 case 2:cout << "Febrero" << endl;
 break;
 case 3:cout << "Marzo" << endl;
 break;
 case 4:cout << "Abril" << endl;
 break;
 case 5:cout << "Mayo" << endl;
 break;
 case 6:cout << "Junio" << endl;
 break;
 case 7:cout << "Julio" << endl;
 break;
 case 8:cout << "Agosto" << endl;
 break;
 case 9:cout << "Septiembre" << endl;
 break;
 case 10:cout << "Octubre" << endl;
 break;
 case 11:cout << "Noviembre" << endl;
 break;
 case 12:cout << "Diciembre" << endl;
 break;

 }
 cout << "Año: "<< anyo << endl;
 return 0;
}
Ejercicio 5
Escribe un programa que permita emitir la factura correspondiente a una compra de un
artículo determinado del que se adquieren una o varias unidades. El IVA a aplicar es del
12%, además si el precio bruto (precio de venta + IVA) es mayor de 300€, se aplicará un
descuento del 5%. En el caso de que se aplique el descuento, deberemos indicarlo por
pantalla.
#include <iostream>
using namespace std;

const double IVA = 0.12;
const double DESCUENTO = 0.05;

int main() {
 int uni;
 double preciounitario, prec1, precio;
 cout << "Introduce las unidades adquiridas: ";
 cin >> uni;
 cout << "Introduce el precio del artículo: ";
 cin >> preciounitario;

 prec1 = uni * preciounitario;
 precio = prec1 + IVA * prec1;

 if(precio > 300){
  precio = precio - DESCUENTO* precio;
  cout << "Se ha producido un descuento del 5%. ";
 }

 cout << "El precio final es "<< precio;


 return 0;
}


Ejercicio 6
El recibo de la electricidad se elabora de la siguiente forma:

  •  1 € de gastos fijos.
  •  0.50 €/Kw para los primeros 100 Kw.
  •  0.35 €/Kw para los siguientes 150 Kw.
  •  0.25 €/Kw para el resto.

Escribe un programa que lea de teclado dos números, que representan los dos últimos
valores del contador de la luz (al restarlos obtendremos el consumo en Kw ), y calcule e
imprima en pantalla el importe total a pagar en función del consumo realizado.
#include <iostream>
using namespace std;

int main() {
 double cont1, cont2, consumo, precio;
 cout << "Factura de la luz." << endl; // prints !!!Hello World!!!
 do{
 cout << "Introduce el valor actual del contador de la luz: ";
 cin >> cont2 ;
 cout << "Introduce el valor anterior del contador de la luz: ";
 cin >> cont1;
 }while(cont2 < cont1);

 consumo = cont2 - cont1;

 if(consumo < 100){
  precio = consumo * 0.5;
 }else{
  precio = 100 * 0.5;

  if(consumo < 250){
   precio = precio + (consumo - 100)*0.35;
  }else{
   precio = precio + 0.35*150 + (consumo - 250)*0.25;


  }
 }
 precio++;
 cout << "El total a pagar es "<< precio;


 return 0;
}
Ejercicio 7
Diseñar un programa que lea el ordinal de un mes y deduzca el número de días que
tiene dicho mes (para un año no bisiesto) sabiendo que: enero, marzo, mayo, julio, agosto,
octubre y diciembre tienen 31 días, febrero 28 y el resto de los meses 30.
#include <iostream>
using namespace std;

int main() {
 int mes;
 do{
 cout << "Introduce el ordinal del mes: ";
 cin >> mes;
 }while(mes < 1 || mes > 12);

 switch(mes){
 case 1:
 case 3:
 case 5:
 case 7:
 case 8:
 case 10:
 case 12: cout << "Dicho mes tiene 31 días.";
 break;
 case 2: cout << "Dicho mes tiene 28 días.";
 break;
 default: cout << "Dicho mes tiene 30 días.";
 break;

 }
 return 0;
}
Ejercicio 8
Una empresa maneja códigos numéricos con las siguientes características:

  • Cada código consta de cuatro dígitos:
  • El primero representa a una provincia.
  • Los dos siguientes indican el número de la operación.
  • El último es un dígito de control.

Escribe un programa que lea de teclado un número de cuatro dígitos (se supone que el primer
dígito no es un cero), y posteriormente imprima en pantalla la siguiente información.
PROVINCIA &
NUMERO DE OPERACION &&
DIGITO DE CONTROL &
En caso de que el número no tenga exactamente cuatro dígitos, o bien el dígito de control sea
erróneo (será correcto si su valor coincide con el resto de dividir el número de operación entre
la provincia), en lugar del mensaje anterior, habrá que imprimir en pantalla el siguiente mensaje
de error:
ERROR: CODIGO INVALIDO.
#include <iostream>
using namespace std;

int main() {
 int cod, provincia, operacion, control;
 cout << "Introduce el código de cuatro digitos: " << endl; // prints !!!Hello World!!!
 cin >> cod;
 if(cod < 1000 || cod > 9999){
  cout << "ERROR: CODIGO INVALIDO.";
 }else{

 provincia = cod %10;
 operacion = ((cod - provincia)/10)%100;
 control = int(cod/1000);


  if(control != operacion%provincia){
   cout << "ERROR: CODIGO INVALIDO.";
  }else{
   cout << "PROVINCIA \t \t" << provincia << endl;
   cout << "NUMERO DE OPERACIÓN \t" << operacion << endl;
   cout << "DÍGITO DE CONTROL \t" << control << endl;
  }

 }


 return 0;
}

miércoles, 12 de marzo de 2014

Listas enlazadas en C.

En esta entrada vemos como implementar listas enlazadas en C. Para ello es importante conocer algo de punteros para lo que recomiendo echar un vistazo a esta entrada a aquellos que no manejen nada el tema. Además este ejercicio es muy parecido a la práctica de Gestión de Memoria, incluso más fácil. Aquí os dejo tanto el archivo .h (header) como el .c -> LinkedList.h LinkedList.c

Cada nodo de la lista está formado por :

  • Un elemento del tipo deseado, en nuestro caso unsigned.
  • Un puntero al próximo nodo, o null si es el último.
Por tanto cada nodo estará definido así.

typedef struct TNodo * TLista; // defino el tipo TLista, que es un puntero a un registro

struct TNodo {
 unsigned valor;
 TLista sig;
 };

Como vemos, hay dos elementos en el registro:
  • Un elemento llamado valor de tipo natural.
  • Un puntero al próximo nodo, pues TLista es un puntero a un nodo.
El ejercicio nos pide crear los siguientes métodos: 

void crear (TLista * lista); // Crear lista vacia

void rellenar(TLista * lista); // Incluir elementos en la lista (pedir numeros hasta que se inserte un negativo) y que los elementos se inserten por el principio

void destruir(TLista* lista); // Destruir todos los elementos de la lista

void mostrar (TLista lista); // Mostrar los elementos de la lista


Pasamos a ver cada uno de los métodos:
Crear: Simplemente hacemos que el puntero apunte a NULL.
void crear (TLista * lista){ // Crear lista vacia
 *lista = NULL;
}
Rellenar: Mientras los números que introduzcamos sean positivos, creamos un auxiliar con el valor del número introducido cuyo siguiente sea el resto de la lista y posteriormente hacemos que la lista apunte a dicho auxiliar.
void rellenar(TLista * lista){
 int insertado;
 TLista auxiliar;

 printf("Introduce los números de la lista (introducir un número negativo para finalizar): \n");
 fflush(stdout);
 scanf("%d", &insertado);
 while (insertado >= 0) {
  auxiliar = malloc(sizeof(struct TNodo));
  auxiliar->valor=insertado;
  auxiliar->sig=*lista;
  *lista=auxiliar;
  scanf("%d", &insertado);
 }
}
Destruir: Añado dos formas distintas de hacerlo. En ambas se necesita un auxiliar para recorres dicha lista e ir eliminando los nodos, liberando memoria mediante el método free.
void destruir(TLista* lista){// Destruir todos los elementos de la lista
 TLista auxiliar;
 while (*lista != NULL) {
   auxiliar=(*lista)->sig;
   free(*lista);
   *lista=auxiliar;
 }
 /*
  TLista ptr = *lista;
   while(ptr != NULL){
     *lista = ptr-> sig;
     free( ptr);
     ptr = *lista;
   }
 */
}
Mostrar:  Simplemente recorremos la lista mostrando los valores de los nodos.
void mostrar (TLista lista){// Mostrar los elementos de la lista
 TLista ptr = lista;
 printf("[");
 while (ptr != NULL) {
  printf("%u", ptr->valor);
  ptr = ptr->sig;
  if (ptr != NULL) {
   printf(", ");
  }
 }
 printf("]\n");
}
Posteriormente en otra entrada completaré el ejercicio agregando otros métodos como:
int is_empty(Linked_List l); // Devuelve verdadero si la lista está vacía 
int contains(Linked_List l, unsigned v); // Devuelve verdadero si la lista 
contiene el elemento v 
int length(Linked_List l); // Devuelve el número de elementos de la lista 
int insert(Linked_List * ptrL, int pos, unsigned v); // Inserta v en la posición 
pos de la lista (*prtL). Si ok, devuelve verdadero; si pos no está entre 1 y 
length +1, entonces devuelve falso 
int remove(Linked_List * ptrL, int pos); // Borra el elemento pos de la 
lista, devolviendo verdadero o falso según la operación se pueda realizar. 
int getElement(Linked_List l, int pos); // Devuelve el elemento de la 
posición pos. Si esa posición no existe, el comportamiento de la función no 
está definido. 
Linked_List readFromFile(char * filename); // Asumiendo que filename 
contiene N líneas, donde cada línea es un entero, lee el fichero y almacena su 
contenido una lista que es devuelta como resultado. En caso de alguna 
situación de error, la función devolverá NULL. 
int writeToFile(Linked_list list, char * filename); // Escribe el contenido 
de la lista list en el fichero denominado filaneme, almacenando cada elemento 
de la lista una línea del fichero. La función devuelve verdadero o falso según 
se haya realizado con éxito o no

Un saludo ;)

lunes, 10 de marzo de 2014

Práctica de creación de Jarras (Parte 3) GUI

Buenas, hoy vamos a implementar una GUI para la aplicación de Jarras vista anteriormente. Vamos a intentar que se parezca lo más posible a esto:



La clase que implementa el panel gráfico será la clase PanelJarras, que debe implementar la interfaz  istaJarras, ControladorJarras será la clase controladora y AplicacionJarras la clase principal de la aplicación. El programa Jarras será el de las prácticas anteriores. La interfaz VistaJarras se proporciona a continuación:
import java.awt.event.ActionListener;

public interface VistaJarras { 
 public static final String INICIAR = "INICIAR"; 
 public static final String LLENAR_A = "LLENAR_A"; 
 public static final String LLENAR_B = "LLENAR_B"; 
 public static final String VACIAR_A = "VACIAR_A"; 
 public static final String VACIAR_B = "VACIAR_B"; 
 public static final String VOLCAR_A_EN_B = "VOLCAR_A_EN_B"; 
 public static final String VOLCAR_B_EN_A = "VOLCAR_B_EN_A"; 
 public static final String FINALIZAR = "FINALIZAR"; 
 /** 
  *  * * Pasamos el controlador.
  *   */ 
 public void controlador(ActionListener ctr); 
 /** 
  * * Obtenemos la capacidad inicial de la jarra A.
  * * @return int con la capacidad inicial de la jarra A.
  *  */ 
 public int capacidadInicialA(); 
 /** 
  * * Obtenemos la capacidad inicial de la jarra B
  * * @return int con la capacidad inicial de la jarra B.
  *  */

 public int capacidadInicialB();
 /**
  *
  *
  ** Mostramos un mensaje de error.
  ** @param mensaje
  * * String con el mensaje a mostrar.
  *
  *@return 
  * */
 public void error(String mensaje);

 /**
  * * Mostramos un mensaje de información.
  * * @param mensaje
  * String con el mensaje a mostrar. 
  */ 
 public void ok(String mensaje); 
 /** 
 *  Habilitamos o deshabilitamos el modo inicialización de jarras o trasvases
 *  @param b
 *  true para habilitar el modo inicialización; false para el modo trasvases 
  */ 
 public void habilitarInicio(boolean b); 
 /** 
* Establecemos la capacidad de la jarra A.
* @param c
 * int con la capacidad de la jarra A 
 */ 
public void capacidadA(int c); 
/** 
* Establecemos la capacidad de la jarra B.
* @param c
 * int con la capacidad de la jarra B. 
 */ 
public void capacidadB(int c); 
/** 
* Establecemos el contenido de la jarra A.
* @param c
 * int con el contenido de la jarra A. 
 */ 
public void contenidoA(int c); 
/** 
* Establecemos el contenido de la jarra B.
* @param c
 * int con el contenido de la jarra B. 
 */ 
public void contenidoB(int c); 
/** 
* Añadimos un mensaje al histórico.
* @param mensaje
 * String con el mensaje a añadir. 
 */ 
public void añadirAHistórico(String mensaje); 
/** 
* Limpiamos el histórico.
 */ 
public void limpiar(); 
}

NOTA 1: Cuando a un componente se le asigna un GridLayout es posible especificar en el constructor el margen que debe quedar entre el elemento a insertar y el borde de la cuadrícula. Así, si creamos un objeto con new GridLayout(4,2,5,5) además de crear la matriz de 4x2 para insertar componentes, cada componente se mostrará con un margen de 5 puntos por cada lado. Es decir, la separación entre componentes será de 10 puntos.

NOTA 2: Mientras las jarras no hayan sido correctamente inicializadas, no tiene sentido hacer trasvases sobre ellas. Por eso la aplicación tiene dos modos. Modo de inicialización de jarras en as que sólo es posible actuar sobre el botón de iniciar capacidades, y modo trasvase en las que ya es posible los trasvases pero no la inicialización. El botón finalizar nos lleva de nuevo al modo de inicialización de jarras.

La verdad es que no necesitais programa de test más que el que se usó para la otra práctica de jarras; pues la GUI se testea "toqueteandola". Aquí teneis los resultados:

Aquí el main
import java.awt.event.ActionListener;

import javax.swing.JFrame;
import javax.swing.JPanel;


public class AplicacionJarras {
 public static void main(String[] args) {
  VistaJarras vj = new PanelJarras();
  //Jarras jarraA;
  //Jarras jarraB;
  
  ActionListener ctrJarras = new ControladorJarras(vj);
  vj.controlador(ctrJarras);
  JFrame ventana = new JFrame("Control de Jarras");
  
  ventana.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  ventana.setContentPane((JPanel)vj);
  ventana.pack();
  ventana.setVisible(true);
 }
}


Aquí el Controlador
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;


public class ControladorJarras implements ActionListener{

 private Jarras jarraA, jarraB;
 private VistaJarras vistaJarra;
 
 public ControladorJarras(VistaJarras vj) {
  vistaJarra = vj;
  vistaJarra.habilitarInicio(true);
  vistaJarra.ok("Indique las capacidades iniciales");
 }
 
 @Override
 public void actionPerformed(ActionEvent e) {
  // TODO Auto-generated method stub
  
  String comando = e.getActionCommand();
  try{
   if(comando.equals(VistaJarras.INICIAR)) {
    
    vistaJarra.habilitarInicio(false);
    jarraA = new Jarras(vistaJarra.capacidadInicialA());
    jarraB = new Jarras(vistaJarra.capacidadInicialB());
    vistaJarra.capacidadA(jarraA.capacidad());
    vistaJarra.capacidadB(jarraB.capacidad());
    vistaJarra.ok("Jarras creadas con éxito");
   } else if(comando.equals(VistaJarras.LLENAR_A)) {
    
    jarraA.llena();
    vistaJarra.contenidoA(jarraA.cantidad());
    vistaJarra.añadirAHistórico("Llenamos la jarra A");
    vistaJarra.ok("Jarra A llena al completo");
   } else if(comando.equals(VistaJarras.LLENAR_B)) {
    
    jarraB.llena();
    vistaJarra.contenidoB(jarraB.cantidad());
    vistaJarra.añadirAHistórico("Llenamos la jarra B");
    vistaJarra.ok("Jarra B llena al completo");
   } else if (comando.equals(VistaJarras.VACIAR_A)){
    jarraA.vacia();
    vistaJarra.contenidoA(jarraA.cantidad());
    vistaJarra.añadirAHistórico("Vaciamos la jarra A");
    vistaJarra.ok("Jarra A vacía");
   } else if(comando.equals(VistaJarras.VACIAR_B)) {
    jarraB.vacia();
    vistaJarra.contenidoB(jarraB.cantidad());
    vistaJarra.añadirAHistórico("Vaciamos la jarra B");
    vistaJarra.ok("Jarra B vacía");
   } else if(comando.equals(VistaJarras.VOLCAR_A_EN_B)) {
    jarraB.llenaDesde(jarraA);
    vistaJarra.contenidoB(jarraB.cantidad());
    vistaJarra.contenidoA(jarraA.cantidad());
    vistaJarra.añadirAHistórico("Volcamos el contenido de la Jarra A en la Jarra B");
    vistaJarra.ok("Jarra A vaciada en Jarra B");
   } else if(comando.equals(VistaJarras.VOLCAR_B_EN_A)) {
    jarraA.llenaDesde(jarraB);
    vistaJarra.contenidoA(jarraA.cantidad());
    vistaJarra.contenidoB(jarraB.cantidad());
    vistaJarra.añadirAHistórico("Volcamos el contenido de la Jarra B en la Jarra A");
    vistaJarra.ok("Jarra B vaciada en Jarra A");
   } else if(comando.equals(VistaJarras.FINALIZAR)) {
    vistaJarra.limpiar();
    vistaJarra.habilitarInicio(true);
    vistaJarra.ok("Indique las capacidades iniciales");
    jarraA = null;
    jarraB = null;
    
   }
  } catch (NumberFormatException e1) {
   vistaJarra.error("No ha introducido un valor numérico");
  } catch (RuntimeException e2) {
   vistaJarra.error("Se ha producido un error");
  }
 }

}


Y aquí el panel
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionListener;

import javax.swing.*;


public class PanelJarras extends JPanel implements VistaJarras{
 private static final long serialVersionUID = 1L;
 private JButton botonIniciar, botonLlenarA, botonLlenarB, botonVaciarA, botonVaciarB, botonVolcarAenB, botonVolcarBenA, botonFinalizar;
 private JLabel etiquetaJarraA, etiquetaJarraB, etiquetaInformacion, etiquetaContenidoA, etiquetaContenidoB, etiquetaCapacidadA, etiquetaCapacidadB;
 private JTextArea textoCapacidadInicialA, textoCapacidadInicialB, textoAreaHistorico, textoCapacidadA, textoCapacidadB, textoContenidoA, 
   textoContenidoB;
 private ImageIcon der, izq;
 
 public PanelJarras() {
  der = new ImageIcon("next.jpg");
  izq = new ImageIcon("previous.jpg");
  
  botonIniciar = new JButton("Iniciar");
  botonLlenarA = new JButton("Llenar");
  botonLlenarB = new JButton("Llenar");
  botonVaciarA = new JButton("Vaciar");
  botonVaciarB = new JButton("Vaciar");
  botonVolcarAenB = new JButton(der);
  botonVolcarBenA = new JButton(izq);
  botonFinalizar = new JButton("Finalizar");
  
  etiquetaJarraA = new JLabel("Jarra A: ");
  etiquetaJarraB = new JLabel("Jarra B: ");
  etiquetaInformacion = new JLabel(" ");
  etiquetaContenidoA = new JLabel("Contenido");
  etiquetaCapacidadA = new JLabel("Capacidad");
  etiquetaContenidoB = new JLabel("Contenido");
  etiquetaCapacidadB = new JLabel("Capacidad");
  
  textoCapacidadInicialA = new JTextArea();
  textoCapacidadInicialB = new JTextArea();
  textoCapacidadA = new JTextArea();
  textoCapacidadB = new JTextArea();
  textoContenidoA = new JTextArea();
  textoContenidoB = new JTextArea();
  textoAreaHistorico = new JTextArea();
  textoContenidoA.append("0");
  textoContenidoB.append("0");
  textoContenidoA.setEditable(false);
 // textoContenidoB.setEditable(false);
  textoCapacidadA.setEditable(false);
  textoCapacidadB.setEditable(false);
  
  //Paneles previos a los de inicio
  JPanel panelInicA = new JPanel();
  panelInicA.setLayout(new GridLayout(1, 2));
  panelInicA.add(etiquetaJarraA);
  panelInicA.add(textoCapacidadInicialA);
  
  JPanel panelInicB = new JPanel();
  panelInicB.setLayout(new GridLayout(1, 2));
  panelInicB.add(etiquetaJarraB);
  panelInicB.add(textoCapacidadInicialB);
  
  
  //Panel de inicio
  JPanel panelInic = new JPanel();
  panelInic.setLayout(new GridLayout(1, 3));
  panelInic.add(panelInicA);
  panelInic.add(botonIniciar);
  panelInic.add(panelInicB);
  
  
  //Paneles previos a los de las Jarra
  JPanel panelContPrevA = new JPanel();
  panelContPrevA.setLayout(new GridLayout(1, 2));
  panelContPrevA.add(etiquetaContenidoA);
  panelContPrevA.add(textoContenidoA);
    
  JPanel panelContPrevB = new JPanel();
  panelContPrevB.setLayout(new GridLayout(1, 2));
  panelContPrevB.add(etiquetaContenidoB);
  panelContPrevB.add(textoContenidoB);
  
  JPanel panelCapPrevA = new JPanel();
  panelCapPrevA.setLayout(new GridLayout(1, 2));
  panelCapPrevA.add(etiquetaCapacidadA);
  panelCapPrevA.add(textoCapacidadA);
    
  JPanel panelCapPrevB = new JPanel();
  panelCapPrevB.setLayout(new GridLayout(1, 2));
  panelCapPrevB.add(etiquetaCapacidadB);
  panelCapPrevB.add(textoCapacidadB);
    
    
  //Panel de la Jarra A
  JPanel panelJarraA = new JPanel();
  panelJarraA.setLayout(new GridLayout(4, 1, 5, 5));
  panelJarraA.add(botonLlenarA);
  panelJarraA.add(panelContPrevA);
  panelJarraA.add(panelCapPrevA);
  panelJarraA.add(botonVaciarA);
  
  //Panel de la Jarra B
  JPanel panelJarraB = new JPanel();
  panelJarraB.setLayout(new GridLayout(4, 1, 5, 5));
  panelJarraB.add(botonLlenarB);
  panelJarraB.add(panelContPrevB);
  panelJarraB.add(panelCapPrevB);
  panelJarraB.add(botonVaciarB);
  
  //Panel central
  JPanel panelCentro = new JPanel();
  panelCentro.setLayout(new GridLayout(3, 1));
  panelCentro.add(botonVolcarAenB);
  panelCentro.add(botonVolcarBenA);
  panelCentro.add(botonFinalizar);
  
  //Panel de comandos
  JPanel panelComand = new JPanel();
  panelComand.setLayout(new GridLayout(1, 3));
  panelComand.add(panelJarraA);
  panelComand.add(panelCentro);
  panelComand.add(panelJarraB);
  
  //mitad de arriba
  JPanel panelArriba = new JPanel();
  panelArriba.setLayout(new GridLayout(2, 1));
  panelArriba.add(panelInic);
  panelArriba.add(panelComand);
  
  //mitad de Abajo
  JPanel panelAbajo = new JPanel();
  panelAbajo.setLayout(new GridLayout(2, 1));
  panelAbajo.add(textoAreaHistorico);
  panelAbajo.add(etiquetaInformacion);
  
  setLayout(new GridLayout(2, 1));
  add(panelArriba, BorderLayout.NORTH);
  add(panelAbajo, BorderLayout.SOUTH);
  
 }
 
 @Override
 public void controlador(ActionListener ctr) {
  // TODO Auto-generated method stub
  botonIniciar.addActionListener(ctr);
  botonLlenarA.addActionListener(ctr);
  botonLlenarB.addActionListener(ctr);
  botonVaciarA.addActionListener(ctr);
  botonVaciarB.addActionListener(ctr);
  botonVolcarAenB.addActionListener(ctr);
  botonVolcarBenA.addActionListener(ctr);
  botonFinalizar.addActionListener(ctr);
  
  botonIniciar.setActionCommand(VistaJarras.INICIAR);
  botonLlenarA.setActionCommand(VistaJarras.LLENAR_A);
  botonLlenarB.setActionCommand(VistaJarras.LLENAR_B);
  botonVaciarA.setActionCommand(VistaJarras.VACIAR_A);
  botonVaciarB.setActionCommand(VistaJarras.VACIAR_B);
  botonVolcarAenB.setActionCommand(VistaJarras.VOLCAR_A_EN_B);
  botonVolcarBenA.setActionCommand(VistaJarras.VOLCAR_B_EN_A);
  botonFinalizar.setActionCommand(VistaJarras.FINALIZAR);
  
 
  // ....................................................................................................
  
 }

 @Override
 public int capacidadInicialA() {
  return Integer.parseInt(textoCapacidadInicialA.getText());
 }

 @Override
 public int capacidadInicialB() {
  return Integer.parseInt(textoCapacidadInicialB.getText());
 }

 @Override
 public void error(String mensaje) {
  // TODO Auto-generated method stub
  etiquetaInformacion.setForeground(Color.RED);
  etiquetaInformacion.setText(mensaje);
  
 }

 @Override
 public void ok(String mensaje) {
  // TODO Auto-generated method stub
  etiquetaInformacion.setForeground(Color.BLUE);
  etiquetaInformacion.setText(mensaje);
  
 }

 @Override
 public void habilitarInicio(boolean b) {
  // TODO Auto-generated method stub
  etiquetaJarraA.setEnabled(b);
  etiquetaJarraB.setEnabled(b);
  botonIniciar.setEnabled(b);
  textoCapacidadInicialA.setEditable(b);
  textoCapacidadInicialB.setEditable(b);
  
  botonLlenarA.setEnabled(!b);
  botonLlenarB.setEnabled(!b);
  botonVaciarA.setEnabled(!b);
  botonVaciarB.setEnabled(!b);
  botonFinalizar.setEnabled(!b);
  botonVolcarAenB.setEnabled(!b);
  botonVolcarBenA.setEnabled(!b);
  etiquetaContenidoA.setEnabled(!b);
  etiquetaContenidoB.setEnabled(!b);
  etiquetaCapacidadA.setEnabled(!b);
  etiquetaCapacidadB.setEnabled(!b);
   
  
 }

 @Override
 public void capacidadA(int c) {
  // TODO Auto-generated method stub

  textoCapacidadA.setText(" " +c);
 }

 @Override
 public void capacidadB(int c) {
  // TODO Auto-generated method stub

  textoCapacidadB.setText(" " +c);
 }

 @Override
 public void contenidoA(int c) {
  // TODO Auto-generated method stub
  textoContenidoA.setText(" " +c);
  
 }

 @Override
 public void contenidoB(int c) {
  // TODO Auto-generated method stub
  textoContenidoB.setText(" " +c);
  
 }

 @Override
 public void añadirAHistórico(String mensaje) {
  // TODO Auto-generated method stub
  textoAreaHistorico.append(mensaje + "\n");
  
 }

 @Override
 public void limpiar() {
  // TODO Auto-generated method stub
  textoAreaHistorico.setText(" ");
  textoCapacidadA.setText(" ");
  textoCapacidadB.setText(" ");
  textoContenidoA.setText(" ");
  textoContenidoB.setText(" ");
  
 }

}


Espero que os esté pareciendo, cuanto menos, interesante este blog. A ver si puedo subir continuamente esta semanilla, pues me voy a un congreso en Gijón y voy a estar liadillo :p

Saludos y a seguir aprendiendo ;)

domingo, 9 de marzo de 2014

Ejercicios de Haskell (II)

Os dejo unos ejercicios más completos de Haskell.

Un método para calcular el máximo común divisor (mcd) de dos números naturales (no nulos simultáneamente) consiste en obtener las descomposiciones en factores primos de ambos y multiplicar los factores comunes de la forma con menor exponente. Por ejemplo: factPrimos 48 -> [2,2,2,2,3] por lo que el mcd de 48 y 60 es 2*2*3.
a) Escribe una función recursiva mezc' que tome las listas de factores primos ordenadas ascendentemente y devuelva una lista ordenada con los factores comunes repetidos según la menor potencia. Por ejemplo:
mezc' [2,2,2,2,3] [2,2,3,5] -> [2,2,3]
b) Usando la función mezc', define una función mcd' que calcule el mcd de dos naturales no nulos simultáneamente.
c) Recuerda que gcd es la función predefinida para calcular el mcd. Comprueba tu función con QuickCheck mediante la siguiente propiedad:
p_mcd' x y = x>0 && y>0 ==> mcd' x y == gcd x y
Modifica la precondición para comprobar la corrección con naturales no nulos simultáneamente.
mezc' :: [Integer] -> [Integer] -> [Integer]
mezc' [] _ = []
mezc' _ [] =  []
mezc' (x:xs)(y:ys)
  | x < y       = mezc' xs (y:ys)
  | x > y       = mezc'(x:xs) ys
  | x == y      = [x] ++ mezc' xs ys

mcd' :: Integer -> Integer-> Integer
mcd' 0 0 = 0
mcd' x y = product(mezc' (factPrimos x) (factPrimos y))

p_mcd' x y = x>0 && y>0 ==> mcd' x y == gcd x y

En Haskell una cadena de caracteres (String) es una lista de tipo [Char]. En este ejercicio vamos a estudiar cómo el procesamiento de cadenas de caracteres resulta muy útil en Biología.
Un problema común en la Biología moderna es entender la estructura de las moléculas de ADN y el papel de las estructuras específicas para determinar la función de una molécula. Una secuencia de ADN es comúnmente representada como una secuencia de los cuatro nucleótidos - adenina (A), citosina (C), guanina (G) y timina (T) - por lo que una molécula puede ser representada con una cadena de caracteres con los correspondientes nucleótidos, como "AAACAACTTCGTAAGTATA".
Una forma de entender la función de una cadena de ADN es ver si contiene subcadenas que coincidan con una colección de secuencias de ADN ya conocidas - es decir, secuencias cuya función y estructura ya se conoce - con la idea de que estructuras similares tienden a implicar funciones similares. Organismos simples como las bacterias pueden tener millones de nucleótidos en sus secuencias del ADN y los cromosomas humanos se cree que constan del orden de 246 millones de bases, por lo que es necesario desarrollar programas de ordenador muy eficientes para procesar estas cadenas.
a) Define una función recursiva esPrefijoDe que tome dos listas como argumentos, y compruebe si la primera es un prefijo de la segunda, es decir, si la segunda comienza exactamente por la primera. Por ejemplo:
"ATG" `esPrefijoDe` "ATGACATGCACAAGTATGCAT" -> True
"ATC" `esPrefijoDe` "ATGACATGCACAAGTATGCAT" -> False
Nota: la función isPrefixOf de la biblioteca List realiza esto mismo.
b) Define una función recursiva búsquedas :: String -> String -> [Int] que tome como parámetros dos listas. La primera será la cadena a buscar y la segunda la cadena donde buscar. La función debe devolver una lista de enteros con todas las posiciones donde aparezca la cadena buscada. Por convenio, consideraremos que las posiciones de las letras en una cadena empiezan a numerarse por cero. Por ejemplo:
búsquedas "ATG" "ATGACATGCACAAGTATGCAT" -> [0,5,15]
ya que la cadena "ATG" aparece justo al principio (posición 0), aparece a continuación 5 posiciones después y por último aparece en la posición 15 (las apariciones de la cadena buscada se indican en subrayado).
Ayuda: usa la función esPrefijoDe para definir la función búsquedas.
c) Define una función distancia que dadas dos cadenas compare los caracteres en las mismas posiciones de las dos cadenas y devuelva cuantos son diferentes. Por ejemplo:
distancia "ATGAG" "ACGAA" -> 2
ya que la caracteres en las posiciones 1 (T y C) y 4 (G y A) difieren en ambas cadenas. Si una cadena es más corta que otra, considera que todos los caracteres extra de la más larga son diferencias. Por ejemplo:
distancia "ATG" "ACGAA" -> 3
ya que ambas cadenas difieren en los caracteres en las posiciones 1, 3 y 4.
esPrefijoDe :: String -> String -> Bool
esPrefijoDe [] _ = True
esPrefijoDe _ [] = False
esPrefijoDe (x:xs) (y:ys)
  | length (x:xs) > length (y:ys) = False
  | x == y      = esPrefijoDe xs ys
  | otherwise   = False

búsquedas :: String -> String -> [Int]
búsquedas xs ys = busquedas xs ys 0 

busquedas :: String -> String -> Int -> [Int]
busquedas xs [] n = []
busquedas xs (y:ys) n
  |esPrefijoDe xs (y:ys) = [n] ++ busquedas xs ys (n+1)
  | otherwise = busquedas xs ys (n+1)

distancia :: String -> String -> Int
distancia [] (y:ys) = length (y:ys)
distancia (x:xs) [] = length (x:xs)
distancia [] [] = 0
distancia (x:xs) (y:ys) 
  | x /= y      = 1 + distancia xs ys
  | otherwise = distancia xs ys

Sea f una función de reales en reales continua en el intervalo cerrado [a,b], que además toma valores con signos opuestos en los extremos de dicho intervalo (es decir, los signos de f(a) y f(b) son distintos). Bajo dichas suposiciones es seguro que f tiene una raíz en dicho intervalo. Este resultado se conoce como el teorema de Bolzano.
El método de bipartición usa la técnica divide y vencerás para encontrar una aproximación a dicha raíz. Los parámetros del método son f, a, b y epsilon. Para ello:

  • Sea c el punto medio del intervalo determinado por a y b.
  • Si la amplitud del intervalo [a,b] es menor o igual que epsilon, se devuelve el punto c como aproximación de la raíz.
  • Si f(c)≅ 0, entonces se devuelve el punto c como aproximación de la raíz.
  • Si hay un cambio de signo en los extremos del intervalo [a,c], se repite todo el proceso con dicho intervalo.
  • En otro caso, el cambio de signo estará en el intervalo [c,b], por lo que se repite el proceso con este intervalo.

Define una función bipartición que tome como parámetros f, a, b y epsilon y devuelva una aproximación a una raíz de f en el intervalo [a,b] calculada con el método de bipartición.
bipartición :: (Double -> Double)-> Double -> Double -> Double -> Double
bipartición f a b ep 
  | b - a <= ep = c
  | f (c) ~= 0 = c -- necesario usar el operador ~=,definido entradas anteriores 
  | (f a) * (f c) < 0 = bipartición f a c ep
  | otherwise = bipartición f c b ep
    where 
      c = (a+b)/2