1. Введение
Древние римляне разработали свою собственную числовую систему, называемую римскими цифрами. Система использует буквы с различными значениями для представления чисел. Римские цифры до сих пор используются в некоторых незначительных приложениях.
В этом учебнике мы будем реализовывать простые преобразователи, которые преобразуют числа из одной системы в другую.
2. Римские цифры
В римской системе у нас есть 7 символов, представляющих :
- Я представляет 1
- V представляет 5
- X представляет 10
- L представляет 50
- C представляет 100
- D представляет 500
- М представляет 1000
Первоначально, люди использовали для представления 4 с IIII или 40 с XXXX. Это может быть довольно неудобно читать. Также легко принять четыре символа рядом друг с другом за три символа.
Римские цифры используют вычитанную нотацию чтобы избежать таких ошибок. Вместо того, чтобы четыре раза по одному (IIII), можно сказать, что это один менее пяти (IV).
Как это важно с нашей точки зрения? Это важно, потому что вместо того, чтобы просто добавлять символ чисел по символу, нам может понадобиться проверить следующий символ, чтобы определить, следует ли добавлять или вычитать это число.
3. Модель
Давайте определим enum для представления римских цифр:
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 ListgetReverseSortedValues() { return Arrays.stream(values()) .sorted(Comparator.comparing((RomanNumeral e) -> e.value).reversed()) .collect(Collectors.toList()); } }
Обратите внимание, что мы определили дополнительные символы, чтобы помочь с вычитательной нотацией. Мы также определили дополнительный метод, названный getReverseSortedValues () .
Этот метод позволит нам явно получить определенные римские цифры в порядке нисходящего значения.
4. От римского до арабского
Римские цифры могут представлять только интеграторы от 1 до 4000 . Мы можем использовать следующий алгоритм для преобразования римской цифры в арабское число (итерируя через символы в обратном порядке М Я ):
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. Осуществление
Далее мы можем реализовать алгоритм в Java:
public static int romanToArabic(String input) { String romanNumeral = input.toUpperCase(); int result = 0; ListromanNumerals = 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 public void given2018Roman_WhenConvertingToArabic_ThenReturn2018() { String roman2018 = "MMXVIII"; int result = RomanArabicConverter.romanToArabic(roman2018); assertThat(result).isEqualTo(2018); }
5. От арабского до римского
Мы можем использовать следующий алгоритм для преобразования с арабского на римские цифры (итерируя через символы в обратном порядке М Я ):
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. Осуществление
Далее мы можем реализовать алгоритм:
public static String arabicToRoman(int number) { if ((number <= 0) || (number > 4000)) { throw new IllegalArgumentException(number + " is not in range (0,4000]"); } ListromanNumerals = 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 public void given1999Arabic_WhenConvertingToRoman_ThenReturnMCMXCIX() { int arabic1999 = 1999; String result = RomanArabicConverter.arabicToRoman(arabic1999); assertThat(result).isEqualTo("MCMXCIX"); }
6. Заключение
В этой быстрой статье мы показали, как конвертировать между римскими и арабскими цифрами.
Мы использовали enum представлять набор римских цифр, и мы создали класс полезности для выполнения конверсий.
Полную реализацию и все тесты можно найти более на GitHub .