Roman Numerals¶
First, we have to read in all the Roman numerals we're working with.
In [1]:
with open("txt/0089_roman.txt") as f:
numerals = [s.strip() for s in f.readlines()]
To assist us, we'll make a map from denominations to Roman numerals.
In [2]:
denominations = {
1: "I",
2: "II",
3: "III",
4: "IV",
5: "V",
6: "VI",
7: "VII",
8: "VIII",
9: "IX",
10: "X",
20: "XX",
30: "XXX",
40: "XL",
50: "L",
60: "LX",
70: "LXX",
80: "LXXX",
90: "XC",
100: "C",
200: "CC",
300: "CCC",
400: "CD",
500: "D",
600: "DC",
700: "DCC",
800: "DCCC",
900: "CM",
1000: "M",
}
We can make a simple greedy parser to convert the given Roman numerals to ints.
In [3]:
def to_int(s):
rev_denominations = {v: k for (k, v) in denominations.items()}
prefixes = sorted(rev_denominations.keys(), key=len, reverse=True)
n = 0
while s != "":
for prefix in prefixes:
if s.startswith(prefix):
n += rev_denominations[prefix]
s = s.removeprefix(prefix)
break
return n
It's arguably easier to convert an int to the minimal Roman numeral.
In [4]:
def to_roman(n):
s = ["", "", "", ""]
for i in range(1, 4):
d = n % 10^i
if d in denominations:
s[-i] = denominations[d]
n -= d
s[0] = "M" * (n // 1000)
return "".join(s)
To solve, convert each Roman numeral to an int, then convert that int to the minimal Roman numeral, and count the characters saved.
In [5]:
total = 0
for numeral in numerals:
value = to_int(numeral)
minimal = to_roman(value)
total += len(numeral) - len(minimal)
total
Out[5]:
743
Copyright (C) 2025 filifa¶
This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license and the BSD Zero Clause license.