Número palíndromo en C

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 ANSI C. 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 algunas funciones 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 la función principal.

Función Principal

El objetivo de la función principal es realizar la lectura del dato de entrada, en este caso el número \texttt{n}. Con el dato leído se invoca a la función \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 la función \texttt{abs} para asegurar que no se invocará a la función con número negativos. Recuerde que para poder usar la función \texttt{abs}, se debe incluir el archivo de cabecera \texttt{stdlib.h}.

#include <stdio.h>
#include <stdlib.h>

int es_palindromo(int numero);
int cuenta_digitos(int numero);

int main() {
    int numero;
    printf("Ingrese un número: ");
    scanf("%d", &numero);

    numero = abs(numero);
    if (es_palindromo(numero))
        printf("El número es palíndromo");
    else
        printf("El número no es palíndromo");
    return 0;
} 

Función verifica palíndromo

La función \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. La función retorna \texttt{1} en caso el número sea palíndromo y \texttt{0} 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 la función \texttt{invierte\_numero}. Como estamos aplicando la técnica de diseño descendente, en esta función no nos preocupamos por la implementación de \texttt{invierte\_numero}. Asumimos que esta función existe y luego la implementaremos. De esta forma estamos encapsulando la complejidad del problema.

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

Función número invertido

En esta parte, nos dedicaremos a implementar la función \texttt{numero\_invertido}. Esta función 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. La función 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 esta función, 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 la función \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 ANSI C, cuando se operan dos valores enteros con \texttt{/}, se ejecuta una división entera.

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 esta función.

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.
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;
} 

Función 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 la función \texttt{cuenta\_digitos}. No nos preocupamos por como implementar esta función, luego la 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.

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

    }
    return 1;
} 

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.

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

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

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 0. 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{1}.

int es_palindromo(int numero) {
    int total_digitos = cuenta_digitos(numero);
    int mitad = total_digitos / 2;
    int i;
    int factor = 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 0;
        numero %= factor;        
        numero /= 10;        
        factor /= 100;        
    }
    return 1;
} 

Función cuenta dígitos

Para encontrar la cantidad de dígitos usamos la función \texttt{cuenta\_digitos}. En el artículo «Cantidad de dígitos de un número en C» 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.

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

Versión recursiva

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

Función palíndromo

La versión recursiva la implementamos mediante dos funciones, una que realiza la llamada recursiva y la otra es la recursión en si. La función \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.

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

Función recursiva

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{1}.
  2. Si los pares de dígitos son diferentes, se retorna \texttt{0}.
  3. Si los pares de dígitos son iguales, se actualiza el \texttt{numero} y el  \texttt{factor} y se invoca recursivamente a la función.
int es_palindromo_recursivo(int numero, int factor) {
    if (numero >= 0 && numero <= 9)
        return 1;

    int digito_derecha = numero % 10;
    int digito_izquierda = numero / factor;
    if (digito_derecha != digito_izquierda)
        return 0;
    
    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 ANSI C. 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 quieres profundizar en el lenguaje ANSI C, no hay mejor libro que C Programming Language de Brian Kernighan  y Dennis Ritchie. Es un libro clásico escrito por el creador del lenguaje C. Uno de los libros favoritos de los que inician en ANSI C es  C Programming Absolute Beginner’s Guide de Greg Perry y Dean Miller.

Deja una respuesta

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