ActiveState Powered by ActiveState

Recipe 415384: Decimal to Roman numerals


Convert decimals to Roman numerials. I use a recursive algorithm here since it reflects the thinking clearly in code. A non-recursive algothrithm can be done as well.

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
"""
Victor Yang, pythonrocks@yahoo.com

Convert decimals to Roman numerials.
I use a recursive algorithm here since it reflects the thinking clearly in code.
A non-recursive algothrithm can be done as well.

usage: python roman.py 
It will run the test case against toRoman function
"""

import sys
import unittest


# these two lists serves as building blocks to construt any number
# just like coin denominations.
# 1000->"M", 900->"CM", 500->"D"...keep on going 
decimalDens=[1000,900,500,400,100,90,50,40,10,9,5,4,1]
romanDens=["M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"]


def toRoman(dec):
	"""
	Perform sanity check on decimal and throws exceptions when necessary
	"""		
        if dec <=0:
	  raise ValueError, "It must be a positive"
         # to avoid MMMM
	elif dec>=4000:  
	  raise ValueError, "It must be lower than MMMM(4000)"
  
	return decToRoman(dec,"",decimalDens,romanDens)

def decToRoman(num,s,decs,romans):
	"""
	  convert a Decimal number to Roman numeral recursively
	  num: the decimal number
	  s: the roman numerial string
	  decs: current list of decimal denomination
	  romans: current list of roman denomination
	"""
	if decs:
	  if (num < decs[0]):
	    # deal with the rest denomination
	    return decToRoman(num,s,decs[1:],romans[1:])		  
	  else:
	    # deduce this denomation till num<desc[0]
	    return decToRoman(num-decs[0],s+romans[0],decs,romans)	  
	else:
	  # we run out of denomination, we are done 
	  return s


class DecToRomanTest(unittest.TestCase):

	def setUp(self):
		print '\nset up'
	def tearDown(self):
		print 'tear down'
	
	def testDens(self):
		
	   for i in range(len(decimalDens)):
		r=toRoman(decimalDens[i])
		self.assertEqual(r,romanDens[i])

	def testSmallest(self):
		self.assertEqual('I',toRoman(1))

	def testBiggest(self):
		self.assertEqual('MMMCMXCIX',toRoman(3999))

	def testZero(self):
		self.assertRaises(ValueError,toRoman,0)

	def testNegative(self):
		
		self.assertRaises(ValueError,toRoman,-100)


	def testMillonDollar(self):
		
		self.assertRaises(ValueError,toRoman,1000000)

		

if __name__ == "__main__":
	
	unittest.main()

Discussion

Just for fun.

Comments

  1. 1. At 3:23 a.m. on 15 jun 2005, Raymond Hettinger said:

    For grins: A non-recursive version.

    coding = zip(
        [1000,900,500,400,100,90,50,40,10,9,5,4,1],
        ["M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"]
    )
    
    def decToRoman(num):
        if num &lt;= 0 or num &gt;= 4000 or int(num) != num:
            raise ValueError('Input should be an integer between 1 and 3999')
        result = []
        for d, r in coding:
            while num &gt;= d:
                result.append(r)
                num -= d
        return ''.join(result)
    
    
    for i in xrange(1,4000):
        print i, decToRoman(i)
    
  2. 2. At 3:23 p.m. on 5 aug 2007, greg p said:

    Now it's Online. I made this recipe into an online utility. Check it out:

    http://www.utilitymill.com/utility/Decimal_to_Roman_Numerals

Sign in to comment