This is now my current code after what user2486 said.
def romanMap():
map=((\"M\", 1000),(\"CM\", 900),(\"D\", 500),(\"CD\", 400),(\"C\", 100),(\"XC\"
There is a very detailed description of the development of the Roman numeral converters in the Dive Into Python 3 by Mark Pilgrim. See the 5.3. Case Study: Roman Numerals that introduces the problem and details.
But that is not all. See the Chapter 9. Unit Testing where the analysis and the implementation of Roman numeral converters continues, including interesting optimization and exception throwing -- the (unit) test driven development.
It is directly related to the enginefree's reference to the code in the first comment below the question (the code was written by Mark Pilgrim).
I know this is an old post, but I would like to add 3 solutions to convert roman numerals to numbers.
Solution 1: (Approx Runtime = 52ms)
def romanToInt(self, s: str) -> int:
roman = {'I':1, 'V':5, 'X':10, 'L':50, 'C':100, 'D':500, 'M':1000 }
num = 0
for i in range(len(s)):
if i!= len(s)-1 and roman[s[i]] < roman[s[i+1]]:
num += roman[s[i]]*-1
else:
num += roman[s[i]]
return num
Solution 2: (Approx Runtime = 60ms)
def romanToInt(self, s: str) -> int:
roman = {'I':1, 'V':5, 'X':10, 'L':50, 'C':100, 'D':500, 'M':1000 }
num = 0
s = s.replace("IV", "IIII").replace("IX", "VIIII")
s = s.replace("XL", "XXXX").replace("XC", "LXXXX")
s = s.replace("CD", "CCCC").replace("CM", "DCCCC")
for x in s:
num += roman[x]
return num
Solution 3: (Approx Runtime = 48ms)
def romanToInt(self, s: str) -> int:
roman = {'I':1, 'V':5, 'X':10, 'L':50, 'C':100, 'D':500, 'M':1000 }
num = 0
for i in range(len(s)-1):
if roman[s[i]] < roman[s[i+1]]:
num += roman[s[i]]*-1
continue
num += roman[s[i]]
num +=roman[s[-1]]
return num
The simplest solution appears to be the best at times :)
Here is my solution:
numerals = [
{'letter': 'M', 'value': 1000},
{'letter': 'D', 'value': 500},
{'letter': 'C', 'value': 100},
{'letter': 'L', 'value': 50},
{'letter': 'X', 'value': 10},
{'letter': 'V', 'value': 5},
{'letter': 'I', 'value': 1},
]
def arabic_to_roman(number):
remainder = number
result = ''
for numeral_index in xrange(len(numerals)):
numeral = numerals[numeral_index]
next_numeral = numerals[numeral_index + 1] if numeral_index + 1 < len(numerals) else None
factor = remainder / numeral['value']
remainder -= factor * numeral['value']
if next_numeral:
numeral_difference = numeral['value'] - next_numeral['value']
if (remainder - numeral_difference >= 0) and (numeral_difference > next_numeral['value']):
result += next_numeral['letter'] + numeral['letter']
remainder -= numeral_difference
if factor > 0:
result += numeral['letter'] * factor
return result
def roman_to_arabic(number):
index_by_letter = {}
for index in xrange(len(numerals)):
index_by_letter[numerals[index]['letter']] = index
result = 0
previous_value = None
for letter in reversed(number):
index = index_by_letter[letter]
value = numerals[index]['value']
if (previous_value is None) or (previous_value <= value):
result += value
else:
result -= value
previous_value = value
return result
A nice compact version with no external libraries:
def rn_to_int(s):
d = {'m': 1000, 'd': 500, 'c': 100, 'l': 50, 'x': 10, 'v': 5, 'i': 1}
n = [d[i] for i in s.lower() if i in d]
return sum([i if i>=n[min(j+1, len(n)-1)] else -i for j,i in enumerate(n)])
for numeral, expected in [['CLXIV', 164], ['MDCCLXXXIII', 1783], ['xiv', 14]]:
assert rn_to_int(numeral) == expected
Work from right to left of the roman numeral to add or subtract values. Easy.
def rome(roman_num):
d = {'I':1,'V':5,'X':10,'L':50,'C':100,'D':500,'M':1000}
nl = list(roman_num)
sum = d[nl[len(nl)-1]]
for i in range(len(nl)-1,0,-1):
if d[nl[i]]>d[nl[i-1]]:
sum -= d[nl[i-1]]
else:
sum += d[nl[i-1]]
return sum