Número palíndromo en Java

Más ejercicios resueltos

Si deseas revisar más ejercicios resueltos, haz click en el siguiente botón. 

A continuación presentamos 3 alternativas de solución al problema de verificar si un número es palíndromo o capicúa en Java. Si deseas ver el enunciado del problema, haz click en el siguiente enlace

A continuación presentamos la primera alternativa de solución al problema de número capicúa. Usaremos para su resolución el paradigma de programación modular y la técnica de diseño descendente. Asumiremos que algunos métodos ya existen y luego nos preocupamos de su implementación. 

La primera versión que revisaremos es la más clásica de todas. Se basa en invertir los dígitos del número. Veremos a continuación el método principal.

Método Principal

El objetivo del método principal es realizar la lectura del dato de entrada, en este caso el número \texttt{n}. Con el dato leído se invoca al método \texttt{es\_palindromo}, para determinar si es número en cuestión es palíndromo o no. En esta función, no nos preocupamos en cómo implementar \texttt{es\_palindromo}, asumimos que ya existe y luego veremos cómo implementarla.

Dado que el enunciado indicaba que el número \texttt{n} era del tipo entero, es posible que el usuario ingrese números negativos. Por este motivo, antes de realizar la invocación, utilizamos el método \texttt{abs} para asegurar que no se invocará a la función con número negativos. Recuerde que para usar el método \texttt{abs}, deberá importarse éste de \texttt{java.lang.Math.abs}.

package palindromo_v1;

import static java.lang.Math.abs;
import java.util.Scanner;

public class Palindromo_v1 {

    public static void main(String[] args) {
        int numero;
        Scanner reader = new Scanner(System.in);
        System.out.printf("Ingrese un número: ");
        numero = reader.nextInt();

        numero = abs(numero);
        if (es_palindromo(numero)) {
            System.out.printf("El número es palíndromo%n");
        } else {
            System.out.printf("El número no es palíndromo%n");
        }
    }
} 

Método verifica palíndromo

El método \texttt{es\_palindromo} tiene por objetivo determinar si determinado número es palíndromo o no. Recibe como parámetro el número \texttt{n}. Se asume que este parámetro \texttt{n} es mayor o igual a cero. El método retorna \texttt{true} en caso el número sea palíndromo y \texttt{false} en caso contrario.

En realidad el algoritmo es bastante sencillo. Se basa en invertir el número \texttt{n} y si el número invertido es igual a \texttt{n}, entonces estaremos ante el caso de un número palíndromo.  Para invertir el número usamos el método \texttt{invierte\_numero}. Como estamos aplicando la técnica de diseño descendente, en este método no nos preocupamos por la implementación de \texttt{invierte\_numero}. Asumimos que este método existe y luego la implementaremos. De esta forma estamos encapsulando la complejidad del problema.

    private static boolean es_palindromo(int numero) {
        int numero_invertido = invierte_numero(numero);
        return numero == numero_invertido;
    } 

Método número invertido

En esta parte, nos dedicaremos a implementar el método \texttt{numero\_invertido}. Este método tiene por objetivo invertir las cifras de determinado número. Recibe como parámetro el número \texttt{n}. Se asume que este parámetro \texttt{n} es mayor o igual a cero. El método retorna un número que contiene las mismas cifras de \texttt{n} pero en orden inverso. Por ejemplo, si se recibe el número 123, retornará el número 321.

Para implementar este método, utilizaremos una estructura algorítmica iterativa con entrada controlada. La cual procedemos a explicar paso a paso.

Control de flujo

El control de flujo para el método \texttt{invierte\_numero} se basa en la división sucesiva entre 10. El control de flujo busca repetir el conjunto de instrucciones, tantas veces como cifras existan en el número. Por tal motivo, se utiliza el mismo parámetro \texttt{numero} como variable de control de flujo. No es necesario realizar ninguna inicialización puesto que el parámetro ya viene con valor asignado.

La condición de la iteración es \texttt{numero>0}. Lo que significa que se repetirá la iteración mientras el número sea mayor que cero. Por lo que se asume que no hay más cifras para sacar del número cuando \texttt{numero} tome el valor de cero.

¿Cómo actualizamos la variable de control? Pues, como es habitual, lo hacemos al final del ciclo iterativo. A \texttt{numero} le debemos quitar una crifra. La forma más sencillo de hacerlo es dividiéndolo entre 10. Para la división entre 10 hemos usado el operador de asignación \texttt{/=}. Recuerde que \texttt{numero/=10} equivale a \texttt{numero=numero/10}. No se olvide que en Java, cuando se operan dos valores enteros con \texttt{/}, se ejecuta una división entera.

    private static int invierte_numero(int numero) {
        while (numero > 0) {

            numero /= 10;
        }
        return numero_invertido;
    } 

Formando número invertido

Lo que sigue es formar el número invertido. Para eso utilizamos la variable \texttt{numero\_invertido}. En esta variable iremos formando el valor que retornaremos en este módulo.

Básicamente lo que se hace en cada iteración es:

  1. Obtener el dígito del número. Para obtener el dígito del número, el dígito que está más a la derecha, utilizamos el resto o módulo de la división entera entre 10. Como en cada iteración al número se le divide entre 10, en cada iteración se obtendrá una cifra diferente. Por ejemplo si \texttt{numero} tuviera el valor de 123, al operar \texttt{123 \% 10} obtenemos como resultado \texttt{3}, que justamente es la cifra que está más a la derecha. Para la siguiente iteración, \texttt{numero} tendrá el valor de 12, al operar \texttt{12 \% 10} obtenemos como resultado \texttt{2}, la cifra que está más a la derecha ahora. De esta manera vamos obteniendo todos las cifras del número. 
  2. Formar el número invertido. Una vez obtenida la cifra, vamos formando el número invertido. ¿Cómo hacemos esto? Multiplicando por 10 y sumándole el dígito obtenido. Al multiplicar por 10 estamos incrementando una potencia de 10 al número, lo que hace que todas las cifras se muevan a la izquierda, dando espacio para sumarle el dígito obtenido. Al inicio \texttt{numero\_invertido} se le inicializa con el valor de \texttt{0}. Si el número a procesar fuese el número \texttt{123}, la primera cifra a sacar sería \texttt{3}. \texttt{numero\_invertido} se multiplica por \texttt{10} y se le suma \texttt{3}. Al final queda con el valor de \texttt{3}. La siguiente cifra a sacar es \texttt{2}. \texttt{numero\_invertido} se multiplica por \texttt{10} lo que resulta en el valor de \texttt{30}, a este resultado le sumamos \texttt{2} quedando \texttt{numero\_invertido} con el valor de \texttt{32}. La última cifra a sacar es es \texttt{1}. \texttt{numero\_invertido} se multiplica por \texttt{10} lo que resulta en el valor de \texttt{320}, a este resultado le sumamos \texttt{1} quedando \texttt{numero\_invertido} con el valor de \texttt{321}. De esta manera formamos el número invertido.
    private static int invierte_numero(int numero) {
        int numero_invertido = 0;
        while (numero > 0) {
            int digito = numero % 10;
            numero_invertido *= 10;
            numero_invertido += digito;
            numero /= 10;
        }
        return numero_invertido;
    } 

Método palíndromo: versión alternativa

Veremos ahora una versión alternativa. ¿Cómo podremos verificar si el número es palíndromo sin invertir las cifras del número? Pues comparando pares de cifra. En esta versión lo que haremos será sacar la cifra que está más a la izquierda, luego sacar la cifra que está más a la derecha y compararlas, si son iguales, continuaremos comparando los dos siguientes pares de cifras. Para que el número sea capicúa, todos los pares de cifras deben ser todos iguales.

Control de flujo

Al igual que en el caso anterior, la solución es iterativa. Como compararemos pares de cifras, entonces la cantidad de iteraciones que realizaremos se reduce a la mitad que en el caso anterior. Para calcular la cantidad de dígitos usamos el método \texttt{cuenta\_digitos}. No nos preocupamos por como implementar este método, luego lo analizaremos.

Para el control de flujo, usaremos como variable de control a \texttt{i} la cual la inicializamos con el valor de 1. La condición de iteración es \texttt{i<=mitad}, siendo \texttt{mitad} la mitad de dígitos que contiene el número. En el caso de la variable \texttt{mitad}, al momento de hacer la división, hacemos una división entera. Al final de la iteración, actualizamos la variable de control incrementándola en 1.

    private static boolean es_palindromo(int numero) {
        int total_digitos = cuenta_digitos(numero);
        int mitad = total_digitos / 2;
        int i;
        for (i = 1; i <= mitad; i++) {

        }
        return true;
    } 

Gestionando el factor

Ya vimos en la versión anterior que para sacar el dígito que está más a la derecha, basta con dividir sucesivamente entre 10. Pero, ¿cómo hacemos para sacar el dígito que está más a la izquierda? Para esto se requerirá de un factor. Este factor permitirá obtener el dígito que está más a la izquierda. Usaremos la variable \texttt{factor} y la inicializamos con 10 elevado a la cantidad de dígitos del número menos 1.

Por ejemplo si el número en cuestión fuera  \texttt{123}, la cantidad de dígitos es 3, por lo que el factor será 10^2, es decir 100. De esta manera es muy simple obtener el dígito que está más a la izquierda, basta dividir el número entre el factor. El cociente de la división entera, será el dígito que está más a la izquierda. 123 dividido entre 100 retorna 1, justamente el dígito buscado.

La gestión del factor supone su inicialización así como su actualización para la extracción del siguiente dígito. Ya vimos su inicialización, pero, ¿cómo lo actualizamos? Dado que en cada iteración se retiran dos pares de dígitos, el factor se debe dividir entre 100.

Por su lado, el \texttt{numero} también debe ser actualizado. Esta actualización posee dos fases, primero lo actualizamos con el resto del número entre factor, con esto eliminamos el dígito más a la izquierda. Luego, hacemos una división entera entre 10. Con esto eliminamos el dígito que está más a la derecha.

    private static boolean es_palindromo(int numero) {
        int total_digitos = cuenta_digitos(numero);
        int mitad = total_digitos / 2;
        int i;
        int factor = (int) pow(10, total_digitos - 1);
        for (i = 1; i <= mitad; i++) {

            numero %= factor;
            numero /= 10;
            factor /= 100;
        }
        return true;
    } 

Comparación de dígitos

Ya se ha realizado lo más complicado de esta versión: el control de flujo y la gestión del factor. Ahora procederemos a comparar los pares de dígitos. Este paso es relativamente simple, el dígito de la derecha se obtiene por medio del resto del número entre 10. El dígito que está más a la izquierda se obtiene del cociente entero de la división del número entre 10.

Con los dos dígitos obtenidos, el siguiente paso es compararlos. Si los dígitos sin diferentes, salimos de la función retornando el valor de false. Al acabar el ciclo iterativo, si es que no se ha salido de la función, significa que todos los pares de dígitos comparados son iguales, por lo tanto debe retornarse el valor de \texttt{true}.

    private static boolean es_palindromo(int numero) {
        int total_digitos = cuenta_digitos(numero);
        int mitad = total_digitos / 2;
        int i;
        int factor = (int) pow(10, total_digitos - 1);
        for (i = 1; i <= mitad; i++) {
            int digito_derecha = numero % 10;
            int digito_izquierda = numero / factor;
            if (digito_derecha != digito_izquierda) {
                return false;
            }
            numero %= factor;
            numero /= 10;
            factor /= 100;
        }
        return true;
    } 

Método cuenta dígitos

Para encontrar la cantidad de dígitos usamos el método \texttt{cuenta\_digitos}. En el artículo «Cantidad de dígitos de un número en Java» explicamos al detalles varias alternativas para contar la cantidad de dígitos de un número, explicando cada una de ellas como es de costumbre. En esta oportunidad, hemos usado una de las versiones, empleando logaritmos.

    private static int cuenta_digitos(int numero) {
        if (numero == 0) {
            return 1;
        }
        return (int) (log10(numero) + 1);
    } 

Versión recursiva

Finalmente procederemos a implementar una versión recursiva la cual se basa en la versión anterior.

Método palíndromo

La versión recursiva la implementamos mediante dos métodos, uno que realiza la llamada recursiva y el otro es la recursión en si. El módulo \texttt{es\_palindromo} básicamente lo que hace es inicializar la variable \texttt{factor} pues la utilizamos al momento de realizar la invocación a la recursión.

    private static boolean es_palindromo(int numero) {
        int total_digitos = cuenta_digitos(numero);
        int factor = (int) pow(10, total_digitos - 1);
        return es_palindromo_recursivo(numero, factor);
    } 

Método recursivo

La versión recursiva es muy similar a la versión anterior, pero con algunas claras diferencias:

  1.  El caso base se da cuando el número pasado como parámetro tiene un solo dígito. En este caso se retorna \texttt{true}.
  2. Si los pares de dígitos son diferentes, se retorna \texttt{false}.
  3. Si los pares de dígitos son iguales, se actualiza el \texttt{numero} y el  \texttt{factor} y se invoca recursivamente al método.
    private static boolean es_palindromo_recursivo(int numero, int factor) {
        if (numero >= 0 && numero <= 9) {
            return true;
        }

        int digito_derecha = numero % 10;
        int digito_izquierda = numero / factor;
        if (digito_derecha != digito_izquierda) {
            return false;
        }

        numero %= factor;
        numero /= 10;
        factor /= 100;
        return es_palindromo_recursivo(numero, factor);
    } 

Conclusión

Hemos presentado en este artículo, 3 propuestas de solución al problema de verificación si un número es palíndromo o capicúa en Java. Se ha utilizado para el diseño algorítmico la técnica del diseño descendente y se ha controlado  el flujo en los módulos usando estructuras selectivas e iterativas. Podrá descargar la solución propuesta en el repositorio GitHub de iterando++ a través del siguiente enlace

Hemos preparado otros artículos adicionales en donde describimos al detalle la implementación de este problema en PSeInt y otros  lenguajes de programación. Te invitamos a leer los siguientes artículos de iterando++

Si te interesa profundizar en Java, el mejor libro que hay es Effective Java de Joshua Bloch. Uno de los libros favoritos de los que se inician en Java es Java: Learn Java in One Day and Learn It Well de  Jamie Chan.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *