Writing a converter from ints to Romans is a standard interview question. I once wrote the following bi-directional implementation (toString
-- decimal to Roman; parse
-- Roman to decimal). The implementaion saticifies a number of additional criteria on the representation of Roman numbers, which are not obligatory, but generally followed:
'''
Created on Feb 7, 2013
@author: olegs
'''
ROMAN_CONSTANTS = (
( "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" ),
( "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" ),
( "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM" ),
( "", "M", "MM", "MMM", "", "", "-", "", "", "" ),
)
ROMAN_SYMBOL_MAP = dict(I=1, V=5, X=10, L=50, C=100, D=500, M=1000)
CUTOFF = 4000
BIG_DEC = 2900
BIG_ROMAN = "MMCM"
ROMAN_NOUGHT = "nulla"
def digits(num):
if num < 0:
raise Exception('range error: negative numbers not supported')
if num % 1 != 0.0:
raise Exception('floating point numbers not supported')
res = []
while num > 0:
res.append(num % 10)
num //= 10
return res
def toString(num, emptyZero=False):
if num < CUTOFF:
digitlist = digits(num)
if digitlist:
res = reversed([ ROMAN_CONSTANTS[order][digit] for order, digit in enumerate(digitlist) ])
return "".join(res)
else:
return "" if emptyZero else ROMAN_NOUGHT
else:
if num % 1 != 0.0:
raise Exception('floating point numbers not supported')
# For numbers over or equal the CUTOFF, the remainder of division by 2900
# is represented as above, prepended with the multiples of MMCM (2900 in Roman),
# which guarantees no more than 3 repetitive Ms.
return BIG_ROMAN * (num // BIG_DEC) + toString(num % BIG_DEC, emptyZero=True)
def parse(numeral):
numeral = numeral.upper()
result = 0
if numeral == ROMAN_NOUGHT.upper():
return result
lastVal = 0
lastCount = 0
subtraction = False
for symbol in numeral[::-1]:
value = ROMAN_SYMBOL_MAP.get(symbol)
if not value:
raise Exception('incorrect symbol')
if lastVal == 0:
lastCount = 1
lastVal = value
elif lastVal == value:
lastCount += 1
# exceptions
else:
result += (-1 if subtraction else 1) * lastVal * lastCount
subtraction = lastVal > value
lastCount = 1
lastVal = value
return result + (-1 if subtraction else 1) * lastVal * lastCount