Número palíndromo en Python

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 Python. 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.

numero=int(input("Ingrese un número: "))
numero = abs(numero)
if es_palindromo(numero):
    print("El número es palíndromo")
else:
    print("El número no es palíndromo") 

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{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 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.

def es_palindromo(numero):
    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 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} y \texttt{//} es el operador de división entera en Python.

def invierte_numero(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.
def invierte_numero(numero):
    numero_invertido = 0
    while numero > 0:
        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 la instrucción \texttt{for}. Como deseamos iterar en el rango [1..mitad], usamos la función \texttt{range(1, mitad+1)}. En el caso de la variable \texttt{mitad}, al momento de hacer la división, hacemos una división entera. Al

def es_palindromo(numero):
    total_digitos = cuenta_digitos(numero)
    mitad = total_digitos // 2
    for i in range(1, mitad+1):

    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.

def es_palindromo(numero):
    total_digitos = cuenta_digitos(numero)
    mitad = total_digitos // 2
    factor = 10 ** (total_digitos - 1)
    for i in range(1, mitad+1):

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

def es_palindromo(numero):
    total_digitos = cuenta_digitos(numero)
    mitad = total_digitos // 2
    factor = 10 ** (total_digitos - 1)
    for i in range(1, mitad+1):
        digito_derecha = numero % 10
        digito_izquierda = numero // factor        
        if digito_derecha != digito_izquierda:
            return False
        numero %= factor       
        numero //= 10        
        factor //= 100
    return True 

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 Python» 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.

def cuenta_digitos(numero):
    if numero == 0:
        return 1
    return int(math.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.

def es_palindromo(numero):
    total_digitos = cuenta_digitos(numero)
    factor = 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{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 a la función.
def es_palindromo_recursivo(numero, factor):
    if numero >= 0 and numero <= 9:
        return True

    digito_derecha = numero % 10
    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 Python. 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 más en el desarrollo en Python, los dos mejores libros que se han escrito son Learning Python de Mark Lutz y Python Crash Course de Eric Matthes.

Deja una respuesta

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