This is now my current code after what user2486 said.
def romanMap():
map=((\"M\", 1000),(\"CM\", 900),(\"D\", 500),(\"CD\", 400),(\"C\", 100),(\"XC\"
Right-to-left solution that is a bit more Pythonic (no indexes) and relatively short.
Algorithm:
Example:
'xiv'
=> sum(5, -1, 10)
=> 14
def parse_roman(s):
numerals = {'M':1000, 'D':500, 'C':100, 'L':50, 'X':10, 'V':5, 'I':1}
n = 0
last_value = 0
# e.g. convert 'xiv' to (5, 1, 10)
for value in (numerals[c] for c in reversed(s.upper())):
# debugging
v = (value, -value)[value < last_value]
print('{:6} += {:5} <== cur, prev = {}, {}'.format(n, v, value, last_value))
# subtract smaller values that come after larger ones, otherwise add
n += (value, -value)[value < last_value]
last_value = value
return n
Output:
parse_roman('MMCMXCVIII')
0 += 1 <== cur, prev = 1, 0
1 += 1 <== cur, prev = 1, 1
2 += 1 <== cur, prev = 1, 1
3 += 5 <== cur, prev = 5, 1
8 += 100 <== cur, prev = 100, 5
108 += -10 <== cur, prev = 10, 100
98 += 1000 <== cur, prev = 1000, 10
1098 += -100 <== cur, prev = 100, 1000
998 += 1000 <== cur, prev = 1000, 100
1998 += 1000 <== cur, prev = 1000, 1000
2998
Note: It would be nice to find a (short, inline) method for changing the signs of the sequence on the fly. For example, (5, 1, 10)
==> (5, -1, 10)
.
Update: This is as close as I got before giving up. It's identical to the code above, but it uses itertools.tee()
with zip()
to generate pairs of the previous and current values to eliminate the need for the state variables. The single call to next(cur)
makes that list one shorter than prev
which is all the state we need to figure out whether to add or subtract the current value.
Example:
cur, prev = (5, 1, 10), (5, 1, 10)
# Take one from cur and zip the rest
next(cur) + sum(... zip(cur, prev))
# 5 + ... zip( (1, 10), (5, 1, 10) ) ==> 5 + ... ((1, 5), (10, 1))
Code:
from itertools import tee
def parse_roman(s):
numerals = {'M':1000, 'D':500, 'C':100, 'L':50, 'X':10, 'V':5, 'I':1}
cur, prev = tee(numerals[c] for c in reversed(s.upper()))
return next(cur) + sum((cur, -cur)[cur < prev] for cur, prev in zip(cur,prev))
roman_conver=[ (1,'I'),
(5,'V'),
(10,'X'),
(50,'L'),
(100,'C'),
(500,'D'),
(1000,'M'),
]
def romantonumeral(roman):
tot = 0
for i in range(0,len(roman)):
for each in roman_conver:
if roman[i]==each[1]:
if each[0]>tot:
tot = each[0] - tot
else:
tot = tot + each[0]
return tot
Consider this additional pseudo-code and hints (some of it is valid Python, some isn't, but there be notes).
def numberOfNumeral(n):
""" Return the number represented by the single numeral """
# e.g. "v" -> 5, "i" -> 5 (and handle v/V cases, etc.)
# avoid "string" as a variable name
# I chose "ns" for "numerals" (which might be better),
# but I'm also a bit terse .. anyway, name variables for what they represents.
ns = str(input("Enter a roman numeral"))
while ns:
firstNum = numberOfNumeral(ns[0])
# This makes secondValue = -1 when there is only one numeral left
# so firstNum is always "at least" secondNum when len(ns) == 1.
secondNum = numberOfNumeral(ns[1]) if len(ns) > 1 else -1
if firstNum is at least secondNum:
# Add firstNum to total.
# Remove the character - so that the loop state advances.
# If we don't don't his, as in the original, it will never end.
# Here we use "slice notation".
ns = ns[1:]
else:
# Add the difference, secondNum - firstNum, to total.
# Remove both characters - again, so we advance state.
ns = ns[2:]
You can use this code:
def roman_integer(roman):
roman = roman.upper() # for taking care of upper or lower case letters
integer_rep = 0
roman_to_integer_map = tuple()
roman_to_integer_map = (('M',1000),
('CM',900),
('D',500),
('CD',400),
('C',100),
('XC',90),
('L',50),
('XL',40),
('X',10),
('IX',9),
('V',5),
('IV',4),
('I',1))
roman_numeral_pattern = re.compile("""
^ # beginning of string
M{0,4} # thousands - 0 to 4 M's
(CM|CD|D?C{0,3}) # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 C's),
# or 500-800 (D, followed by 0 to 3 C's)
(XC|XL|L?X{0,3}) # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 X's),
# or 50-80 (L, followed by 0 to 3 X's)
(IX|IV|V?I{0,3}) # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 I's),
# or 5-8 (V, followed by 0 to 3 I's)
$ # end of string
""" ,re.VERBOSE)
if not roman_numeral_pattern.search(roman):
return 0
index = 0
for numeral, integer in roman_to_integer_map:
while roman[index:index+len(numeral)] == numeral:
#print numeral, integer, 'matched'
integer_rep += integer
index += len(numeral)
return integer_rep
This is one of the leetcode questions.
def romanToInt(s):
sum=0
dict={'M':1000,'D':500,'C':100,'L':50,'X':10,'V':5,'I':1}
for i in range(len(s)):
if i==0:
sum=sum+dict[s[i]]
else:
if s[i]=='M':
sum=sum+1000
if s[i-1]=='C':
sum=sum-200
elif s[i]=='D':
sum=sum+500
if s[i-1]=='C':
sum=sum-200
elif s[i]=='C':
sum=sum+100
if s[i-1]=='X':
sum=sum-20
elif s[i]=='L':
sum=sum+50
if s[i-1]=='X':
sum=sum-20
elif s[i]=='X':
sum=sum+10
if s[i-1]=='I':
sum=sum-2
elif s[i]=='V':
sum=sum+5
if s[i-1]=='I':
sum=sum-2
elif s[i]=='I':
sum=sum+1
return (sum)
Here's something i came up with using dictionary. It should be v.simple. Tell me what you think. I must say it does not handle the spoof roman numerals written in the form of MIM (instead of MCMXCIX for 1999). This is only for valid roman numerals.
import re
s = 0;
a = dict();
b = dict();
r = "MMCMXCVIII"
a['CM'] = 900;
a['IX'] = 9;
a ['IV'] = 4;
a ['XL'] = 40;
a ['CD'] = 400;
a ['XC'] = 90;
b['M'] = 1000;
b['C'] = 100;
b['D'] = 500;
b['X'] = 10;
b['V'] = 5;
b['L'] = 50;
b['I'] = 1;
# Handle the tricky 4's and 9's first and remove them from the string
for key in a:
if key in r:
r = re.sub(key,'',r)
s+=a[key];
# Then straightforward multiplication of the not-so-tricky ones by their count.
for key in b:
s+= r.count(key) * b[key];
print s; # This will print 2998