jueves, 26 de septiembre de 2024

■ Lógica en los métodos de Math de Java

Los métodos de la clase Math de Java son funciones preprogramadas que nos ayudan a realizar determinadas operaciones de una forma más sencilla, por ejemplo si queremos el cuadrado de un número utilizaríamos el método "pow", el cual recibe dos parámetros, el primero es el número que queremos elevar a cierta potencia y el segundo es precisamente la potencia, en este caso para el cuadrado la potencia es 2, esto es así:

int num = 3;
int cuadrado = Math.pow(num, 2);

La variable "cuadrado" valdrá 9 ya que internamente Math.pow toma el número que le pasamos (num), lo eleva a la potencia que le indicamos (2) y nos devuelve el resultado.


■ Poniendo a prueba nuestra lógica

Pero ¿si quisieramos hacerlo manualmente?, tendríamos que pensar en cómo realizaríamos las operaciones para conseguir el mismo resultado, hacer esto supondría un buen ejercicio pues nos obliga a comprender qué es lo que está sucediendo y pensar de qué forma podemos llegar a dicho resultado, veamos algunos casos.


• Math.abs(double)

"abs" es una función de Math que devuelve el valor absoluto de un número, pero, ¿qué es el valor absoluto?, el valor absoluto de un número es la distancia que hay entre el número y el cero sin importar si el número es positivo o negativo, por ejemplo los números -6 y 5, veamos la siguiente recta numérica:


La distancia entre -6 y 0 es 6 ya que hay 6 unidades entre -6 y 0.
La distancia entre 5 y 0 es 5 ya que hay 5 unidades entre 5 y 0.

Podríamos decir que el valor absoluto de un número es el propio número pero siempre positivo y eso es precisamente lo que devuelve Math.abs(número).

int absoluto;
absoluto = Math.abs(-13); // Devolverá 13
absoluto = Math.abs(10);  // Devolverá 10

Si quisiéramos hacer una función propia que devuelva el valor absoluto de un número obviamente sin usar Math.abs, ¿cómo lo haríamos?.

Analizando el problema

Si el valor absoluto de un número es el mismo número pero positivo significa que:
 - El valor absoluto de un número positivo es el propio número.
 - El valor absoluto de un número negativo es el mismo número pero con el signo contrario, es decir, de negativo lo pasamos a positivo.

Entonces ¿cómo programaríamos eso?

Como siempre, en programación hay muchas soluciones y una de ellas podría ser:
1) Preguntar si el número es positivo o negativo.
2) Si el número es positivo, su valor absoluto es el mismo número.
3) Si el número es negativo lo multiplicaríamos por -1 para convertirlo a positivo y ese sería su valor absoluto.

Programando lo anterior:

public static double Absoluto(double numero) {
    double valor_absoluto;

    if (numero > -1) {
        valor_absoluto = numero;
    } else { // Si el número es negativo
        valor_absoluto = numero * -1; // Se convierte a positivo
    }

    return valor_absoluto;
}

Listo, ya tenemos una función propia que emula a Math.abs, recibe un número entero o decimal y regresa su valor absoluto.

int absoluto;
absoluto = Absoluto(-3); // Devuelve 3.0
absoluto = Math.abs(-3); // Devuelve 3

Ambas devuelven 3.


• Math.round(double)

Esta función lo que hace es redondear un número a su valor más cercano ya sea superior o inferior, por ejemplo si le pasamos 4.4 lo redondea a 4 y si le pasamos 4.6 lo redondea a 5, esto funciona como se muestra en la imagen de abajo:


Si nosotros quisiéramos hacer una función propia que haga lo mismo ¿cómo sería?

Analizando el problema

Si la función "round" redondea dependiendo de los decimales de esta forma:
 - Si los decimales del número terminan en .49 o menor, es decir, no llega a .5 lo redondea hacia el siguiente entero inferior.
 - Si los decimales del número terminan en .5 o superior lo redondea hacia el siguiente entero superior.

Partiendo de lo anterior, ¿cómo lo haríamos?

Una forma podría ser la siguiente:
1) Obtener el residuo del número con "módulo 2" del número (número % 2) y guardarlo en una variable.
2) Si el residuo es menor que 0.5 entonces convertimos el número a entero con (int)numero, "(int)" lo que hace es un "cast", es decir, convierte el decimal a entero y por lo tanto el número pierde los decimales quedando solo la parte entera, ejemplo si el número es 4.3 o 4.8, solo quedará el 4, por lo que esto funciona cuando los decimales son menores que .5 ya que nos queda el número original como si lo redondearamos hacia abajo.
3) Por otro lado, si el residuo no es menor que 0.5 significa que es 0.5 o mayor y por lo tanto el número debe redondearse hacia arriba, entonces se convertiría a entero con (int) igual que el caso anterior y como en este caso debe redondearse hacia arriba solo se le suma 1.

Programando lo anterior:

public static double Redondear(double numero) {
    double redondeado;
    double residuo;
        
    residuo = numero % 2; // Se obtiene el residuo
    if (residuo < 0.5) {
        redondeado = (int)numero; // Se convierte a int
    } else {
        redondeado = (int)numero + 1; // Se convierte a int y se le suma 1
    }

    return redondeado;
}

Hecho esto ya tenemos una función propia que se comporta igual que Math.round donde ambas funcionan de la siguiente forma:

double redondeado;
redondeado = Redondear(4.6);  // Devuelve 5.0
redondeado = Math.round(4.6); // Devuelve 5

Ambos devolverán 5.


• Math.max(int/double, int/double)

Esta función es muy simple, recibe dos números y devuelve el mayor, y si son iguales solo devuelve uno de ellos.

Analizando el problema

Creo que en este caso no hay mucho que pensar, la cosa es así:
1) Se reciben dos números y si son iguales se deveuelve uno de ellos.
2) Si son diferentes se pregunta cuál es el mayor y ese es el que se devuelve.

Programando lo anterior:

public static double Mayor(double n1, double n2) {
    double mayor;
        
    if (n1 == n2) {
        mayor = n1;
    } else if (n1 > n2) {
        mayor = n1;   
    } else {
        mayor = n2;
    }

    return mayor;
}

Listo, tenemos una función que nos regresa el mayor de dos números, sean enteros o decimales.

int nMayor;
nMayor = Mayor(4, 7);    // Devolverá 7.0
nMayor = Math.max(4, 7); // Devolverá 7

Ambos devolverán 7.


• Math.pow(int/double, int/double)

Esta es una función que recibe dos números y lo que hace es elevar el primero a la potencia que indique segundo. Como ya se había mencionado al inicio, si quisiera elevar 3 al cuadrado usando Math.pow(base, potencia) le pasaría los números 3 y 2, donde 3 es el número a elevar (base) y 2 representa al cuadrado (potencia).

Analizando el problema

Sabemos que si se quiere elevar un número m a una potencia n una forma es multiplicar n-1 veces el número m por sí mismo, por ejemplo:

Si m = 2 y n = 2 la multiplicación sería → m * m
Si m = 2 y n = 3 entonces sería → m * m * m

Pero, ¿qué pasa si la potencia es negativa?

Por ejemplo 2⁻² es 0.25.
¿Cómo obtenemos eso?, la respuesta es dividiendo el número entre sí mismo n+1 veces (condierando a n como positivo), esto es:
Si m = 2 y n = -2, entonces la división sería → m / m / m / m
Si m = 2 y n = -3, entonces sería → m / m / m / m / m


Considerando esto, ¿cómo lo haríamos?

Algo así:
1) Primero preguntamos si el número base es mayor que 0.
2) Preguntamos si la potencia es igual a 0, si es así regresamos 1.
3) Si la potencia no es 0 preguntamos si es mayor a 0.
4) Si la potencia es mayor a 0 entonces se multiplica el número por si mismo el mismo número de veces que valga la potencia - 1, ejemplo si la potencia es 3 se multiplica dos veces, n * n * n.
5) Si la potencia es menor que 0, en lugar de multiplicar se divide el número entre sí mismo el mismo número de veces que vale la potencia convertida a positiva + 1, ejemplo si la potencia es -3 se dividiría 4 veces, n / n / n / n / n.

Programando lo anterior:

public static double Potencia(double base, double potencia) {
    double resultado = 0;
        
    if (base > 0) {
        if (potencia == 0) {
            resultado = 1;
        } else if (potencia > 0) {
            resultado = base;
            for (int i=1; i < potencia; i++) {
                resultado = resultado * base
            }
        } else {
            resultado = base;
            for (int i=0; i < ((potencia * -1) + 1); i++) {
                resultado = resultado / base;
            }
        }
    }

    return resultado;
}

Así tenemos una función propia que nos regresa un número elevado a la potencia que le indiquemos, igual que Math.pow().

int potencia;
potencia = Potencia(2, 3);  // Devolverá 8.0
potencia = Math.pow(2, 3);  // Devolverá 8
potencia = Potencia(2, -3); // Devolverá 0.125
potencia = Math.pow(2, -3); // Devolverá 0.125

Ambos regresan 8.


■ Conclusión

Las funciones preprogramadas del lenguaje nos ayudan a resolver situaciones comunes como lo es elevar un número al cuadrado, obtener su raíz, redondearlo, etc., haciendo uso de ellas nos ahorramos tener que codificarlas nosotros mismos sin embargo cuando se está aprendiendo intentar emularlas puede ser un buen ejercicio de lógica ya que debemos pensar en una solución que nos lleve al mismo resultado.

No hay comentarios:

Publicar un comentario