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