quinta-feira, 18 de janeiro de 2018

Java: conversão de números decimais em romanos

Neste artigo veremos como converter números decimais em romanos na linguagem de programação Java. Teremos também uma comparação com implementações em outras linguagens como PHP e Python.

O processo de conversão entre números decimais e romanos é bastante simples uma vez que se conhece o algoritmo. O primeiro passo é obter uma tabela com os símbolos romanos e os valores decimais correspondentes:

1000 M
900 CM
500 D
400 CD
100 C
90 XC
50 L
40 XL
10 X
9 IX
5 V
4 IV
1 I

Em seguida, divida o número decimal pelo valor decimal correspondente ao maior símbolo romano e considere apenas a parte inteira do resultado. Por exemplo, se o número decimal original for 3.449:

3449 / 1000 = 3.449 (parte inteira: 3)

Adicione ao número romano resultante a letra romana (M) multiplicada pela parte inteira (3), resultando em "MMM". Multiplique a parte inteira pelo valor decimal do romano (1000) e subtraia do valor original:

3449 - (3 * 1000) = 449

Os próximos números romanos da tabela (CM e D) são maiores do que o valor restante, por isso serão ignorados. Iremos direto para o numeral CD (400):

449 / 400 = 1,1225

Agora, acrescente CM ao número romano resultante: MMMCD. O valor restante é 449 - 400 = 49. Pularemos todos os números restantes que forem maiores que 49, indo para o XL (40):

49 / 40  = 1,225

Ficou fácil, não é? O número romano ganha de presente um XL: MMMCDXL. Agora, o resto é apenas 9, que corresponde exatamente ao romano IX:

9 / 9 = 1

Portanto, o número 3.449 convertido em algarismos romanos é MMMCDXLIX.

Implementação em Java

Como mencionado, a implementação é simples depois que o algoritmo é compreendido. O Java não seria Java se não tivéssemos uma classe, então, iniciaremos com a estrutura básica da classe:

public class NumerosRomanos {
    public static String converterEmRomanos(int decimal) {
        (...)
    }
}

Optamos por um método static para simplificar a invocação. O número romano será retornado como String, o que é suficiente para a maioria dos casos. Acima de tudo, não queremos complicar o nosso código mais do que o necessário.

Seguindo em frente, criaremos a representação da tabela de conversão:

public class NumerosRomanos {

    private static final int[] DECIMAIS =
        {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
    private static final String[] ROMANOS =
        {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};

    (...)
}

Desta vez, criamos dois arrays, um para os números decimais e outro para os romanos correspondentes. Por que não usar um HashMap? Porque, como vamos iterar pelos valores da tabela, um HashMap, por ser desordenado, tornaria as coisas um pouco mais complicadas para nós. Poderíamos ter utilizado um LinkedHashMap, que é ordenado, mas mesmo assim, inicializar um Map em Java não é nada elegante e a nossa dupla de arrays será o bastante.

Agora, vamos finalmente ao método:

    public static String converterEmRomanos(int decimal) {
        StringBuilder resultado = new StringBuilder();
        (...)
        return resultado.toString();
    }

Utilizamos um StringBuilder em vez de uma String por ser mais eficiente, embora neste exemplo a diferença de desempenho não vá ser muito grande.

public static String converterEmRomanos(int decimal) {
        StringBuilder resultado = new StringBuilder();
        for (int i = 0; i < DECIMAIS.length; i++) {
            (...)
        }
        return resultado.toString();
    }

Aqui, iteramos pelos números decimais, mas a variável i será útil também para acessar a lista de romanos.

import static java.util.Collections.nCopies;
import static java.lang.String.join;

(...)
    public static String converterEmRomanos(int decimal) {
        StringBuilder resultado = new StringBuilder();
        for (int i = 0; i < DECIMAIS.length; i++) {
            int parteInteira = decimal / DECIMAIS[i];
            decimal -= DECIMAIS[i] * parteInteira;
            reesultado.append(join("", nCopies(parteInteira, ROMANOS[i])));
        }
        return resultado.toString();
    }
(...)

Com isso, implementamos totalmente o algoritmo apresentado anteriormente. Importamos e utilizamos os métodos estáticos join e nCopies para facilitar o nosso trabalho. Segue o código completo:

import static java.util.Collections.nCopies;
import static java.lang.String.join;

public class NumerosRomanos {

    private static final int[] DECIMAIS =
        {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
    private static final String[] ROMANOS =
        {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};

    public static String converterEmRomanos(int decimal) {
        StringBuilder resultado = new StringBuilder();
        for (int i = 0; i < DECIMAIS.length; i++) {
            int parteInteira = decimal / DECIMAIS[i];
            decimal -= DECIMAIS[i] * parteInteira;
            resultado.append(join("", nCopies(parteInteira, ROMANOS[i])));
        }
        return resultado.toString();
    }
}

Implementação em PHP

Uma implementação em PHP semelhante à feita em Java segue abaixo. Como arrays em PHP são ordenados e têm uma inicialização muito mais elegante, podemos utilizá-los para representar nossa tabela de conversão satisfatoriamente.

<?php

const ROMANOS = [
    1000 => "M",
    900  => "CM",
    500  => "D",
    400  => "CD",
    100  => "C",
    90   => "XC",
    50   => "L",
    40   => "XL",
    10   => "X",
    9    => "IX",
    5    => "V",
    4    => "IV",
    1    => "I"
];

function converterEmRomanos($decimal) {
    $resultado = '';
    foreach (ROMANOS as $valor => $romano) {
        $div = intdiv($decimal, $valor);
        $resultado .= str_repeat($romano, $div);
        $decimal -= $div * $valor;
    }
    return $resultado;
}

Implementação em Python

A implementação em Python segue a implementação em PHP. A diferença é que em Python os dicionários não são ordenados, obrigando-nos a utilizar uma lista (uma tupla) de pares. Um detalhe interessante é que, com a função divmod, é possível obter a parte inteira da divisão e o resto ao mesmo tempo:

romanos = (
    (1000, "M"),
    (900, "CM"),
    (500, "D"),
    (400, "CD"),
    (100, "C"),
    (90, "XC"),
    (50, "L"),
    (40, "XL"),
    (10, "X"),
    (9, "IX"),
    (5, "V"),
    (4, "IV"),
    (1, "I")
)

def converterEmRomanos(decimal):
    resultado = ''
    for val, rom in romanos:
        div, decimal = divmod(decimal, val)
        resultado += rom * div
    return resultado

Conclusão

Como vimos, o algoritmo para converter decimais em romanos é bastante fácil de implementar. As implementações em Python e PHP podem não ter sido as mais idiomáticas, mas demonstram algumas diferenças em relação a Java. Por serem linguagens em que os tipos não são obrigatórios, observa-se que o código fica um pouco mais conciso.

Nenhum comentário:
Postar um comentário

Sua opinião é bem-vinda!