Cambio de base en Java

Más ejercicios resueltos

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

A continuación presentamos 2 alternativas de solución al problema de cambio de base 10 a cualquier base en Java. Si deseas ver el enunciado del problema, haz click en el siguiente enlace

Presentaremos la resolución del problema de cambio de base en dos versiones: una iterativa y otra recursiva. Ambas soluciones han sido construidas usando el paradigma de programación modular y usando la técnica de diseño descendente.

Versión iterativa

Comenzaremos con la versión iterativa del cambio de base. Como ya se mencionó anteriormente, este ejercicio lo resolveremos usando la técnica del diseño descendente. Empezaremos desde lo más general hasta lo más particular. En cada paso asumiremos que ya existen ciertas funciones listas y las usaremos, luego nos preocuparemos de su implementación.

Método principal

Iniciamos con el método principal. Este método básicamente realiza la lectura de datos, la validación de los datos de entrada y finalmente, invoca al método \texttt{imprime\_en\_otra\_base} quien se encarga en realidad de hacer el cambio de base.

En este punto, no nos interesa cómo se implementa \texttt{imprime\_en\_otra\_base}, ese detalle se verá después, lo que importa en este punto es qué hace dicho método. Dicho método se encargará de imprimir el número natural n pero en otra base. El número natural se representa con la variable \texttt{numero} y la base con la variable \texttt{base}.

Este método tiene como precondición que numero sea mayor que 1 y que se cumpla que 2 \leq base \leq 36. Al momento de invocarla, debemos garantizar que esta precondición se cumpla. Esa responsabilidad es del método principal l y no de \texttt{imprime\_en\_otra\_base}. Como estamos usando la técnica de diseño descendente, es importante tener claramente definidas las responsabilidades de cada método.

package cambio_de_base_v_iterativa;

import java.util.Scanner;

public class Cambio_de_Base_v_Iterativa {

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

        if (base >= 2 && base <= 36) {
            imprime_en_otra_base(numero, base);
        } else {
            System.out.printf("Debe ingresar una base entre 2 y 36%n");
        }
    }
} 

Impresión en la otra base

Ahora procedemos a implementar el método \texttt{imprime\_en\_otra\_base}. Este método, tal como su nombre lo indica, imprime un número en otra base. Recibe como parámetro \texttt{numero} que representa el número que queremos imprimir en otra base y \texttt{base} que representa la base en la cual se imprimirá el número. La precondición es: numero>1 y 2 \leq base \leq 36. El método no retorna ningún valor, es de tipo \texttt{void}. A continuación se explicará paso a paso cómo se ha construido este método.

Los algoritmos que comúnmente se encuentran en internet para el cambio de base, se basan en el método de divisiones sucesivas. Si no sabes como funciona el método de divisiones sucesivas, te invitamos a leer el artículo representación de números de números naturales en el computador en donde explicamos al detalle este método.

El problema que ocurre con el método de divisiones sucesivas es que cuando se realiza la primera división del número entre la base se obtiene el último dígito en la nueva base, por lo que no lo podemos imprimir. Si lo hacemos así el número se imprimirá al revés. Algunas soluciones intentan formar el nuevo dígito en una variable entera de forma tal que el nuevo número exprese el número en la nueva base, pero esto falla cuando la base es mayor que 10. Otros optan por usar arreglos o cadenas de caracteres, pero por restricción del enunciado, no podremos usarlas.

Entonces, ¿qué debemos hacer para que en la primera división se obtenga el primer dígito del número transformado? Bueno, la respuesta a esta pregunta se encuentra en las matemáticas. En lugar que la primera división sea entre la base, dividiremos entre una potencia de base. Pero, ¿qué potencia? Pues la mayor potencia de la base pero que sea menor o igual al número que queremos convertir.  En otras palabras realizaremos el método de divisiones sucesivas pero al revés.

En el método que hemos implementado, esta máxima potencia la almacenamos en la variable \texttt{factor}. La inicializamos con el valor que retorna el método \texttt{encuentra\_mayor\_potencia\_menor\_o\_igual\_a\_numero} que justamente tiene como objetivo retornar la mayor potencia de la base pero que sea menor o igual al número pasado como parámetro. La base también la recibe como parámetro.  Como estamos usando la técnica de diseño descendente, no nos preocuparemos en este momento por cómo se implementa este método. Luego lo veremos.

La variable \texttt{factor} es fundamental en este algoritmo por que es la variable que controla el flujo de la iteración. Para la iteración hemos optado por una estructura iterativa con salida controlada dado que como asumimos que \texttt{numero} es mayor que 1, por lo menos deberemos hacer una división. Seguiremos iterando mientras \texttt{factor>0}

¿Cómo obtenemos los dígitos a imprimir? Pues básicamente se hace una división entera entre \texttt{numero}\texttt{factor}. Esta operación retornará el \texttt{digito} que se debe imprimir. Dependiendo de la base, este \texttt{digito} podría ser un número mayor que 10, por lo que mejor encargamos la impresión del dígito al método \texttt{imprime\_digito\_en\_base}. No nos preocuparemos en este momento por cómo se implementa este método. Luego lo veremos.

Luego de haber realizado la división, tanto el \texttt{numero} como la \texttt{el factor} se deben actualizar. En el caso de \texttt{número}, lo actualizamos con el resto de la división entre \texttt{numero}\texttt{factor}. En el caso de \texttt{factor} lo dividimos entre la \texttt{base}.

Recuerde que \texttt{numero\%=factor} equivale a \texttt{numero=numero\%factor}\texttt{factor/=base} equivale a \texttt{factor=factor/base}.

    private static void imprime_en_otra_base(int numero, int base) {
        int factor = encuentra_mayor_potencia_menor_o_igual_a_numero(numero, base);
        do {
            int digito = numero / factor;
            imprime_digito_en_base(digito);
            numero %= factor;
            factor /= base;
        } while (factor > 0);
    }
 

Encontrando la potencia adecuada

Ahora analizaremos la implementación del método \texttt{encuentra\_mayor\_potencia\_menor\_o\_igual\_a\_numero}. Este método, tal como su nombre lo indica, calcula y retorna la mayor potencia de una base pero menor o igual a un determinado número. Recibe como parámetro \texttt{numero} que representa el número que servirá para verificar la mayor potencia y \texttt{base} que representa la base para la cual se hallará la potencia. La precondición es: numero>1 y 2 \leq base \leq 36. La función retorna un valor entero (\texttt{int}). A continuación explicaremos paso a paso cómo se ha construido este método.

El algoritmo es muy simple de entender. En realidad es un productoria. Sigue la misma lógica que el factorial o la descomposición de factores primos. Utilizamos la variable \texttt{factor} que será la variable que retornaremos pero además será la variable que usaremos para el control de flujo. Inicializamos a \texttt{factor} con el valor de 1 y usando un ciclo iterativo con entrada controlada, lo vamos multiplicando por la \texttt{base}. Haremos esto mientras \texttt{factor<=numero}. Dado que estamos usando un ciclo con entrada controlada, cuando finalice la iteración será por que \texttt{factor>numero}. Como hemos realizado una multiplicación de más, antes de retornar el valor, debemos de dividirlo entre la \texttt{base}.

Este factor también se puede calcular con logaritmos. Pero hemos preferido usar, en esta publicación, la versión iterativa para que sea entendida por todos, inclusive aun por los que no tienen conocimientos de logaritmos. Pero si deseas ver  cómo se puede implementar esta función con logaritmos, te invitamos a que visites nuestro artículo Cantidad de dígitos de un número en donde explicamos este tema al detalle.

    private static int encuentra_mayor_potencia_menor_o_igual_a_numero(int numero, int base) {
        int factor = 1;
        while (factor <= numero) {
            factor *= base;
        }
        return factor / base;
    } 

Imprimiendo el dígito en otra base

Finalmente revisaremos la implementación de la función \texttt{imprime\_digito\_en\_base}. Esta función tiene por objetivo imprimir el dígito pasado como parámetro. Recibe como parámetro \texttt{digito} que contiene la magnitud del dígito que deseamos imprimir. La precondición es: 0 \leq digito \leq 35. La función no retorna ningún valor, es de tipo \texttt{void}. A continuación se explicará paso a paso cómo se ha construido esta función.

Esta función es muy simple de entender. Existen dos situaciones. La primera de ellas se da cuando el \texttt{digito} que queremos imprimir es menor que 10. Esta situación se da cuando la base usada para la conversión del número se encuentra en el rango de [2..10]. En este caso, la función imprime el dígito como número.

La otra situación se da cuando el \texttt{digito} que queremos imprimir es mayor o igual a 10. Esta situación es normal cuando la base usada para la conversión del número se encuentra en el rango de [11..36]. Pero, ¿cómo representaremos a los dígitos entonces? Pues con un caracter del alfabeto inglés. Cuando \texttt{digito}  contenga el valor de 10 imprimiremos el caracter \texttt{A}. Cuando \texttt{digito}  contenga el valor de 11 imprimiremos el caracter \texttt{B}. Cuando \texttt{digito}  contenga el valor de 12 imprimiremos el caracter \texttt{C}. Cuando \texttt{digito}  contenga el valor de 13 imprimiremos el caracter \texttt{D}. Y así sucesivamente.

Entonces, ¿cómo hacemos la transformación del dígito en caracter? Pues para esto primero debemos obtener el orden del caracter en el alfabeto. De forma que el orden de la \texttt{A} será 0, el orden de la \texttt{B} será 1, el orden de la \texttt{C} será 2  y así sucesivamente. ¿Cómo obtenemos el orden que le corresponde al dígito que queremos imprimir? Pues basta restarle 10 al dígito, pues el valor de 10 corresponde con el caracter \texttt{A} y es el primero de todos. Una vez que tenemos el orden del caracter que deseamos imprimir, simplemente se lo sumamos al código ASCII de \texttt{A}. De esta forma por ejemplo, si queremos imprimir el caracter cuyo orden es 4, haríamos la suma \texttt{(int)’A’+4}, lo que da el código ASCCI de \texttt{‘E’}.

Recuerde que en Java, el casteo de una constante caracter para entero (\texttt{(int)’A’} por ejemplo) retorna el código ASCII del caracter. Por su lado, el casteo de un valor entero para caracter (\texttt{char(65)} por ejemplo) retorna el caracter del código ASCII.

    private static void imprime_digito_en_base(int digito) {
        if (digito < 10) {
            System.out.printf("%d", digito);
        } else {
            char digito_transformado = (char) (digito - 10 + (int) 'A');
            System.out.printf("%c", digito_transformado);
        }
    } 

Versión recursiva

Procederemos ahora a explicar la versión recursiva de esta solución. También ha sido implementada usando la técnica de diseño descendente pero con un par de alteraciones. Primero el método \texttt{imprime\_en\_otra\_base} lo hemos implementado totalmente recursivo. Y segundo, el método \texttt{encuentra\_mayor\_potencia\_menor\_o\_igual\_a\_numero} no es necesario en esta versión, veamos el porqué.

La versión recursiva la hemos implementado usando el método de divisiones sucesivas. Este método básicamente divide el número en cuestión entre la base a la cual quiere convertir hasta que el cociente de este división se haga cero. Luego de cada división, el resto contendrá el dígito a imprimir y la división sucesiva se realiza con el cociente. Si no sabes como funciona el método de divisiones sucesivas, te invitamos a leer el artículo representación de números de números naturales en el computador en donde explicamos al detalle este método.

Habíamos comentado que no podíamos usar el método de divisiones sucesivas pues la primera división nos retornaba el último dígito a imprimir. Y esto es verdad en la solución iterativa, pero si usamos recursión, al poner la impresión luego de la llamada recursiva, hace que primero se imprima el siguiente el dígito de forma recursiva y luego se imprima el dígito actual. De esta forma, la recursión hace posible que dejemos de lado la función \texttt{encuentra\_mayor\_potencia\_menor\_o\_igual\_a\_numero}.

    private static void imprime_en_otra_base(int numero, int base) {
        if (numero > 0) {
            int digito = numero % base;
            numero /= base;
            imprime_en_otra_base(numero, base);
            imprime_digito_en_base(digito);
        }
    } 

Conclusión

Hemos presentado en este artículo, 2 propuestas de solución al problema de cambio de base 10 a cualquier base usando 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 PSeInt y en 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 *