Convertendo entre algarismos romanos e árabes em Java

Convertendo entre algarismos romanos e árabes em Java

1. Introdução

Os romanos antigos desenvolveram seu próprio sistema numérico chamado numeração romana. O sistema usa letras com valores diferentes para representar números. Os algarismos romanos ainda são usados ​​hoje em algumas aplicações menores.

Neste tutorial, implementaremossimple converters that will transform numbers from one system to the other.

2. Números romanos

No sistema romano, temos7 symbols that represent numbers:

  • I representa 1

  • V representa 5

  • X representa 10

  • L representa 50

  • C representa 100

  • D representa 500

  • M representa 1000

Originalmente, as pessoas costumavam representar um 4 com IIII ou 40 com XXXX. Isso pode ser bastante desconfortável de ler. Também é fácil confundir quatro símbolos lado a lado com três símbolos.

Roman numerals use subtractive notation para evitar tais erros. Em vez de dizerfour times one (IIII), pode-se dizer que éone less than five (IV).

Como isso é importante do nosso ponto de vista? É importante porque, em vez de simplesmente adicionar números, símbolo por símbolo, podemos precisar verificar o próximo símbolo para determinar se o número deve ser adicionado ou subtraído.

3. Modelo

Vamos definir um enum para representar os algarismos romanos:

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

    private int value;

    RomanNumeral(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    public static List getReverseSortedValues() {
        return Arrays.stream(values())
          .sorted(Comparator.comparing((RomanNumeral e) -> e.value).reversed())
          .collect(Collectors.toList());
    }
}

Observe que definimos símbolos adicionais para ajudar na notação subtrativa. Também definimos um método adicional denominadogetReverseSortedValues().

Este método nos permitirá recuperar explicitamente os números romanos definidos em ordem decrescente.

4. Romano para Árabe

Roman numerals can only represent integers between 1 to 4000. Podemos usar o seguinte algoritmo para converter um algarismo romano em um número arábico (iterando por meio de símbolos na ordem inversa deM aI):

LET numeral be the input String representing an Roman Numeral
LET symbol be initialy set to RomanNumeral.values()[0]
WHILE numeral.length > 0:
    IF numeral starts with symbol's name:
        add symbol's value to the result
        remove the symbol's name from the numeral's beginning
    ELSE:
        set symbol to the next symbol

4.1. Implementação

Em seguida, podemos implementar o algoritmo em Java:

public static int romanToArabic(String input) {
    String romanNumeral = input.toUpperCase();
    int result = 0;

    List romanNumerals = RomanNumeral.getReverseSortedValues();

    int i = 0;

    while ((romanNumeral.length() > 0) && (i < romanNumerals.size())) {
        RomanNumeral symbol = romanNumerals.get(i);
        if (romanNumeral.startsWith(symbol.name())) {
            result += symbol.getValue();
            romanNumeral = romanNumeral.substring(symbol.name().length());
        } else {
            i++;
        }
    }

    if (romanNumeral.length() > 0) {
        throw new IllegalArgumentException(input + " cannot be converted to a Roman Numeral");
    }

    return result;
}

4.2. Test

Por fim, podemos testar a implementação:

@Test
public void given2018Roman_WhenConvertingToArabic_ThenReturn2018() {
    String roman2018 = "MMXVIII";

    int result = RomanArabicConverter.romanToArabic(roman2018);

    assertThat(result).isEqualTo(2018);
}

5. Árabe para romano

Podemos usar o seguinte algoritmo para converter algarismos arábicos em romanos (iterando por meio de símbolos na ordem inversa deM paraI):

LET number be an integer between 1 and 4000
LET symbol be RomanNumeral.values()[0]
LET result be an empty String
WHILE number > 0:
    IF symbol's value <= number:
        append the result with the symbol's name
        subtract symbol's value from number
    ELSE:
        pick the next symbol

5.1. Implementação

Em seguida, agora podemos implementar o algoritmo:

public static String arabicToRoman(int number) {
    if ((number <= 0) || (number > 4000)) {
        throw new IllegalArgumentException(number + " is not in range (0,4000]");
    }

    List romanNumerals = RomanNumeral.getReverseSortedValues();

    int i = 0;
    StringBuilder sb = new StringBuilder();

    while ((number > 0) && (i < romanNumerals.size())) {
        RomanNumeral currentSymbol = romanNumerals.get(i);
        if (currentSymbol.getValue() <= number) {
            sb.append(currentSymbol.name());
            number -= currentSymbol.getValue();
        } else {
            i++;
        }
    }

    return sb.toString();
}

5.2. Test

Por fim, podemos testar a implementação:

@Test
public void given1999Arabic_WhenConvertingToRoman_ThenReturnMCMXCIX() {
    int arabic1999 = 1999;

    String result = RomanArabicConverter.arabicToRoman(arabic1999);

    assertThat(result).isEqualTo("MCMXCIX");
}

6. Conclusão

Neste artigo rápido, mostramos como converter entre algarismos romanos e arábicos.

Usamos umenum para representar o conjunto de algarismos romanos e criamos uma classe de utilitário para realizar as conversões.

A implementação completa e todos os testes podem ser encontradosover on GitHub.