viernes, 8 de agosto de 2014

Variables primitivas en Java

A pesar de que Java es un lenguaje orientado a objetos, existen también unos tipos de datos que no son objetos; se conocen como variables primitivas. Éstas variables almacenan datos puntuales, como por ejemplo números, a diferencia de los objetos que almacenan datos complejos. Estas variables se almacenan en la memoria Stack.

Los 8 tipos de datos primitivos que Java nos ofrece son:


                       
                       
               
           
            
Un momento, ¿Dónde está el String? En java,  String es una clase especial, no hace parte de los datos primitivos. La manera en que se declara en inicializa una variable primitiva es:

[modificador de acceso]   [static] [final]  tipo_de_dato  nombre_de_la_variable = valor;

No es indispensable escribir el modificador de acceso; cuando se omite, se toma como default (package), de otro modo puede ser: public, protected o private.

El modificador static tampoco es obligatorio ponerlo; lo mismo sucede con final.
Nótese como se inician en minúscula, dado que no son objetos. Sin embargo existe una manera de tratarlos como tales, pero por lo pronto los veremos como un grupo separado de los objetos.


byte

Las variables de tipo byte no se suelen usar para realizar cálculos numéricos dada su limitada capacidad de almacenamiento. A pesar de que su uso en reemplazo del int puede devengar en un ahorro de memoria, los ordenadores actuales traen consigo cantidades grandes de memoria y no se ven afectadas por el uso de variables de tipo int.

byte b1 = 2; 
byte b2 = -0;          // Java lo toma = 0
byte b3 = '\n';        // Valor Unicode (UTF-16) del salto de línea (10)
byte b4 = 022;         // Número en octal, (18 en decimal)
byte b5 = 0xF;         // Número en hexadecimal ( 15 en decimal)


byte b6 = 999;         // Error de compilación, 999 excede el dominio de byte

Nótese como las variables de tipo byte pueden almacenar algo más que simple números.


short

Si nos fijamos en la tabla al inicio del post, notaremos como las variables short tienen una capacidad de almacenamiento mayor que los byte, sin embargo para gran parte de las aplicaciones en el mercado esta capacidad se queda corta, por lo que es más usual emplear los int.

short s1 = 999; 
short s2 = -0;          // Java lo toma = 0
short s3 = '\n';        // Valor Unicode (UTF-16) del salto de línea (10)
short s4 = 0b1001101;   // Número en binario (77 en decimal)
short s5 = 0xF;         // Número en hexadecimal ( 15 en decimal)


short s6 = 32768;       // Error de compilación,se excedió el dominio de short



int

Es el tipo de dato más común cuando de manejar números enteros se trata, la capacidad de almacenamiento es suficiente para casi todas las situaciones.

int i1 = 930489; 
int i2 = -0;          // Java lo toma = 0
int i3 = '\n';        // Valor Unicode (UTF-16) del salto de línea (10)
int i4 = 022;         // Número en octal, (18 en decimal)
int i5 = 0xF;         // Número en hexadecimal ( 15 en decimal)

/*El underscore en medio de dos digitos no genera error, sirve de guía visual 
para el progamador cuando son números muy largos. Al momento de imprimir no aparece*/
int i6 = 326_832;       


long

Cuando en java escribimos un número entero, por ejemplo 354, éste será tomado por defecto como un int a no ser que explícitamente le digamos de que tipo. Las variables tipo long sólo se recomiendan usarlas cuando se trabaje con números realmente grandes.




/**Java por defecto toma el 34 como un int, pero automaticamente hace algo
denominado  promoción y lo trata como un long **/
long m1 = 34;               
                            
/**Lo ideal es colocar la L, al final del número para indicar que 
se trata de un long. También se puede la "l" minúscula pero
no se recomienda por su parecido con un 1**/
long m2 = 34L;              


/**Error de compilación, el numero que le asignamos sobrepasa el alcance del int,
entonces se hace necesario colocar L al final para que de antemano trate 
el numero como long**/
long m3 = 2147_483_648;     


float y double

Si deseamos trabajar con números decimales, Java nos ofrece estos dos tipos de datos primitivos. Por defecto los decimales son tomados como double, es decir, el número 10.43 será tratado como un double, si queremos que lo trate como un float debemos escribirlo de este modo 10.43f.
Se recomienda usar double sólo cuando los números sean realmente grandes e importe la precisión en los decimales, por ejemplo en aplicaciones matemáticas, etc.


/**Java por defecto toma el 324 como un int, pero automaticamente hace algo
denominado  promoción y lo trata como un float **/
float f1 = 324;               
                            
/**Lo ideal es colocar la f, al final del número para indicar que 
se trata de un float.**/
float f2 = 324f;              


/**Error de compilación, al escribir 23.41 Java cree que es un double, en
este caso no lo puede tratar automaticamente como float, por lo que se 
hace necesario escribir la f al final**/
float f3 = 23.41;

/**Java por defecto toma el 665 como un int, pero automaticamente hace algo
denominado  promoción y lo trata como un double **/
double d1 = 665;

double d2 = 19.8;     // Ningún error de compilación
double d3 = 32.22;    // Ningún error de compilación

/** A pesar de que se le dice explícitamente que lo trate como float
Java hace la promoción y lo trata como double*/
double d4 = 57.3f;   
     


boolean

Los datos de tipo boolean sólo pueden contener dos valores, true o false, sin embargo esto no significa que explicitamente se deba escribir alguno de esos valores.


/** OK **/
boolean b1 = true;

/** dado que 3>2 es verdad entonces b2 vale true**/
boolean b2 = 3>2;

 

char

Para finalizar el post, hablaremos un poco de las variables tipo char. En primer lugar, y dado que la capacidad de almacenamiento de éstas es de 2 bytes, es posible darle valores numéricos enteros que se encuentren en el rango de [0,65536). Cuando se le asigna un número entero comprendido en ese rango a una variable, dicho valor corresponde a un caracter Unicode (UTF-16), por lo cual si se imprime la variable se verá en pantalla el símbolo.

/**  **/
char c1 = 169;    // ©
char c2 = 0xA3;   // £
char c3 = '썐';   // Debe ir entre comillas sencillas cuando se escribe el caracter

char c4 = 'sd';   // Error de compilación, sd conforman varios caracteres y no uno.
char c5 = "d";    // Error de compilación, se debe usar comillas sencillas ''

 

viernes, 26 de julio de 2013

Algoritmos de ordenamiento en java.

En este post se mostrarán algunos métodos de ordenamiento; este tipo de algoritmos pone los elementos de un vector -casi siempre- según un orden numérico de manera que sea más fácil la manipulación de estos elementos. También resulta menester que un vector esté ordenado para aplicarle ciertos algoritmos de búsqueda, como el Binary search, que a pesar de ser eficiente debe ir de la mano con alguno de los siguientes algoritmos:


Ordenamiento de burbuja ( Bubble sort ).


Uno de los más sencillos entre los que se veremos. El ordenamiento por burbuja resulta no ser eficiente para vectores con muchos elementos puesto que el orden de complejidad es O(n2)  .Se puede implementar de diversas maneras, mostraremos dos, una usando ciclos for anidados, otra reemplazando uno de estos ciclos for por un while.

El siguiente algoritmo imprime:
[8 10 23 7 6 1 4 2 3 ]
[1 2 3 4 6 7 8 10 23 ]
[4 1 31 8 9 5 10 11 13 ]
[1 4 5 8 9 10 11 13 31 ]


public class BubbleSort {

    public static void manera1(int [] vector){

     for(int i=0; i < vector.length;i++){
      for(int j=1; j < (vector.length - i );j++){
       if(vector[j-1]>vector[j]){
        int aux = vector[j-1];
        vector[j-1] = vector[j];
        vector[j] = aux;
       }
      }
     }
    }

    public static void manera2(int [] vector){
  boolean control;
     while(true){
      control=false;
      for(int i=0;i < (vector.length)-1;i++){
       if(vector[i]>vector[i+1]){
        int aux = vector[i];
        vector[i]=vector[i+1];
        vector[i+1]=aux;
        control = true;
       }
      }
      if(control==false)break;
     }
    }

    public static void printElements(int [] vector){
     System.out.print("[");
     for(int i=0;i < vector.length;i++){
      System.out.print(vector[i] + " ");
     }
     System.out.println("]");
    }


    public static void main (String[]args){
     int [] vector1 = {8,10,23,7,6,1,4,2,3};
     int [] vector2 = {4,1,31,8,9,5,10,11,13};

     printElements(vector1);
     manera1(vector1);
     printElements(vector1);

     printElements(vector2);
     manera2(vector2);
     printElements(vector2);
    }
}

En ambos métodos el proceso para intercambiar de posición dos elementos es el mismo. Se debe crear una variable auxiliar para almacenar el valor de alguno de esos dos elementos llamémoslo A, luego el otro valor (B) pasa a la posición de A, y finalmente en donde teníamos el valor de B, allí se reemplaza este por A, que estaba contenido por la variable auxiliar que mencionamos. Si esta no se creara , el valor de A se perdería al ser reemplazado por el de B.
Analicemos pues los dos métodos que se aplicaron,

manera1: En este método como su puede ver, se usa un for dentro de otro (anidado). En una primera iteración del ciclo externo, lo que hace el for interno es verificar cada par de elementos consecutivos, es decir, elemento 1 vs elemento 2, elemento 2 vs elemento 3 ..... elemento (n-1) vs elemento n, -siendo n el número total de elementos que tiene el vector. De manera que todos los elementos se verifican al menos una vez lo que conduce a que el mayor termine en la última posición.
Continuan las iteraciones del ciclo externo y se verifican los elementos hasta (n-1), -esto debido a que ya conocemos que el mayor está en la posición final y sería en vano que se verificara éste con otro número.- hasta que el segundo mayor termine en la penúltima posición. ...

manera2:  Bastante similar a la anterior con la diferencia de que esta puede realizar menos iteraciones  -siempre que el vector esté relativamente ordenado-. El for funciona de la misma forma en como lo hace en el método anterior. Siempre que haya una modificación de posiciones durante todo el ciclo for, se volverá a realizar el siguiente con el fin de determinar si falta algún cambio. Si esto no ocurre es deducible que del inicio al final del vector, sus elementos están ordenados de manera que nunca se cumplió el if. Acá termina el trabajo del algoritmo.


Ordenamiento por inserción (insertion Sort).

Otro de los sencillos, aunque ineficiente cuando de manejar muchos datos se trata, O(n2).
El ordenamiento por inserción consiste en tomar uno a uno los elementos del vector (excepto el primero), compararlos con los elementos anteriores hasta detenerse si se topa con uno menor al que se está moviendo o bien si llegó al inicio del vector.

Así pues supongamos el vector [ 1, 2, 5, 6, 4, 3]
Primeramente se tomará el 2, se compara con los elementos anteriores. Como 2 > 1 entonces el 2 se quedará allí.
Luego el 5 se compara con el 2, pero como 5 > 2 entonces el 5 también se quedará allí.
Lo mismo pasa con el 6, que al ver que es mayor que su inmediato término anterior se queda allí.
Ahora bien, el 4 se compara con el 6 y debido a que 4 < 6, entonces se cambian de posición. Nuevamente el 4 se intercambia con el 5, hasta que se topa con el 2 y allí no retrocede más.

Así va quedando el vector [ 1, 2, 4, 5, 6, 3]

Ya el algoritmo examinó los valores correspondientes a los índices 0, 1, 2, 3, 4, por lo cual resta examinar el valor que tiene el vector en el índice 5.
Este valor es el 3, que es menor a 6, 5 y 4, llegando a estar en medio del 2 y el 4. Así:

[ 1, 2, 3, 4, 5, 6] 

public class InsertionSort {

    public static int [] organizar(int [] vector){
     for(int i=1 ; i < vector.length; i++){
      for(int j=i; j >= 1; j--){
       if(vector[j] < vector[j-1]){
        int aux = vector [j-1];
        vector[j-1] = vector[j];
        vector[j] = aux;
       }
      }
     }return vector;
    }

    public static void print(int [] vector){
     for(int i = 0; i < vector.length; i++){
      System.out.print(vector[i] + " ");
     }
    }

    public static void main(String [] args){
     int [] array = { 6,11,11,3,4,2,7,-21,4,99};
     int [] vector = organizar(array);
     print(vector);
    }
} 

Ordenamiento por mezcla (MergeSort).

Este algoritmo se puede considerar más eficiente que los antes vistos, puesto que tiene complejidad : O(nlogn). No es tan sencilla su implementación como los algoritmos anteriores, pero es bastante estable y de hecho en ciertos lenguajes, el manejo de arrays utiliza este algoritmo.

Tomada de Wikipedia.

A grandes rasgos el algoritmo, empieza a dividir en mitades al vector inicial (mergeSortHelper), hasta que se encuentran elementos aislados; en este punto y gracias al metódo merge, cada par de vectores se analiza y mezcla de tal forma que se retorna uno organizado, esto pasa tantas veces como se dividió el vector inicial. Finalmente el resultado es el vector organizado. Tratar de hacer un vector ordenar partiendo de dos ordenados resulta más fácil que hacerlo sin que estos últimos no estén ordenados.


 public class MergeSort {

 //Dados dos vectores, los une y retorna uno ordenado.
 public static int[] merge(int[]a,int[]b){

  int []result= new int[a.length+b.length];
  int i=0,j=0;
  for(int k=0; k <  result.length ;k++){
   if (j==b.length || (i < a.length && a[i]<=b[j])) {
    result[k]=a[i];
       i++;
   }
   else {
    result[k]=b[j];
    j++;
   }
  }
  return result;
 }

 //Divide
 public static int[] mergeSortHelper(int []data, int bottom, int top){

  if ( bottom==top) return new int[] {data[bottom]};

    int midpoint=(bottom + top)/2;
  return merge(mergeSortHelper(data,bottom,midpoint),mergeSortHelper(data,midpoint+1,top));

 }

 public static int[] mergeSort(int [] data){
  return mergeSortHelper(data,0,data.length-1);
 }

 public static void print (int [] vect){
  System.out.print("[");
  for(int i=0; i < vect.length;i++){
   System.out.print(vect[i] + " , ");
  }
  System.out.print("]");
 }

 public static void main(String[]args){
  int[] x={-3,-7,-11,6,7,-11,6,7,-11,};
  int [] y = mergeSort(x);
  print( y);
 }
}

Ordenamiento rápido (QuickSort).

También aplica recursividad. Caso promedio O(nlogn).
Consiste en elegir un pivote y de este determinar cuales son menores y mayores, los primeros se ubicarán al lado izquiero del pivote, mientras que los mayores (o iguales) a la derecha.
El vector inicial es partido en dos, justo en donde se halla el pivote. A cada sub-vector se le aplica el mismo proceso recursivamente..
En este algoritmo el pivote es el último elemento del vector.


public class QuickSort {
 // intercambia dos elementos de un vector.
 public static void swap(int[]data, int i, int j){
  int temp=data[i];
  data[i]=data[j];
  data[j]=temp;
 }
 //ubica los elementos menores que el pivote a su izquierda
 //mientras que los mayores a su derecha.
 public static int partition(int[] data, int bottom, int top){
  int  firstAfterSmall=bottom;
  int pivot=data[top];
  for(int i=bottom; i < top;i++){
   if(data[i] <= pivot){
    swap(data,i,firstAfterSmall);
    firstAfterSmall++;
   }
  }
  swap(data,firstAfterSmall,top);
  return firstAfterSmall;
 }
 //aplica el método anterior, luego se divide en dos y a cada división
 //le aplica este mismo proceso -recursivamente-.
 public static void quickSortHelper(int[] data, int bottom, int top){
  if (bottom < top){
   int fasindex=partition(data,bottom,top);
   quickSortHelper( data,  bottom, fasindex-1);
   quickSortHelper( data,  fasindex+1, top);
  }
 }

 public static void quickSort(int[]data){
  quickSortHelper( data,  0, data.length - 1);
 }
 public static void print(int [] data){
  for(int i=0;i < data.length;i++)
   System.out.print(data[i] + " , ");
 }

 public static void main(String[]args){
  int[] x=new int[10];
  for(int i=0;i < x.length;i++)
  x[i]=(int)(Math.random()*100);

  quickSort(x);
  print(x);
 }

}

La variable denominada 'fasindex' que entre otras cosas significa First After Small index, (demasiado largo para llamar una variable así) se usa para determinar en que punto va a ser partido el vector que se maneje en el momento. En un caso ideal el vector se partirá en la mitad, en un caso desastroso en un borde...

jueves, 25 de julio de 2013

Algoritmos de búsqueda en Java.

En lo concerniente al manejo de datos, es de suma importancia conocer los métodos que se suelen usar para localizar elementos en una estructura de datos. Para facilitar las cosas en este post se usarán vectores que contienen números.


Búsqueda secuencial o lineal (Linear Search)


Es posiblemente el más sencillo, de orden de complejidad lineal. Consiste en desplazarse uno a uno por cada elemento del vector mientras se compara con el que estamos buscando. El proceso bien puede finalizar cuando se encuentra o cuando termina de recorrer el vector.

Cabe aclarar que en Java los índices en los vectores comienza en cero, de manera que en el ejemplo, el índice 1 alberga un int de valor 2.

Lo que imprime el algoritmo es:

6
-1



 /*Busca un elemento ingresado recorriendo
 *linealmente el vector donde se buscará este primero.
 */

public class LinearSearch {

 // Retorna el índice donde se halla el elemento que se buscó, o -1 si este no está.
    public int find(int[] vector,int value){
     for(int i=0; i < vector.length;i++){
      if(value==vector[i])
       return i;
     }

 return -1;
    }

    public static void main(String[] args){

     LinearSearch ls = new LinearSearch();
  int []vector = { 1, 2, 3, 4 , 5,6 ,7, 8, 9, 10};
  int elemento1 = 7;
  int elemento2 = 12;

  System.out.println(ls.find(vector,elemento1));
  System.out.println(ls.find(vector,elemento2));


    }


}

Búsqueda binaria o dicotómica (Binary Search)


Más eficiente que el de búsqueda lineal, este algoritmo  tiene orden de complejidad logarítmica, O(log n) aunque tiene un precio a pagar por esta ventaja, y es que para que sea útil este mecanismo, el vector a tratar debe estar organizado. Entonces de lo anterior se deduce que es ideal a usar en situaciones donde el vector posee muchos elementos.

Asumiremos que contamos con un vector organizado ascendentemente ya sea que se haya aplicado otro algoritmo para esto.

Explicación del algoritmo: El número que buscamos -target- se compara con el 'midPoint', usualmente éste se toma como el centro del intervalo acotado por 'bottom' y 'top'.
Si el target es menor que midPoint, en el próximo ciclo, la búsqueda se efectuará en la primera mitad del vector. En cambio si target es mayor que midPoint, en el siguiente ciclo no se tendrá en cuenta la primera mitad. Así transcurren los ciclos, partiendo el intervalo de búsqueda.
Si se tuviese un vector ordenado descendentemente sólo se necesita reemplazar los dos primeros condicionales por <  y > respectivamente.

El algoritmo imprime:
0
-1


public class BinarySearch {



 public int find(int[] vectorOrganizado , int target)  {

  int top = vectorOrganizado.length - 1 ;
  int bottom = 0;
  int midPoint;

  while(bottom<=top){
   //se toma la parte entera, es decir, de 2.5 se toma el 2.
   midPoint = (bottom + top)/2;
   if(target > vectorOrganizado[midPoint])
    bottom = midPoint + 1 ;
   else if (target < vectorOrganizado[midPoint])
    top = midPoint - 1;
   else
    return midPoint;

  }
  return -1;
 }

 public static void main(String []args){
  BinarySearch bs = new BinarySearch();
  int [] vect = {1,2,3,4,5,6,7,8,9,10};
  int value1 = 1 ;
  int value2 = 22;
  System.out.println(bs.find(vect,value1));
  System.out.println(bs.find(vect,value2));

 }

}

viernes, 15 de marzo de 2013

Primer programa en Java

El Hola Mundo suele ser el primer programa que se enseña cuando se está empezando a programar.
Para dar continuación a dicha tradición se mostrará un código sencillo escrito en Java, que básicamente muestra un mensaje en pantalla - salida por defecto- 



public class Main {
 
    public static void main(String[] args) {
        System.out.println("¡Hola Mundo!");
    }
}
En java todo lo que se programa debe pertenecer a una Clase. Las clases son "plantillas" que contienen atributos y métodos. En próximos posts se ampliará el tema.

En la línea 4 se llama a un método que muestra el mensaje que se ingrese como parámetro, es decir lo que se escribe dentro del paréntesis. Es importante escribir dicho mensaje entre comillas, de lo contrario el programa no funcionará y tendrás tu primer ataque de ira de manera precoz.