← Назад к вопросам
Преобразование числа в римское
2.0 Middle🔥 201 комментариев
#Теория тестирования#Фреймворки тестирования
Условие
Напишите функцию, которая преобразует целое число в римскую запись.
Пример
Вход: 4 Выход: IV
Вход: 58 Выход: LVIII
Вход: 1994 Выход: MCMXCIV
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Решение
Понимание задачи
Преобразовать целое число (1-3999) в римскую запись. Используется принцип вычитания: V (5) перед X (10) означает 4, а не 15.
Оптимальный алгоритм: Greedy с таблицей значений
def int_to_roman(num):
"""
Преобразует число в римскую запись.
"""
# Таблица (значение, символ) отсортирована по убыванию
values = [
(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')
]
result = ''
for value, symbol in values:
count = num // value
if count:
result += symbol * count
num -= value * count
return result
Как это работает
Пример: 1994
Шаг 1: 1994 / 1000 = 1 -> 'M', остаток 994 Шаг 2: 994 / 900 = 1 -> 'CM', остаток 94 Шаг 3: 94 / 90 = 1 -> 'XC', остаток 4 Шаг 4: 4 / 4 = 1 -> 'IV', остаток 0
Результат: 'MCMXCIV'
Сложность
- Временная: O(1) — максимум 13 значений в таблице
- Пространственная: O(1) — результат максимум 4 символа на digit
Расширенная версия с комментариями
def int_to_roman_detailed(num):
"""
Преобразует число в римскую запись.
Args:
num: Число от 1 до 3999
Returns:
Строка с римской записью
Raises:
ValueError: Если число вне диапазона
"""
if not isinstance(num, int) or num <= 0 or num >= 4000:
raise ValueError("Число должно быть от 1 до 3999")
# Таблица с поддержкой субтрактивной нотации
mapping = [
(1000, 'M'), # M = 1000
(900, 'CM'), # CM = 900 (1000 - 100)
(500, 'D'), # D = 500
(400, 'CD'), # CD = 400 (500 - 100)
(100, 'C'), # C = 100
(90, 'XC'), # XC = 90 (100 - 10)
(50, 'L'), # L = 50
(40, 'XL'), # XL = 40 (50 - 10)
(10, 'X'), # X = 10
(9, 'IX'), # IX = 9 (10 - 1)
(5, 'V'), # V = 5
(4, 'IV'), # IV = 4 (5 - 1)
(1, 'I') # I = 1
]
roman = ''
for value, numeral in mapping:
count = num // value
roman += numeral * count
num -= value * count
return roman
Тестирование
# Базовые случаи
assert int_to_roman(1) == 'I'
assert int_to_roman(3) == 'III'
assert int_to_roman(4) == 'IV'
assert int_to_roman(5) == 'V'
assert int_to_roman(9) == 'IX'
assert int_to_roman(27) == 'XXVII'
assert int_to_roman(58) == 'LVIII'
assert int_to_roman(1994) == 'MCMXCIV'
assert int_to_roman(3999) == 'MMMCMXCIX'
# Граничные случаи
assert int_to_roman(1000) == 'M'
assert int_to_roman(400) == 'CD'
assert int_to_roman(40) == 'XL'
assert int_to_roman(4) == 'IV'
Альтернативные подходы
Способ 1: Перевёрнутое преобразование (неоптимально)
def roman_to_int(s):
roman_values = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000}
total = 0
prev_value = 0
for char in reversed(s):
value = roman_values[char]
if value < prev_value:
total -= value
else:
total += value
prev_value = value
return total
- Преобразование из римского в десятичное
- Не решает исходную задачу
Способ 2: С использованием divmod (более elegantly)
def int_to_roman_divmod(num):
mapping = [(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')]
roman = ''
for value, numeral in mapping:
count, num = divmod(num, value)
roman += numeral * count
return roman
- Использует divmod вместо целочисленного деления
- Более Pythonic
Граничные случаи и проверка
# Числа с повторениями
assert int_to_roman(3) == 'III' # Максимум 3 одинаковых
assert int_to_roman(30) == 'XXX'
assert int_to_roman(300) == 'CCC'
assert int_to_roman(3000) == 'MMM'
# Субтрактивная нотация
assert int_to_roman(4) == 'IV' # Не 'IIII'
assert int_to_roman(9) == 'IX' # Не 'VIIII'
assert int_to_roman(40) == 'XL' # Не 'XXXX'
assert int_to_roman(90) == 'XC' # Не 'LXXXX'
assert int_to_roman(400) == 'CD' # Не 'CCCC'
assert int_to_roman(900) == 'CM' # Не 'DCCCC'
# Комплексные числа
assert int_to_roman(2023) == 'MMXXIII'
assert int_to_roman(1949) == 'MCMXLIX'
Выводы
- Greedy алгоритм с таблицей — оптимальное решение
- O(1) время — количество операций ограничено
- Таблица включает субтрактивные пары — 4, 9, 40, 90, 400, 900
- Для QA: проверить граничные числа, повторения, субтрактивную нотацию
- Валидные диапазоны: 1-3999 (древний Рим не имел нуля)