jueves, 30 de noviembre de 2017

Eficiencia

Tiempo de ejecución y eficiencia de algoritmos

En esta sección vamos a estudiar algunas formas de medir la eficiencia de nuestros programas en Sage, y a estudiar la complejidad de las operaciones usuales en Sage.
Medir de forma precisa este tiempo no es una tarea trivial, y los resultados pueden variar sensiblemente de un ordenador a otro. La cantidad de factores que pueden influir en el tiempo de ejecución es muy larga:
  • algoritmo usado
  • sistema operativo
  • velocidad del procesador, número de procesadores y conjunto de instrucciones que entiende
  • cantidad de memoria RAM, y caché, y velocidad de cada una
  • coprocesador matemático, GPU
  • ...
Incluso en la misma máquina, el mismo algoritmo tarda algunas veces mucho más tiempo en dar el resultado que otras, debido a factores como el tiempo que consumen las otras aplicaciones que se están ejecutando, o si hay suficiente memoria RAM en el momento de ejecutar el programa.
Nuestro objetivo es comparar sólo los algoritmos , intentando sacar conclusiones independientes de la máquina.
Un mismo algoritmo se puede llamar con distintos datos de entrada. Nuestro objetivo es estudiar el tiempo de ejecución como función del “tamaño” de los datos de entrada . Para ello usamos dos técnicas:
  • Medir tiempos de ejecución de los programas con datos de entrada de distintos tamaños
  • Contar el número de operaciones que realiza el programa


Usar el siguiente código para medir la velocidad con la que procede el algoritmo

miércoles, 15 de noviembre de 2017

ArrayList en Java: Recorriendo y eliminando elementos

La clase ArrayList es una de las tantas que implementa la implementa la interfaz List. Por lo tanto es una colección ordenadaque puede contener elementos duplicados. A no confundir, cuando digo ordenada me refiero al orden en que se encuentran los elementos dentro de la lista, el cual es independiente a que los elementos se encuentren ordenados por algún criterio, como por ejemplo de menor a mayor.
La clase ArrayList es redimensionable, esto quiere decir que puede crecer o achicarse en tiempo de ejecución, aunque esto es una operación ineficiente cuando se desea insertar o eliminar elementos que se encuentran en el medio, ya que todos los elementos que se encuentran después de este deben ser desplazados. En este caso es mejor usar otras estructuras o clases como LinkedList.
ArrayList comparte muchos comportamientos con la clase Vector y se diferencian en que la primera tiene mejor performance, siempre y cuando no se necesite trabajar con operaciones sincronas.
Pero bueno, basta de tanta introducción. Querían código? Código tendran.
El siguiente ejemplo, Muestra Diferentes formas de recorrer una lista, la antigua forma usando for, con un for mejorado (enhanced for), y usando Iterator. Asi también muestra como eliminar elementos de una lista
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

public class ArrayListExample {

 public static void main(String[] args){

  String[] bands = {"Nirvana", "Jamiroquai",
                                     "Guns and Roses", "Funkadelic Parliament"};
  List<String> bandList = new ArrayList<String>();

  //add elements from String array to List
  for (String band : bands){
   bandList.add(band);
  }

  String[] undesirableBands = {"Nirvana","Led Zepelling",
                                              "The Strokes", "Jamiroquai"};
  List<String> undesirableList = new ArrayList<String>();

  //add elements from String array to List
  for (String undesirableBand : undesirableBands){
   undesirableList.add(undesirableBand);
  }

  System.out.println("List of bands has: ");
  for (int count = 0; count < bandList.size(); count++){
   System.out.printf("%s ", bandList.get(count));
  }

  removeBands(bandList, undesirableList);
  System.out.println("\n\nI just wish these bands: ");

  for (int count = 0; count < bandList.size(); count++){
   System.out.printf("%s ", bandList.get(count));
  }
 }

 public static void removeBands(Collection<String> bands,
                                         Collection<String> undesirableBands){
  Iterator<String> iteratorBand = bands.iterator();

  while(iteratorBand.hasNext()){
   if(undesirableBands.contains(iteratorBand.next())){
    iteratorBand.remove();
   }
  }
 }
}






Empecemos a analizar el código:

String[] bands = {"Nirvana", "Jamiroquai",
                       "Guns and Roses", "Funkadelic Parliament"};
  List<String> bandList = new ArrayList<String>();
Primero lo que hacemos es declarar e inicializar un arreglo de String bands que contendra los nombres de las bandas.
Una clase ArrayList es una clase generica y por lo tanto podemos especificarle el tipo de argumento que indican los elementos que la misma contendrá.
Pero, por qué declaramos bandList de tipo List? Simple, esto hace que nuestro código sea mas flexible para modificarlo. Si más tarde desearamos cambiar la implementación a LinkedList podríamos hacerlo solo modificando el lado derecho de la expresión.

Recorrer usando for mejorado

La primera forma de recorrer una lista es esta (Enhanced For Loop):
for (String band : bands){
bandList.add(band);
}

Esta forma de recorrer una lista esta disponible desde la versión Java SE 5.0. Su estructura es simple y permite recorrer todos y cada uno de los elementos de un arreglo/colección (recordar que una Lista es también una colección) en el orden en que se encuentran.
No hay mayores comentarios acerca del método add, este simplemente agrega un elemento a bandList.
De la misma forma se agregan los elementos a la lista undesirableBand. Esta, la vamos a usar para eliminar elementos de la primera
//add elements from String array to List
        for (String undesirableBand : undesirableBands){
            undesirableList.add(undesirableBand);
        }


Recorrer usando for clásico

La segunda forma de recorrer una lista es con la ya conocida estructura repetitiva for:
for (int count = 0; count < bandList.size(); count++){
System.out.printf("%s ", bandList.get(count));
}

En este caso, un tanto más engorroso que el primero, usamos métodos propios de la interfaz List. El primero es size() el cual devuelve un número entero con la cantidad de elementos de la lista. El otro método de la interfaz List es get(count) que devuelve el elemento que se encuentra en la posición count.

Recorrer utilizando un Iterador

La tercer forma de recorrer una lista es con un Iterator. La interfaz Iterator permite iterar sobre los elementos de una colección y como mencione anteriormente, una List es un tipo de colección.
El método removeBands(Collection<String> bands, Collection<String> undesirableBands) recibe dos parámetros que pueden ser cualquier tipo de Collections que contengan String. La razon de usar Collection en vez de ArrayList es que le da al método mayor flexibilidad, pudiendo recibir cualquier tipo de objeto que implemente la interfaz Collection.
Tanto la interfaz Collection como Iterator son de tipo genérico.
Analicemos el siguiente fragmento:
Iterator<String> iteratorBand = bands.iterator();

while(iteratorBand.hasNext()){
if(undesirableBands.contains(iteratorBand.next())){
iteratorBand.remove();
}
}

El método iterator() de bands.iterator() devuelve un Iterator de la colección. La interfaz Iterator tiene dos métodos importantes:
  • hasNext(): Este método devuelve true en caso de que existan más elementos
  • next(): Este método nos posiciona en el siguiente elemento del Iterator
El método contains() pregunta si la colección undesirableBands posee el elemento devuelto por iteratorBand.next(), en tal caso lo elimina de iteratorBand con la instrucción iteratorBand.remove()
Para borrar elementos de una colección, la mejor forma es usar Iterator.  Si intentamos hacerlo con el enhanced forobtendremos una excepción de tipo ConcurrentModificationException ya que estamos recorriendo e intentando modificar.
Si una colección es modificada, luego de que se creo el Iterator para la misma, dicho Iterator se vuelve inválido y las operaciones que se quieran realizar sobre el lanzarán excepción de tipo ConcurrentModificationException. Por esto se dice que los iteradores son de fallo rápido.

Interfaces

Un interfaz es una lista de acciones que puede llevar a cabo un determinado objeto. Sorpresa, ¿eso no eran los métodos que se definen en un...