编写一个从整数到罗马的转换器是一个标准的面试问题。我曾经写过以下双向实现(toString-- 十进制转罗马;parse -- 罗马转十进制)。该实现满足了一些关于罗马数字表示的附加标准,这些标准不是强制性的,但通常遵循:
'''
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