domingo, 18 de marzo de 2012

Canvas de HTML5 y KineticJS

Hace unas semanas empecé a leer cosas sobre HTML5 y JavaScript (JS) para algo que tenía que desarrollar. Comencé a buscar documentación por la red sobre HTML5, información sobre la etiqueta <canvas> y más documentación sobre JavaScript.

Al empezar no tenía ni idea de por donde "meterle mano" al asunto, así que empecé por leer sobre la sintaxis de JS. Encontré esta página W3Schools, en dicha página se explicaba todo muy detalladamente y con ejemplos que además puedes modificar tú mismo y ver que cambios produce. Me vino muy bien, pero solo para adquirir los conocimientos básicos sobre este lenguaje interpretado. A continuación empecé a buscar información sobre como manipular el canvas que ofrece la última versión del lenguaje básico de la web, HTML5 (en el enlace podéis encontrar algo de información interesante) y me encontré con una API bastante simple y reducida (aquí podéis encontrarla), así que seguí buscando un poco más por la web en busca de algun entorno de desarrollo gráfico que automatizase un poco más el desarrollo del código.

Finalmente encontré una librería JS bastante interesante. KineticJS se hace llamar y proporciona un manejo del canvas mucho más fluido, dinámico y comprensible a posibles futuros lectores del código. Esta librería es gratuita y se puede descargar desde la propia página principal. Actualmente su creador, Eric Rowell, sigue actualizando de manera regular la librería añadiendo nueva funcionalidad y eliminando los bugs reportados.

Voy a dar una pequeña pincelada sobre esta librería que la verdad a mi me ha venido genial, por si a otras personas en el futuro les pudiese venir bien la información. En la página hay varios tutoriales donde se explica bastante bien las funcionalidades básicas de la librería.

Para comenzar a usarla debemos descargarnosla para añadirla como source en nuestro fichero .html o podríamos usar la URL. Yo recomiendo descargarla. Una vez descargada debemos añadir a nuestro fichero html una etiqueta <div id="nuestroID"> y colocarlo donde queramos que aparezca el canvas. Cabe destacar que nuestroID es importante ya que posteriormente en el código JS necesitaremos dicho ID para añadir el canvas en su interior. Una vez hecho esto pasamos a lo que viene siendo la programación en JS.

Lo más interesante de KineticJS es la abstracción que proporciona de escenario, capas, grupos y formas/elementos. Esto es muy práctico ya que podemos distribuir funcionalidades y manejar las capas de formas diferente.

Para crear el canvas debemos hacer lo siguiente:

var ancho = 1042;
var alto = 576;
var stage = new Kinetic.Stage("nuestroID", ancho, alto);

De esta forma tenemos declarado un nuevo escenario en el que podemos añadir capas, grupos y/o elementos. Como podemos observar la definición es bien sencilla. El primer parámetro es la cadena que representa el ID que tenía la etiqueta DIV mencionada anteriormente, ancho y alto... creo que no es necesario decir que son.

A continuación podríamos declarar una capa en la que introduciremos varios elementos. Para declarar una capa simplemente debemos hacer:


var layer = new Kinetic.Layer();

Cabe destacar que podemos pasarle como parámetro a esta función una configuración. Los elementos más destacables de la configuración serían si queremos o no mostrar la capa (visible: true, por ejemplo) o si queremos que la capa sea draggable (draggable: true). Para ver más sobre la configuración en la página web HTML5canvastutorials podemos encontrar información. La principal diferencia entre grupos y capas es que los primeros pueden contener grupos y formas (circulos, imagenes, etc) y las capas pueden contener grupos o cosas además de que cada capa está asociada a su propia etiqueta <canvas>. Yo, siguiendo los tutoriales que he encontrado en la página mencionada, he usado los grupos cuando quiero agrupar una serie de elementos con propiedades comunes (por ejemplo para poder hacer drag con muchos elementos de manera uniforme).

Por último podríamos declarar una forma/elemento. Estas formas pueden ser Circulos, Imagenes, Poligonos,  Rectangulos, Poligonos regulares, Estrellas o Texto. Todos los elementos mencionados están documentados en la API oficial y además en la página se pueden encontrar tutoriales de uso además de los distintos elementos de la configuración. Vamos a suponer que queremos añadir un circulo. Simplemente deberíamos hacer lo siguiente:


var circulo = new Kinetic.Circle({
 x: stage.width / 2,
 y: stage.height / 2,
 radius: 40,
 fill: "#AABBCC"
 stroke: "black",
 strokewidth: 4
});


//Podriamos declarar eventos, tanto de escritorio como de movil asi:

circulo.on("mouseover touchstart", function(){
 alert("Sorpresa!!");
});

//Anyadimos el circulo a la capa
layer.add(circulo);

//Anyadimo la capa al escenario
stage.add(layer);

Quizás al principio sea algo lioso, pero encuanto le dedicas un tiempo se convierte en algo muy intuitivo y muy muy sencillo de manejar. En el ejemplo podemos ver la facilidad con la que se gestionan los eventos sobre el circulo. Los eventos disponibles, tanto para PC de escritorio como para móvil, son los siguientes:

Escritorio: mouseovermouseoutmousemovemousedownmouseupclick,dblclickdragstart, and dragend
Móvil:  touchstarttouchmovetouchenddbltapdragstartdragmove, and dragend


Por último en el ejemplo vemos como se añaden los elementos a la capa y por último como se añade la capa al escenario.


Además podemos crear aplicaciones interactivas, haciendo uso de los métodos para eliminar, mostrar, dibujar y ocultar objetos.


Algo que me pareció curioso es que si le ponemos nombre a los elementos, este nombre debe ser único, ya que si no, al eliminar un elemento de una capa, cuando intentásemos eliminar el siguiente con el mismo nombre nos daría un error.


También me gustaría destacar que normalmente se suele comenzar a manipular la página con las sentencias JS cuando se ha cargado completamente, para ello simplemente debemos hacer lo siguiente:

window.onload = function(){
 //Creacion de escenario, capas, elementos, etc...
};

Por el momento aquí dejo la pequeña introducción a esta gran librería, en caso de estimarlo oportuno, añadiré más entradas en el futuro. Un saludo y espero que os animéis a usar esta librería porque está genial.

miércoles, 15 de febrero de 2012

Algoritmos Genéticos, mochila 0/1

Hace muchísimo tiempo que no escribo una entrada. Prácticas, exámenes, y tener un poco de vida han hecho que no pueda escribir nada por aquí últimamente. Tal y como mencioné en mi ultima entrada, por aquél entonces estábamos haciendo trabajos con algoritmos genéticos y programando un Shell. Desde aquella fecha ha pasado ya bastante tiempo, y, como es lógico, ya terminé aquellos trabajos y empecé otros nuevos. Pero lo prometido es deuda, así que primero voy a tratar el tema de los algoritmos genéticos que es algo más corto y en mi próxima entrada comentare y daré algunas pinceladas sobre el Shell que programamos mi compañero y yo.

Cualquier persona que no tenga idea de informática y aquellos que la tengan pero no hayan tenido la oportunidad de conocer esta técnica se preguntarán, ¿Qué es un algoritmo genético?. Según Wikipedia "Un algoritmo es una serie de pasos organizados que describe el proceso que se debe seguir, para dar solución a un problema específico. Son llamados así porque se inspiran en la evolución biológica y su base genético-molecular. Estos algoritmos hacen evolucionar una población de individuos sometiéndola a acciones aleatorias semejantes a las que actúan en la evolución biológica (mutaciones y recombinaciones genéticas), así como también a una Selección de acuerdo con algún criterio, en función del cual se decide cuáles son los individuos más adaptados, que sobreviven, y cuáles los menos aptos, que son descartados". Según yo diría que son unos algoritmos que buscan una solución a un problema de optimización en un espacio de soluciones más o menos aleatorio, consiguiendo en algunos casos la solución óptima en un tiempo de ejecución bastante reducido.

En comparación con otras técnicas como Backtracking el tiempo en encontrar soluciones ambos algoritmos puede llegar a tener una grandísima diferencia.

La primera toma de contacto que tuvimos con estos algoritmos fue para la reslocuión del problema de las n reinas, y tal y como observamos el tiempo que empleaba el algoritmo para unos tamaños de problema considerables era bastante aceptable.

Estos algoritmos tienen las siguientes características:

Los algoritmos genéticos se representan mediante una cadena sobre un alfabeto finito (una posible codificación sería una cadena de 0s y 1s). Dicha codificación define el tamaño del espacio de búsqueda y el tipo de operadores de combinación necesarios.
Cada elemento de un estado (también llamado individuo o cromosoma) es denominado gen.
Se utilizan unos operadores denominados "Selección, Cruce y Mutación" que explicaremos más adelante.
Estos algoritmos hacen uso de funciones heurísticas, y el valor heurístico de un estado se denomina fitness. La función de idoneidad o fitness mide la calidad de los estados.
Se basan en la afirmación de que combinando buenos estados se obtienen estados mejores.

Ahora pasamos a explicar de manera breve los distintos operadores:

Selección: Escoge qué individuos tendrán la oportunidad de reproducirse. Habrán individuos que aparezcan más de una vez y habrán individuos que no aparezcan. Principalmente hay dos técnicas para realizar esta función: Ruleta y Torneo. La primera técnica elige individuos con probabilidad proporcional a su función de idoneidad. La segunda establece k torneos aleatorios entre parejas de individuos y selecciona aquellos que ganen cada torneo.

Cruce: Los individuos seleccionados son recombinados para producir la descendencia que formará la siguiente generación. La técnica más común y básica es el operador de cruce por un punto.

Mutación: El operador de mutación cambia aleatoriamente el valor de algunos genes.

La función de idoneidad asigna un medida de calidad a cada estado (individuo). Cuando mejor sea dicho estado mayor será el valor de la función idoneidad.

De esta forma queda definido más o menos qué es un algoritmo genético.

El problema que nos tocó resolver era el problema de la mochila 0/1 (el cual ya hicimos también con otras técnicas de programación el curso anterior). Está resuelto en C++ haciendo uso de la librería Galib (no hemos tenido que hacer ningún tipo de implementación de individuos y demás). La única función que hemos tenido que realizar ha sido modificar los operadores de inicialización, cruce, mutación y la función objetivo (la que calcula el fitness de un individuo).

El problema que debíamos resolver es el siguiente:

Una empresa de publicidad necesita sacar al mercado un cierto producto. Para ello, debe elegir los avisos (anuncios publicitarios) en los medios de comunicación con el objetivo de maximizar el número de personas (audiencia) a los que alcanzan. La empresa cuenta de un presupuesto para ello y existe una cota superior para el número de avisos de cada tipo. Además, de acuerdo a ciertos estudios, se conoce la audiencia de cada medio de comunicación. El costo por aviso en cada medio de comunicación también es fijo y conocido.

Este problema puede modelarse mediante el modelo knapsack 0/1:

Tenemos n objetos. El problema knapsack 0/1 consiste en llenar una mochila con objetos. Cada objeto i tiene un peso determinado pi siempre positivo y una utilidad o valor asociado, también positivo, bi. Se ha de considerar además que la mochila tiene una capacidad máxima limitada C, por tanto, se han de escoger aquellos objetos xi para llenar la mochila que maximicen la utilidad sin exceder su capacidad”.

Diseñar e implementar en GALib, este problema utilizando un algoritmo genético simple.
Tener en cuenta los siguientes elementos o cuestiones:

Como los individuos que se obtienen en la inicialización de la población y al aplicar los operadores genéticos pueden ser individuos inválidos (individuos que no verifican las restricciones, es decir, sobrepasan la capacidad de la mochila), necesitamos modificar la inicialización y operadores genéticos para que comprueben la validez de los individuos generados. Para ello, vamos a partir de la inicialización, operador de cruce y mutación disponibles en GALib para string binarios.

Las modificaciones deben realizarse en el siguiente sentido:

- Inicialización: Ir creando el individuo gen a gen. Ir introduciendo elementos en los genes de forma aleatoria siempre que sea un individuo válido.

- Operador de cruce: supongamos que tenemos los cromosomas padre P11+P12 y P21+P22, cortados por el punto de corte x.

El operador de cruce empieza a crear los individuos hijos como sigue:

Tomar P11+P’, donde P’ es el trozo de cromosoma e inicialmente está a cero (no incluye ningún objeto). Ir recorriendo P22 gen a gen. Si al introducir el valor de un gen en P’ produce un P11+P’ válido, se introduce copiando sus valor en P’. En caso contrario, pasar al siguiente gen.

Tomar P21+P’, donde P’ es el trozo de cromosoma e inicialmente está a cero (no incluye ningún objeto). Ir recorriendo P12 gen a gen. Si al introducir el valor de un gen en P’ produce un P21+P’ válido, se introduce. En caso contrario, pasar al siguiente gen.

- Operador de mutación: si al mutar un gen provoca un individuo inválido, está mutación no se realiza. Se sigue con el siguiente gen candidato a ser mutado. Una vez diseñado el algoritmo genético, probarlo para el caso siguiente:

a) Si suponemos que disponemos de 5.000 euros para la publicidad, que como máximo podríamos poner 20 anuncios en paneles estáticos, 20 anuncios en paneles móviles, 40 anuncios en prensa, 50 anuncios en radio y 50 en televisión. Además, cada anuncio tiene un costo de 15, 15, 25, 45 y 90 euros, y una audiencia de 35, 40, 90, 135 y 220, respectivamente. ¿Cuántos anuncios podríamos poner en total en cada medio?

b) Cambiar los datos del problema para hacerlo más complejo. Además, del caso anterior, definir cuatro casos distintos del problema con complejidad distinta. Comparar los resultados en tiempo y solución del algoritmo genético en los 5 casos distintos.


A continuación expondré la solución propuesta para la implementación:

Para la implementación del algoritmo genético que resolviese el problema de la mochila 0/1 hemos optado por hacer uso de la clase GA1DArrayAlleleGenome, la cual, a su vez, hace uso de un array de conjuntos de alelos: GAAlleleSetArray.

El primer paso importante en la realización del algoritmo es la inicialización del array de conjuntos de alelos. Este array almacenará conjuntos de valores que representarán el número de cada tipo de anuncios que disponemos. Más adelante veremos que esto nos sirve para establecer el número de anuncios de un tipo concreto que hemos metido en la mochila. Por tanto, dichos valores estarán en el rango [0 – anunciosTipoi].

La inicialización de este array se hace de la siguiente manera:
1) Declaramos el array de conjuntos de alelos.
2) Creamos un conjunto de alelos y añadimos los valores deseados con la función add.
3) Añadimos el conjunto creado al array de alelos mediante la función add.

Cabe destacar que ,debido a que estas dos clases están parametrizadas, se han declarado para que almacene tipo de dato entero, ya que no tiene sentido tener valores reales de la cantidad de anuncios.

Una vez realizada la inicialización del array de conjuntos de alelos que usaremos para resolver este problema, pasamos ahora a la declaración de la clase que usaremos para crear posteriormente el algoritmo genético simple.

Dicha clase es la mencionada anteriormente, GA1DArrayAlleleGenome. Para la creación de esta clase usamos las ya definidas anteriormente que nos representan los conjuntos de alelos que emplearemos en el problema. Esta clase además nos sirve para especificar los operadores de inicialización, cruce y mutación que emplearemos para nuestra población de genomas. Dichas operaciones se detallan a continuación:

•Operador de Inicialización: El objetivo de esta función es el de inicializar la población con valores válidos. Valores válidos son aquellos que no superen la capacidad máxima de la mochila y, además, cada valor individual no supere el número de anuncios máximo del tipo que representa.
El hecho que motiva que tengamos que definir nosotros mismos este operador, es que debemos controlar que el incializado de los individuos de nuestra población sea correcto ya que, si lo hiciésemos con valores aleatorios, podríamos tener individuos inválidos.

Para realizar este control realizamos los siguientes pasos:

1) Recorremos el genoma.
2) Para cada gen del genoma generamos un valor aleatorio dentro de los limites especificados por el conjunto al que pertenece.
3) Comprobamos si al insertar dicho valor superamos la capacidad máxima de la mochila. En caso afirmativo vamos reduciendo dicho valor hasta que consigamos meterlo o el valor sea 0. En caso negativo lo metemos sin más.

De esta forma conseguiríamos tener toda la población inicializada con valores 3aleatorios y afirmar que son válidos.

•Operador de Cruce: El operador de cruce nos sirve para generar individuos nuevos a partir de dos individuos que tomaremos como progenitores. De nuevo debemos definirnos nuestro propio operador para asegurar que se cumplan las restricciones impuestas por el problema.

El operador de cruce que hemos implementado es el de cruce por un punto. Dicho punto de cruce es establecido por un valor aleatorio entre 0 y la longitud del genoma.

Una vez establecido este punto recorremos todos los genes del genoma hasta dicho punto y copiamos los genes de la madre en el primer hijo y los del padre en el segundo, y posteriormente se copian el resto de genes del padre para el primero y el resto de la madre para el segundo.

Al realizar la copia, de la primera parte de cualquiera de los dos progenitores no debemos realizar ninguna comprobación, ya que si partimos de individuos válidos, una parte de ellos también es válida. Por tanto las comprobaciones debemos hacerlas al copiar la segunda parte. La comprobación a realizar es ver si al copiar el gen en el hijo excedemos la capacidad de la mochila, en caso afirmativo optamos por establecer dicho gen a 0. Una alternativa podría ser ir reduciendo el valor del gen del progenitor e insertarlo.

•Operador de Mutación: Este operador ha sido modificado para que nos cambie el valor de un gen por un valor aleatorio. La única consideración es comprobar si al realizar la mutación el individuo generado es válido. Si es válido seguimos con los siguientes genes, si por el contrario generamos uno inválido entonces establecemos su valor como 0. Una posible alternativa es ir reduciendo el valor del número aleatorio generado hasta que sea posible asignárselo al gen.

•Función objetivo: La función objetivo será usada por el algoritmo genético para conocer el valor de fitness de un individuo. Este valor es calculado de la siguiente manera:

1) Recorremos los genes del individuos
2) Por cada gen multiplicamos su valor por el beneficio
3) Vamos llevando la suma y la devolvemos.

Los valores asignados para el tamaño de la población y el número de generaciones ha sido 100 y 1000 respectivamente. Además la probabilidad de mutación y de cruce son de 0.1 y 0.9 respectivamente.

Cabe destacar que hemos optado por leer los datos del problema por teclado. El realizarlo de esta manera nos beneficia en cuanto a que podemos insertar los valores del problema que queramos ejecutar a mano, o mediante un fichero redireccionando la entrada desde consola.

 /*Declaracion y creacion del array de conjuntos de alelos */
    GAAlleleSetArray arrayAlelos;
    cin>>maximo_dinero>>num_publicidad;
    datos.capacidadMaxima = maximo_dinero;
    for(int i=0;i>aux;
            datos.pesos.push_back(aux);
            cin>>aux;
            datos.beneficio.push_back(aux);
            cin>>aux;
            GAAlleleSet alelos;
            for(int j = 0;j<=aux;j++)
                    alelos.add(j);
            arrayAlelos.add(alelos);
    }

// Creamos el GA. Primero creamos el genoma y creamos una poblacion de genomas
    GA1DArrayAlleleGenome genome(arrayAlelos, Objective, &datos);

// Inicializamos -minimizar la funcion objetivo, tamaño de la poblacion, no.
    // generaciones, prob. mutacion, prob. Cruce, y le indicamos que evolucione.
    genome.initializer(inicializar); //Establecemos funcion operadores de inicializacion, cruce y mutacion
    genome.mutator(mutador); 
    genome.crossover(cruzador);
    GASimpleGA ga(genome);
    ga.minimaxi(1);
    ga.populationSize(popsize);
    ga.nGenerations(ngen);
    ga.pMutation(pmut);
    ga.pCrossover(pcross);

    ga.solve(); //Resuelve problema

 // Ahora imprimimos el mejor individuo que encuentra el GA y su valor fitness
    GA1DArrayAlleleGenome & g = (GA1DArrayAlleleGenome &)ga.statistics().bestIndividual();
    int pesoTotal = capacidadMochila(g, g.length());
    cout << "El GA encuentra:\n\t" << ga.statistics().bestIndividual() << "\n\n";
    cout << "Mejor valor fitness es " << ga.statistics().maxEver() << "\n";
    cout << "El peso alcanzado es " << pesoTotal << endl;
La funciones de inicializar, mutador, cruzador las dejo para que aquél que le interese las realice.

Me ha quedado una publicación bastante extensa, pero quién sabe, a lo mejor a alguien le sirve.

Espero haberme explicado con claridad y haber creado algo de interés a aquella persona que lea esta entrada.

Saludos y ¡hasta la próxima!

miércoles, 12 de octubre de 2011

Shell y AG's

Estos días estamos programando un Shell en una asignatura de la Universidad que de momento parece que está marchando bien. El lenguaje no es el habitual de las publicaciones que he ido haciendo, si no que tenemos que hacerlo en C (a base de punteros señales interrupciones POSIX...). Para cuando esté terminado colgaré alguna parte del código que vea que es bastante más interesante que el resto y suponiendo que la práctica vaya bien. Por otro lado estamos también programando algoritmos genéticos en C++, para dentro de muy poco tenemos que entregar la primera práctica, quizás también cuelgue algo por aquí que sea instructivo, porque la verdad que es un tema bastante interesante. ¡¡Próxima publicación dentro de poco!!

viernes, 16 de septiembre de 2011

Archivos en Java y revertir un número

Últimamente he tocado poco Eclipse ya que he tenido un examen y me ha tenido bastante ocupado. Aun así, el foro en el que suelo comentar lo he seguido visitando y, a pesar de que no es algo extremadamente interesante, me vale para hacer una muy breve entrada y dejar algo por aquí que sea nuevo.

Por un lado alguien preguntó sobre comprobar si una cadena de texto era sufijo de otra, pero de forma recursiva. La verdad que es algo muy simple de comprobar ya que apenas tiene dificultad. El código es el siguiente:

public boolean sufijo(String c1, String c2){
	if(c1.length() == 0)
		return true;
	int aux = c1.length();
	if(c1.charAt(c1.length()-1) == c2.charAt(c2.length()-1))
		return sufijo(c1.substring(0, c1.length()-1), c2.substring(0, c2.length()-1));
	else
		return false;
}


Con este método, que devuelve true o false, conseguimos lo que queríamos realizar. La función lo que hace es comprobar que la cadena c1 es sufijo de c2. El caso base es bastante claro, sí la longitud de c1 es 0 es un sufijo (ya que sería una cadena vacía y teóricamente una cadena vacía es sufijo de cualquier cadena. Sí el tamaño no es 0 comprobamos que el último carácter de cada canea sea igual, sí es así, entonces hacemos return con lo que nos devuelva la función eliminando el último carácter puesto que ya hemos comprobado que son iguales. En caso de que no sean iguales es porque no es un sufijo y por tanto devolvemos false. Las llamadas recursivas terminarán en algún momento, puesto que en cada nueva llamada al método eliminamos un carácter a cada cadena.

Otra persona del foro colgó un trozo de código para hacer uso de archivos, más concretamente crear un archivo vacío con formato .txt e ingresar datos en su interior. Tras leer el código que propuso él le comenté que quizás sería mejor el código con el que respondí:

Crear un archivo vacío
File archivo2 = new File("rutaYNombreArchivo" + ".txt");
	    try {
			if(archivo2.createNewFile()){
                                 System.out.println("Archivo creado!");
                        }
		} catch (IOException e) {
			System.out.println("Imposible crear archivo");
		}


De esta forma tendríamos un archivo vacío creado en la ruta especificada en el constructor del objeto File. Creo que es mejor dejar la ruta de creación del objeto parametrizada, es decir, pasarle un parámetro con la ruta completa hasta el archivo a crear o leer, ya que si por ejemplo estableciesemos a la fuerza que la ruta inicial comenzase en D:\\ podríamos tener errores sí una persona que ejecutase nuestro código no tuviese dicho directorio.

El código no podría ser más sencillo, simplemente declaramos un objeto de tipo File pasandole como parámetro la ruta completa, posteriormente llamamos al método de la clase File createNewFile() el cual nos crea un archivo nuevo con el nombre especificado en la ruta. Es decir, si nuestra ruta fuese C:\\Users\\Usuario\\Documents\\Carpeta\\archivoNuevo.txt crearía en la carpeta "Carpeta" un archivo con el nombre "archivoNuevo.txt".

He de decir que probé intentar crear un archivo vacío en el directorio raíz C:\\ y me lanzó una excepción, supongo que al usar Windows 7 necesita privilegios de Administrador.

Por otro lado, a la hora de ingresar datos en el archivo creado sería tan sencillo como esto:
String linea;
FileReader lector = new FileReader(archivo);
BufferedReader br = new BufferedReader(lector);
FileWriter escritor = new FileWriter(archivo);
BufferedWriter ayudaEscritor= new BufferedWriter(escritor);

while((linea = br.readLine()) != null){
       ayudaEscritor.write(linea);
       ayudaEscritor.newLine();
}

for(int a=0;a<=datos.length-1;a++){
       ayudaEscritor.write(datos[a]); //datos es una variable de tipo String[].
       ayudaEscritor.newLine();
}
ayudaEscritor.close();


Este es un código ejemplo, ya que sería mucho más sencillo escribir los datos si por ejemplo fuese un StringBuffer o un String directamente y no necesitaríamos un bucle for.

Por último un método que sirve para eliminar un fichero (suponiendo que tengamos permisos del sistema suficientes para hacerlo), crearlo de nuevo e introducir en el datos.
public boolean guardar(String nombreTabla, String rutaArchivo){
       File archivo= new File (rutaArchivo);
	       try{
	       if(archivo.exists()){
	              if(archivo.delete()){
	                     if(archivo.createNewFile()){
	                            FileWriter escritor = new FileWriter(archivo);
	                            BufferedWriter auxescritor = new BufferedWriter(escritor);
	                            auxescritor.write("Esto es una prueba!!");
	                            auxescritor.close();
                                    return true;
	                     }
	                     else
	                            System.out.println("Imposible crear archivo vacío");
	              }
	              else
	                     System.out.println("Imposible borrar archivo");
	       }
	       else
	              System.out.println("El archivo no existe");
               return false;
	}catch(IOException e){
               System.out.println("Ha ocurrido una excepción");
               return false;
	}
}


Con este código primero crearíamos un objeto de tipo File con la ruta completa del archivo, antes de realizar la llamada a este método deberíamos comprobar que el parámetro que le pasamos sea algo con extensión .txt. En primer lugar comprobamos que el archivo exista (podríamos cambiar un poco la función para que, en caso de que no existiese, crease directamente el archivo, este cambio es muy simple) en caso de existir, lo eliminamos, si la eliminación ha sido correcta entonces creamos un archivo nuevo vacío, y procedemos a insertar los datos.

Por último, un miembro del foro propuso un "reto" por así decirlo en C++, era bastante sencillo. Os dejo el enunciado y la resolución.

Dado un número 0 <= n <= 10100 por la entrada estándar (stdin), se debe mostrar por la salida estándar (stdout) su número girado, tal que si los dígitos de n son a1, ..., an, los de su número girado serán an, ..., a1 (es la misma definición, formalizada un poco). Tened en cuenta por ejemplo que el girado de 100 es 001 y no 1.

Restricciones: No se pueden utilizar arrays, structs, strings, clases ni nada similar, tan sólo los tipos de variables básicos de C/C++, es decir int, char, long long, etc.


La solución es la siguiente:
public static void procedimiento() throws IOException{
	int caracter = System.in.read();
	if(Character.isDigit((char)caracter)){
		procedimiento();
		System.out.print((char)caracter);
	}
}
	 
public static void main(String[] args) throws IOException {
	procedimiento();
	System.out.println();
}


De esta forma podríamos realizar el inverso de un número de forma muy sencilla. La única pega que veo al método es si el número fuese extremadamente grande (y con extremadamente me refiero a un número enorme enorme enorme) pues podría hacer un overflow en la pila con tanta llamada recursiva.

Bueno, hasta aquí la publicación de hoy, espero que a alguien le sea interesante o de utilidad.

Saludos.

viernes, 5 de agosto de 2011

Eliminación de espacios de sobra en cadenas de texto

Este tema es algo simple, pero dado que existen algunas alternativas, y es algo interesante a la hora de realizar trabajos con cadenas de texto, expongo aquí métodos en Java para que, en una cadena de caracteres, solo exista un espacio entre cada palabra y la cadena empiece y termine por una letra.

Una de las primeras formas que se nos podría ocurrir, sería la siguiente:

String cadena = new String("   Hola   esto es   una prueba      ");
cadena = cadena.trim();
String[] nueva = cadena.split(" +");

De esta forma, tendríamos un array de cadenas que corresponderían a la frase original, y, a la hora de reconstruir la frase podríamos realizarlo de la siguiente manera:

StringBuffer frase = new StringBuffer();
frase.append(nueva[0]);
for(int i = 1;i < nueva.length;i++)
    frase.append(" " + nueva[i]);

Lo que estamos realizando en este código es: en primer lugar llamamos al método trim() de la clase String, que elimina espacios por delante y por detrás de la cadena en caso de que los haya. Posteriormente llamamos al método split() y le pasamos como parámetro una expresión regular, en este caso " +" que es una expresión regular que reconoce las cadenas que poseen uno o más espacios. A continuación recorremos el array devuleto por split y vamos concatenando en un StringBuffer (se podría realizar concatenando en una variable String con el operador +, pero por temas de eficiencia es preferible y muy recomendable realizarlo con StringBuffer).

De esta forma, en la variable frase, que es de tipo StringBuffer, tendríamos la cadena inicial, pero sin espacios al principio ni al final y además con un único espacio entre fases.

Este método es correcto y realiza su función, pero hemos tenido que escribir 6 líneas de código para realizarlo además de declarar un stringbuffer y parece que hemos tenído que darle demasiadas vueltas.


Una segunda forma de realizar este cometido podría ser la siguiente:

String texto = new String("    Hola    esto   es una prueba         de eliminar espacios  ");
texto = texto.replaceAll(" +", " ");
texto = texto.trim();

Lo que hacemos es reemplazar con el método replaceAll() todas las cadenas especificadas por el primer parámetro, que es una expresión regular, y las sustituimos por el segundo parámetro, es decir, reemplazamos todos los espacios por un único espacio. Por ejemplo, sí tenemos: "El         jamón     ", tras aplicar este método nos quedaría lo siguiente: "El jamón ". Por tanto, a continuación tan solo deberíamos aplicar el método trim() y conseguiríamos lo que andábamos buscando.

En este caso, tenemos tan solo dos líneas de código (no cuento la declaración del objeto) y de nuevo realiza correctamente su cometido. Incluso de esta manera sería más eficiente que la propuesta anteriormente.

Una última forma podría ser empleando la clase StringTokenizer, y sería de la siguiente manera:

String texto = new String("    Hola esto   es  una prueba      de     eliminar      espacios                    ");
StringTokenizer tokenizer = new StringTokenizer(texto, " ");
StringBuffer frase = new StringBuffer();
if(tokenizer.hasMoreTokens()){
    frase.append(tokenizer.nextToken());
    while(tokenizer.hasMoreTokens())
        frase.append(" " + tokenizer.nextToken());
}

De nuevo en la variable frase, tendríamos el contenido.

Aquí estamos declarando una variable de tipo StringTokenizer y le pasamos como parámetro al constructor el texto y el delimitador de cada token. Por último, si queremos reconstruir la frase hacemos lo especificado arriba y la conseguiríamos.

Este método no me agrada tanto como el anterior, ya que hay que realizar muchas más comprobaciones (la del if se ejecutará siempre y las del bucle while tantas como tokens hayan a partir del primero) además de que se realizan muchas más llamadas a métodos que en el anterior caso.

No puedo decir que una sea más eficiente que la otra ya que debería saber el coste de cada método, pero por intuición (cosa que puede estar equivocada) diría que el 2º método sería el más eficiente.

Espero que sea de utilidad a todo aquel que venga a parar aquí.

Saludos y suerte.

viernes, 1 de julio de 2011

Cambiar icono JFrame y JDialog.

Hola y buenos días para las personas que vengan a parar aquí.

El motivo de esta entrada es explicar un poco como cambiar el icono de las ventanas JFrame y JDialog, ya que sinceramente, me parece algo lioso para quién no lo sepa, y que muchas de las cosas que leí por internet solamente me funcionó una de ellas.

Para las clases que programemos que hereden de JFrame, es bastante sencillo realizar el cambio de icono.

En primer lugar vamos a redefinir el método getIconImage(), heredado de la clase JFrame. El código que insertaremos en este método será el siguiente:


public Image getIconImage() {
    Image retValue = Toolkit.getDefaultToolkit().
    getImage(ClassLoader.getSystemResource("resources/Cadenas.png"));

    return retValue;
}

Donde "resources/Cadenas.png" es la ruta donde se encuentra el icono que queremos establecer para nuestra ventana. El formato debe ser PNG, ya que en otros formatos da ciertos problemas.

También es recomendable que nuestros iconos se encuentren en un paquete de nuestro proyecto. En mi caso cree una carpeta llamada resources y dentro de ella metí la imagen del icono.

Una vez realizado esto, en el constructor de nuestra clase pondremos la siguiente instrucción:

setIconImage(getIconImage());

De esta forma cambiaremos el icono por el que indicamos en el método redefinido.

Ya tenemos nuestro JFrame con nuestro icono personalizado.

En los JDialog la cosa cambia, ya que por si solos ellos no poseen un icono. Para poder establecer un icono para los JDialog, debemos crear un JFrame, asignarle a este JFrame un icono (como hemos visto anteriormente) y, a la hora de crear nuestro JDialog, pasarle como padre el JFrame modificado. De esta forma tendremos nuestro JDialog con icono.

lunes, 20 de junio de 2011

La primera entrada.

Aquí da comienzo un blog donde iré colgando algunas mini-aplicaciones creadas por mí. La mayoría estarán hechas en Java, así que para quién le guste algún programilla que cuelgue, y además necesite o le interese la portabilidad, le vendrá genial.

Sin más rodeos un pequeño programa que he terminado hoy mismo. Es bastante simple aunque al menos tiene una pequeña interfaz gráfica. El programa realiza lo siguiente: Coge dos ficheros, uno de origen y otro de destino. El fichero de origen contendrá nombres, o palabras cada nombre o cada frase en una linea. Se irá al segundo fichero y buscará ese nombre o frase o número o lo que sea, y lo sustituirá por lo que nosotros queramos. Además tiene la opción de sustituirlos todas las coincidencias de una vez, o ir de una en una.

Por ejemplo si tenemos estos dos ficheros:

FICHERO 1                                                            FICHERO 2
-------------                                                            -------------

Pascual Antonio López                                             Perico de los palotes
29-10-1990                                                             Santiago Segura
Hola esto es una prueba                                            Pascual Antonio López
Fuckencio Segura                                                     29*10+5 = 295
29*10+5 = 295

recorrería todo el fichero 1 leyendo cada linea, encontraría en el fichero 2 "Pascual Antonio López" y la expresión aritmética y las sustituiría por lo que quisiésemos.

El programa no estará exento de fallos seguramente. Sí alguien lo descarga, prueba y desea hacer cualquier comentario acerca de alguna mejora o fallo, encantado.

Sustitución de cadenas:

Sustitucion de cadenas en Java


VERSIÓN NUEVA CON ALGUNAS MEJORAS:


Sustitución de cadenas en Java Versión actualizada

Esta versión incluye la opción de poder seleccionar varios archivos destino al mismo tiempo (manteniendo pulsado control y pinchando en los distintos ficheros), además de poder seleccionar una carpeta contenedora donde se encuentren los distintos ficheros a analizar.

Además, a la hora de hacer reemplazos de uno en uno existe la posibilidad de dejar la cadena tal cual está.

También se ha cambiado el icono de la aplicación y ahora se muestra en la barra de herramientas.

VERSIÓN ACTUALIZADA Y FINAL:


Sustitución de cadenas en Java versión final

Esta versión incluye una ventana de ayuda con especificaciones generales del programa. Además se han resuelto algunos problemas de las versiones anteriores y añadido nuevas funcionalidades.

Dos capturas de pantalla del programa en ejecución:

Ventana principal

Ejemplo ejecución