Roman Numerals (Python)

Updated vegaseat 0 Tallied Votes 3K Views Share

Let Python do the work for you and figure out the roman numerals for a given integer. I have to ask you to keep the integer values somewhat reasonably low (from 1 to 4999), since roman numerals from 5000 on use characters with an overline and most PCs don't have those in the character set.

# convert an integer to a roman numeral
# keep it reasonable since from 5000 on special characters are used
# see also: http://en.wikipedia.org/wiki/Roman_numerals
# tested with Python24       vegaseat        25jan2007

def int2roman(number):
    numerals = { 1 : "I", 4 : "IV", 5 : "V", 9 : "IX", 10 : "X", 40 : "XL", 
        50 : "L", 90 : "XC", 100 : "C", 400 : "CD", 500 : "D", 900 : "CM", 1000 : "M" }
    result = ""
    for value, numeral in sorted(numerals.items(), reverse=True):
        while number >= value:
            result += numeral
            number -= value
    return result

print int2roman(input("Enter an integer (1 to 4999): "))

"""
Enter an integer (1 to 4999): 2007
MMVII
"""
pythonuser18 0 Newbie Poster

i'm just playing around with python and decided to try this code out but keep getting a syntax error pointing to the colon after the 1 in,

Gribouillis 1,391 Programming Explorer Team Colleague

I just tested it with python 2.6 and it works. For python 3.1, I had to change the last statement to

print(int2roman(int(input("Enter an integer (1 to 4999): "))))

and it works.

vegaseat 1,735 DaniWeb's Hypocrite Team Colleague

@pythonuser18
you used () instead of {}
a dictionary container needs the curly braces.

pythonuser18 0 Newbie Poster
def DecToRom (number) :
    numerals={1: "I", 4: "IV", 5: "V", 9: "IX", 10: "X", 40: "XL", 50: "L", 90: "XC", 100: "C", 400: "CD", 500: "D", 900: "CM", 1000: "M"}
    result=""
    for value, numeral in sorted(numerals.items(), reverse=True):
        while number >= value:
            result += numeral
            number -= value
    return result
print DecToRom(raw_input(“Decimal number: “))

could someone tell me why this isn't working? It says Syntax Error: invalid syntax

I know the indents aren't formatted correctly on this post, but they are as they are suppose to be in python

Gribouillis 1,391 Programming Explorer Team Colleague

@@pythonuser18 For reference, I got it working, with python 2.6 and this last line:

print DecToRom(int(raw_input("Decimal number: ")))
pythonuser18 commented: Thanks a million! +0
woooee 814 Nearly a Posting Maven

Roman to integer. Note that it will take something like XXIXV and convert it. If someone already has a routine to check for valid input, please post it. And I'm afraid we've just eliminated one standard homework question. Oh well, nothing lasts forever.

roman_to_decimal = { 'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, \
                     'D': 500, 'M': 1000 }

roman = raw_input("Enter the roman numeral to convert to arabic: ").upper()

converted = True
arabic = 0
for n in range(0, len(roman)-1):
    this_char = roman[n]
    next_char = roman[n+1]
    if (this_char in roman_to_decimal) and \
       (next_char in roman_to_decimal):

        this_number = roman_to_decimal[this_char]
        next_number =  roman_to_decimal[next_char]
        print "converting %s to %d....." % (this_char, this_number),
        if this_number < next_number:
            arabic -= this_number
        else:
            arabic += this_number
        print "Total now =", arabic

    else:
        print "\nthis chr (%s) or next chr (%s) is not a valid roman numeral" \
              % (this_char, next_char)
        converted = False
        break

if converted:
    ##  add last roman numeral
    arabic += roman_to_decimal[roman[len(roman)-1]]

    print "\nThe roman numeral", roman, "is equal to",arabic
eljakim 0 Newbie Poster

A quick solution that does both directions:

# Roman Numerals
# 2010-06-15, Eljakim Schrijvers

mapping = [ ('cd','cccc'), ('xl','xxxx'),('iv','iiii'),('d','ccccc'),('l','xxxxx'),('v','iiiii'), ('cm','ccccccccc'), ('xc','xxxxxxxxx'),('ix','iiiiiiiii')]
mapping.reverse()
bignums = [ ('m',1000), ('c',100), ('x', 10), ('i',1) ]

def fromroman(x):
	for (kort, lang) in mapping: x= x.replace(kort, lang)
	x = '+'.join(list(x))
	for (karakter, waarde) in bignums: x = x.replace(karakter, str(waarde))
	if x=='': x = '0'
	return eval(x)

def toroman(x):
	val = ''
	for (karakter, waarde) in bignums:
		val = val + (x / waarde) * karakter
		x = x % waarde
	for (kort, lang) in mapping: 
		val = val.replace(lang,kort)
	return val
TrustyTony 888 pyMod Team Colleague Featured Poster

Nice concept but fails in execution:

# Roman Numerals
# 2010-06-15, Eljakim Schrijvers

# bug testing, translation of variables and reformating
# Tony Veijalainen 2010-06-16

mapping = [ ('cd','cccc'),
            ('xl','xxxx'),
            ('iv','iiii'),
            ('d','ccccc'),
            ('l','xxxxx'),
            ('v','iiiii'),
            ('cm','ccccccccc'),
            ('xc','xxxxxxxxx'),
            ('ix','iiiiiiiii')]


mapping.reverse()
bignums = [ ('m',1000), ('c',100), ('x', 10), ('i',1) ]

def fromroman(x):
	for (shortv, longv) in mapping: x= x.replace(shortv, longv)
	x = '+'.join(list(x))
	for (character, word) in bignums: x = x.replace(character, str(word))
	if x=='': x = '0'
	return eval(x)

def toroman(x):
	val = ''
	for (character, word) in bignums:
		val = val + (x / word) * character
		x = x % word
	for (shortv, longv) in mapping: 
		val = val.replace(longv,shortv)
	return val

for i in range(5000):
    if fromroman(toroman(i)) != i:
        print ('Bug found: %i, %s, %s' % (i,toroman(i),fromroman(toroman(i))))

The code gives plenty of bugs printed.

TrustyTony 888 pyMod Team Colleague Featured Poster

Got it passing my test by rearranging the values that were present in bug cases in the mapping (cleaned up multiletter expressions by *):

# Roman Numerals
# 2010-06-15, Eljakim Schrijvers

# bug testing, translation of variables and reformating
# Tony Veijalainen 2010-06-16

mapping = [ ('d',5*'c'),
            ('l',5*'x'),
            ('v',5*'i'),
            ('cm',9*'c'),
            ('xc',9*'x'),
            ('ix',9*'i'),
            ('cd',4*'c'),
            ('iv',4*'i'),
            ('xl',4*'x')]


mapping.reverse()
bignums = [ ('m',1000), ('c',100), ('x', 10), ('i',1) ]

def fromroman(x):
	for (shortv, longv) in mapping: x= x.replace(shortv, longv)
	x = '+'.join(list(x))
	for (character, word) in bignums: x = x.replace(character, str(word))
	if x=='': x = '0'
	return eval(x)

def toroman(x):
	val = ''
	for (character, word) in bignums:
		val = val + (x / word) * character
		x = x % word
	for (shortv, longv) in mapping: 
		val = val.replace(longv,shortv)
	return val

bugs=0
for i in range(5000):
    if fromroman(toroman(i)) != i:
        print ('Bug found: %i, %s, %s' % (i,toroman(i),fromroman(toroman(i))))
        bugs+=1

print(('Number of bugs found: %i' % bugs) if bugs else 'Test passed!')


""" Output:
Test passed!
"""
vegaseat commented: sharp mind +11
TrustyTony 888 pyMod Team Colleague Featured Poster

Correct fix is take out permanent reverse and use reversed() in to roman translation:

# Roman Numerals
# 2010-06-15, Eljakim Schrijvers

mapping = [ ('cd',4*'c'),
            ('xl',4*'x'),
            ('iv',4*'i'),
            ('d',5*'c'),
            ('l',5*'x'),
            ('v',5*'i'),
            ('cm',9*'c'),
            ('xc',9*'x'),
            ('ix',9*'i')]

bignums = [ ('m',1000), ('c',100), ('x', 10), ('i',1) ]

## vegaseat function for error check
def int2roman(number):
    numerals = { 1 : "I", 4 : "IV", 5 : "V", 9 : "IX", 10 : "X", 40 : "XL", 
        50 : "L", 90 : "XC", 100 : "C", 400 : "CD", 500 : "D", 900 : "CM", 1000 : "M" }
    result = ""
    for value, numeral in sorted(numerals.items(), reverse=True):
        while number >= value:
            result += numeral
            number -= value
    return result

def fromroman(x):
	for (shortv, longv) in mapping: x= x.replace(shortv, longv) ## reverse order needed
	x = '+'.join(list(x))
	for (character, word) in bignums: x = x.replace(character, str(word))
	if x=='': x = '0'
	return eval(x)

def toroman(x):
	val = ''
	for (character, word) in bignums:
		val = val + (x / word) * character
		x = x % word
	for (shortv, longv) in reversed(mapping): ## reverse here not permanantly
		val = val.replace(longv,shortv)
	return val

bugs1 = bugs2 = 0
for i in range(5000):
    if fromroman(toroman(i)) != i:
        print ('Reverse failure: %i, %s, %s' % (i,toroman(i),fromroman(toroman(i))))
        bugs1+=1

    if  int2roman(i).lower() != toroman(i): 
        print ('Wrong roman: %i, %s, %s' % (i,int2roman(i),toroman(i)))
        bugs2+=1

print(('Number of bugs found: %i reverse failure, %i wrong roman' % (bugs1,bugs2) if bugs1 + bugs2 else 'Test passed!'))
""" Last ilne of output:
Test passed!
"""
eljakim 0 Newbie Poster

I am quite embarrassed at the low quality I produced in the middle of the night.

Thank you for fixing it.

Eljakim

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.