13 nov 2016

Números muy grandes, con todos sus dígitos, en Java

¿Cómo calcular la última cifra de un número muy grande, por ejemplo 22016? ¿Y si quisiéramos también la penúltima cifra? Es un problema típico, por ejemplo, para fomentar el razonamiento en las olimpiadas matemáticas.

Si analizamos las potencias de 2, observamos que se repite la serie de terminaciones 2  4  8  6, por lo que necesariamente tiene que ser uno de estos dígitos.
21 -> 2
22 -> 4
23 -> 8
24 -> 16
25 -> 32
26 -> 64
27 -> 128
28 -> 256
29 -> 512
210 -> 1024
211 -> 2048
212 -> 4096
213 -> 8192
214 -> 16384
215 -> 32768
216 -> 65536
217 -> 131072
218 -> 262144
219 -> 524288
220 -> 1048576

Si dividimos 2016 entre 4 terminaciones, nos da 54 grupos y de resto 0, por lo que la última cifra tiene que ser la última del grupo, es decir 6.

Si queremos la penúltima cifra tenemos que analizar los dígitos que acompañan al 6, que también son una serie, en este caso de cinco elementos, 1  5 9 3 y 7. Si ahora dividimos nuestros 54 grupos entre cinco, obtenemos 10, y de resto 4, por lo que el penúltimo número será el cuarto de la serie, es decir el 3.

Pero, ¿por qué no obtener todas las cifras del número? Pues aparentemente algo tan sencillo no es posible en muchas calculadoras a no ser que se utilice la notación científica. Pero el problema es que con esta notación no vemos todas las cifras. Si recurrimos a los lenguajes de programación el problema viene dado por los tipos para representar los enteros, los int, double, o  long, no nos permiten almacenar números tan grandes. La solución viene por utilizar clases especializadas como puede ser la clase BigInteger de Java.

El siguiente código nos muestra el resultado de 22016, aún más, muestra todas las potencias de 2, hasta 2016.

import java.math.BigInteger;

public class Potencias {

    public static void main(String[] args) {

       BigInteger resul = new BigInteger("1");

       BigInteger dos = new BigInteger("2");

       for (int i=1; i<=2016; i++){
           resul= resul.multiply(dos);
           System.out.println("2^"+ i + " -> " + resul+ "\n");
       }
    }
}

Y de esta forma, podemos ver que 22016 es un número de sólo 607 cifras:

7524389324549354450012295667238056650488661292408472865850279440061341770661038088891003609523855490537527473858068236032063038821912119420032983735773778315780422968627185582125139830259059580693966159220800634538007951025529707819651368618588002973837229854435730968342995245834129352264002058451047722604571453619205472623157541916371455764131661512732115122042085430429090324954236930736866452001076451671762299658372499364800367306988138217572983729940207496105489713305332746758395131148149101871456611571055068153665866066783899124296271513772531723497342815490725823828326183977758546404902789185536

10 oct 2016

Manejo de fechas en Java

java.util.Date


Durante mucho tiempo la clase utilizada para manejar fechas fue java.util.Date, pero hoy día muchos de sus métodos se consideran obsoletos.

java.util.Calendar


Posteriormente se utilizó la clase java.util.Calendar y java.util.GregorianCalendar. Su principal diferencia con Date, es que Calendar es mutable, permite cambiar el valor de un objeto sin tener que generar un nuevo objeto.
Calendar es una clase abstracta por lo que sus objetos deben instanciarse a través de alguna de sus clases heredadas, por ejemplo con el método getInstance() que nos devolvería la fecha actual del sistema o bien con GregorianCalendar, que sí es instanciable. Ejemplos:

Calendar hoy= Calendar.getInstance();
Calendar hoy= new GregorianCalendar();

Calendar dispone de métodos “get” y “set” que permiten recuperar o almacenar valores de las fechas, los correspondientes al argumento pasado, pero también tiene métodos “add” y “roll” que permiten a partir de unos valores incrementar o decrementar las fechas, recalculando las fechas correctas si es necesario.

Ejemplo:


Si quisiéramos darle valores, por ejemplo de una fecha de nacimiento, sería:



La clase Calendar permite trabajar de dos modos, en un modo indulgente o permisivo (Lenient), que es el que está activo por defecto, y que nos permitiría almacenar fechas sin realizar validaciones, por ejemplo darle al atributo día el valor 34, o no tener en cuenta sin un año es bisiesto, y un modo más restrictivo, que produciría una excepción de tipo IllegalArgumentException, si los parámetros de la fecha no son correctos.
Para activar el modo restrictivo, o no indulgente, pondríamos:
nacim.setLenient(false);

El ejemplo anterior con una fecha no válida, daría el siguiente resultado:



Paquete java.time


A partir de la versión 8 de Java se recomienda el uso del paquete java.time. Este paquete contiene clases para tratar fechas, tiempos, instantes y duraciones. Por ejemplo LocalTime, permite almacenar horas sin fecha, como: 14:37:26;  LocalDate, sirve para almacenar fechas sin hora, como 2015-05-22; LocalDateTime, permite almacenar fechas con hora.

LocalDate

Los objetos se crean con el método estático of() de la clase LocalDate.
LocalDate fecha = LocalDate.of(2016, 3, 27);

Si lo que queremos es obtener la fecha actual del sistema, utilizaremos el método now():
LocalDate ahora = LocalDate.now();

Dispone de métodos get para obtener los componentes día, mes y año de una fecha:
int dia, mes, anno;
dia= fecha.getDayOfMonth();
mes= fecha.getMonthValue(); //devuelve valores 1..12
anno= fecha.getYear();

Otros métodos get:
DayOfWeek diaSemana= fecha.getDayOfWeek();
Month nombreMes= fecha.getMonth();

Tiene métodos para incrementar, plus(), o para hacer retroceder una fecha, minus(). Estos métodos admiten dos argumentos el primero es el número de unidades y el segundo que expresa el tipo de unidades, mediante el tipo enumerado ChronoUnit, que tiene valores como DAYS, WEEKS, MONTHS o YEARS. Ejemplos:
LocalDate fecha2= fecha1.plus(5, ChronoUnit.MONTHS); //Aumenta fecha1, 5 meses
LocalDate fecha2= fecha1.minus(3, ChronoUnit.DAYS); //Disminuye fecha1, 3 días

Otros métodos interesantes:
·         isLeapYear()      verifica si un año es bisiesto
·         until()                   permite calcular el tiempo entre dos fechas
·         parse()                 permite analizar una cadena para generar una fecha

Para establecer el formato de las fechas, se utiliza la clase DateTimeFormatter.