I’m trying to write a code that converts a user-inputted integer into its Roman numeral equivalent. What I have so far is:
The point of the generate_all_of_numeral function is so that it creates a string for each specific numeral. For example, generate_all_of_numeral(2400, 'M', 2000) would return the string 'MM'.
I’m struggling with the main program. I start off finding the Roman numeral count for M and saving that into the variable M. Then I subtract by the number of M’s times the symbol value to give me the next value to work with for the next largest numeral.
Any nod to the right direction? Right now my code doesn’t even print anything.
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
One of the best ways to deal with this is using the divmod function. You check if the given number matches any Roman numeral from the highest to the lowest. At every match, you should return the respective character.
Some numbers will have remainders when you use the modulo function, so you also apply the same logic to the remainder. Obviously, I’m hinting at recursion.
See my answer below. I use an OrderedDict to make sure that I can iterate “downwards” the list, then I use a recursion of divmod to generate matches. Finally, I join all generated answers to produce a string.
from collections import OrderedDict
def write_roman(num):
roman = OrderedDict()
roman[1000] = "M"
roman[900] = "CM"
roman[500] = "D"
roman[400] = "CD"
roman[100] = "C"
roman[90] = "XC"
roman[50] = "L"
roman[40] = "XL"
roman[10] = "X"
roman[9] = "IX"
roman[5] = "V"
roman[4] = "IV"
roman[1] = "I"
def roman_num(num):
for r in roman.keys():
x, y = divmod(num, r)
yield roman[r] * x
num -= (r * x)
if num <= 0:
break
return "".join([a for a in roman_num(num)])
Taking it for a spin:
num = 35 print write_roman(num) # XXXV num = 994 print write_roman(num) # CMXCIV num = 1995 print write_roman(num) # MCMXCV num = 2015 print write_roman(num) # MMXV
Method 2
Here is another way, without division:
num_map = [(1000, 'M'), (900, 'CM'), (500, 'D'), (400, 'CD'), (100, 'C'), (90, 'XC'),
(50, 'L'), (40, 'XL'), (10, 'X'), (9, 'IX'), (5, 'V'), (4, 'IV'), (1, 'I')]
def num2roman(num):
roman = ''
while num > 0:
for i, r in num_map:
while num >= i:
roman += r
num -= i
return roman
# test
>>> num2roman(2242)
'MMCCXLII'
Update see the execution visualized
Method 3
A KISS version of Manhattan’s algorithm, without any “advanced” notion such as OrderedDict, recursion, generators, inner function and break:
ROMAN = [
(1000, "M"),
( 900, "CM"),
( 500, "D"),
( 400, "CD"),
( 100, "C"),
( 90, "XC"),
( 50, "L"),
( 40, "XL"),
( 10, "X"),
( 9, "IX"),
( 5, "V"),
( 4, "IV"),
( 1, "I"),
]
def int_to_roman(number):
result = ""
for (arabic, roman) in ROMAN:
(factor, number) = divmod(number, arabic)
result += roman * factor
return result
An early exit could be added as soon as number reaches zero, and the string accumulation could be made more pythonic, but my goal here was to produce the requested basic program.
Tested on all integers from 1 to 100000, which ought to be enough for anybody.
EDIT: the slightly more pythonic and faster version I alluded to:
def int_to_roman(number):
result = []
for (arabic, roman) in ROMAN:
(factor, number) = divmod(number, arabic)
result.append(roman * factor)
if number == 0:
break
return "".join(result)
Method 4
Here’s a lambda function for integer to roman numeral conversion, working up to 3999. It anchors some corner of the space of “unreadable things you probably don’t actually want to do”. But it may amuse someone:
lambda a: (
"".join(reversed([
"".join([
"IVXLCDM"[int(d)+i*2]
for d in [
"", "0", "00", "000", "01",
"1", "10", "100", "1000", "02"][int(c)]])
for i,c in enumerate(reversed(str(a))) ]))
)
This approach gives an alternative to using arithmetical manipulations to
isolate decimal digits and their place, as OP and many of the examples do.
The approach here goes straight for converting the decimal number to a string.
That way, digits can be isolated by list indexing. The data table is fairly
compressed, and no subtraction or division is used.
Admittedly, in the form given, whatever is gained in brevity is immediately
given up in readability. For people without time for puzzles, a version below
is given that avoids list comprehension and lambda functions.
Stepthrough
But I’ll explain the lambda function version here…
Going from back to front:
-
Convert a decimal integer to a reversed string of its digits, and enumerate
(i) over the reversed digits (c)..... for i,c in enumerate(reversed(str(a))) ....
-
Convert each digit c back to an integer (range of 0-9), and use it as an index into a list of magic digit strings. The magic is explained a little later on.
.... [ "", "0", "00", "000", "01", "1", "10", "100", "1000", "02"][int(c)]]) ....
-
Convert your selected magic digit string into a string of roman numeral
“digits”. Basically, you now have your decimal digit expressed as roman
numeral digits appropriate to the original 10’s place of the decimal digit.
This is the target of thegenerate_all_of_numeralfunction used by the OP..... "".join([ "IVXLCDM"[int(d)+i*2] for d in <magic digit string> .... -
Concatenate everything back in reversed order. The reversal is of the order
of the digits, but order within the digits (“digits”?) is unaffected.lambda a: ( "".join(reversed([ <roman-numeral converted digits> ]))
The Magic String List
Now, about that list of magic strings. It allows selecting the appropriate
string of roman numeral digits (up to four of them, each being one of three types 0, 1, or 2) for each different 10’s place that a decimal digit can occupy.
- 0 -> “”; roman numerals don’t show zeros.
- 1 -> “0”; 0 + 2*i maps to I, X, C or M -> I, X, C or M.
- 2 -> “00”; like for 1, x2 -> II, XX, CC, MM.
- 3 -> “000”; like for 1, x3 -> III, XXX, CCC, MMM.
- 4 -> “01”; like for 1, then 1 +2*i maps to V, L, or D -> IV, XL, CD.
- 5 -> “1”; maps to odd roman numeral digits -> V, L, D.
- 6 -> “10”; reverse of 4 -> VI, LX, DC.
- 7 -> “100”; add another I/X/C -> VII LXX, DCC
- 8 -> “1000”; add another I/X/C -> VIII, LXXX, DCCC
- 9 -> “02”; like for 1, plus the next 10’s level up (2 + i*2) -> IX, XC, CM.
At 4000 and above, this will throw an exception. “MMMM” = 4000, but this
doesn’t match the pattern anymore, breaking the assumptions of the algorithm.
Rewritten version
…as promised above…
def int_to_roman(a):
all_roman_digits = []
digit_lookup_table = [
"", "0", "00", "000", "01",
"1", "10", "100", "1000", "02"]
for i,c in enumerate(reversed(str(a))):
roman_digit = ""
for d in digit_lookup_table[int(c)]:
roman_digit += ("IVXLCDM"[int(d)+i*2])
all_roman_digits.append(roman_digit)
return "".join(reversed(all_roman_digits))
I again left out exception trapping, but at least now there’s a place to put it inline.
Method 5
I referred to this url for online decimal to roman conversion. If we extend the range of decimals up to 3,999,999 the script given by @Manhattan will not work. Here is the the correct script up to the range of 3,999,999.
def int_to_roman(num):
_values = [
1000000, 900000, 500000, 400000, 100000, 90000, 50000, 40000, 10000, 9000, 5000, 4000, 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]
_strings = [
'M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"]
result = ""
decimal = num
while decimal > 0:
for i in range(len(_values)):
if decimal >= _values[i]:
if _values[i] > 1000:
result += u'u0304'.join(list(_strings[i])) + u'u0304'
else:
result += _strings[i]
decimal -= _values[i]
break
return result
The unicode character u’304′ prints the overline char; e.g.

Sample Output:
Method 6
The Python package roman (repository) can be used to convert to and from roman numerals:
import roman
r = roman.toRoman(5)
assert r == 'V', r
n = roman.fromRoman('V')
assert n == 5, n
This package can be installed from the Python Package Index (PyPI) using the package manager pip:
pip install roman
Method 7
The approach by Laughing Man works. Using an ordered dictionary is clever. But his code re-creates the ordered dictionary every time the function is called, and within the function, in every recursive call, the function steps through the whole ordered dictionary from the top. Also, divmod returns both the quotient and the remainder, but the remainder is not used. A more direct approach is as follows.
def _getRomanDictOrdered():
#
from collections import OrderedDict
#
dIntRoman = OrderedDict()
#
dIntRoman[1000] = "M"
dIntRoman[900] = "CM"
dIntRoman[500] = "D"
dIntRoman[400] = "CD"
dIntRoman[100] = "C"
dIntRoman[90] = "XC"
dIntRoman[50] = "L"
dIntRoman[40] = "XL"
dIntRoman[10] = "X"
dIntRoman[9] = "IX"
dIntRoman[5] = "V"
dIntRoman[4] = "IV"
dIntRoman[1] = "I"
#
return dIntRoman
_dIntRomanOrder = _getRomanDictOrdered() # called once on import
def getRomanNumeralOffInt( iNum ):
#
lRomanNumerals = []
#
for iKey in _dIntRomanOrder:
#
if iKey > iNum: continue
#
iQuotient = iNum // iKey
#
if not iQuotient: continue
#
lRomanNumerals.append( _dIntRomanOrder[ iKey ] * iQuotient )
#
iNum -= ( iKey * iQuotient )
#
if not iNum: break
#
#
return ''.join( lRomanNumerals )
Checking the results:
>>> getRomanNumeralOffInt(35) 'XXXV' >>> getRomanNumeralOffInt(994) 'CMXCIV' >>> getRomanNumeralOffInt(1995) 'MCMXCV' >>> getRomanNumeralOffInt(2015) 'MMXV'
Method 8
I have observed that in most of the answers, people are storing excess notations like "IX" for 9, "XL" for 40 and so on.
This misses the main essence of Roman Conversion.
Here’s a small introduction and algorithm before I actually paste the code.
The original pattern for Roman numerals used the symbols I, V. and X (1, 5, and 10) as simple tally marks. Each marker for 1 (I) added a unit value up to 5 (V), and was then added to (V) to make the numbers from 6 to 9:
I, II, III, IIII, V, VI, VII, VIII, VIIII, X.
The numerals for 4 (IIII) and 9 (VIIII) proved problematic, and are generally replaced with IV (one less than 5) and IX (one less than 10). This feature of Roman numerals is called subtractive notation.
The numbers from 1 to 10 (including subtractive notation for 4 and 9) are expressed in Roman numerals as follows:
I, II, III, IV, V, VI, VII, VIII, IX, X.
The system being basically decimal, tens and hundreds follow the same pattern:
Thus 10 to 100 (counting in tens, with X taking the place of I, L taking the place of V and C taking the place of X):X, XX, XXX, XL, L, LX, LXX, LXXX, XC, C. Roman Numerals – Wikipedia
So, the main logic that can derive from the above introduction is that, we would strip the positional value and perform divisions based on the values of the literals Romans used.
Let’s start the base example. We have the integral list of the literals as [10, 5, 1]
-
1/10 = 0.1 (not of much use)
1/5 = 0.2 (not of much use, either)
1/1 = 1.0 (hmm, we got something!)
CASE 1: So, if quotient == 1, print the literal corresponding to the integer. So, the best data structure would be a dictionary.
{10: "X", 5: "V", 1:"I"}“I” will be printed.
-
2/10 = 0.2
2/5 = 0.4
2/1 = 2
CASE 2: So, if quotient > 1, print the literal corresponding to the integer which made it so and subtract it from the number.
This makes it 1 and it falls to CASE 1.
“II” is printed. -
3/10 = 0.3
3/5 = 0.6
3/1 = 3
So, CASE 2: “I”, CASE 2: “II” and CASE 1: “III”
-
CASE 3: Add 1 and check if quotient == 1.
(4+1)/10 = 0.5
(4+1)/5 = 1
So, this is the case where we first subtract the divisor and the number and print the literal corresponding to the result, followed by the divisor. 5-4=1, thus “IV” will be printed.
-
(9+1)/10 == 1
10-9=1. Print “I”, print “X”, i.e. “IX”
This extends to the tenths place and hundredths as well.
-
(90+(10^1))/100 = 1.
Print 100-90=”X”, followed by 100=”C”.
-
(400+(10^2))/500 = 1.
Print 500-400=”C”, followed by 500=”D”.
The last thing we need here is, extract the positional values. Ex: 449 should yield 400, 40, 9.
This can be made by removing the subtracting the modulo of 10^(position-1) and then taking the modulo of 10^position.
Ex: 449, position = 2: 449%(10^1) = 9 -> 449-9 -> 440%(10^2)= 40.
'''
Created on Nov 20, 2017
@author: lu5er
'''
n = int(input())
ls = [1000, 500, 100, 50, 10, 5, 1]
st = {1000:"M", 500:"D", 100:"C", 50:"L", 10:"X", 5:"V", 1:"I"}
rem = 0
# We traverse the number from right to left, extracting the position
for i in range(len(str(n)), 0, -1):
pos = i # stores the current position
num = (n-n%(10**(pos-1)))%(10**pos) # extracts the positional values
while(num>0):
for div in ls:
# CASE 1: Logic for 1, 5 and 10
if num/div == 1:
#print("here")
print(st[div], end="")
num-=div
break
# CASE 2: logic for 2, 3, 6 and 8
if num/div > 1:
print(st[div],end="")
num-=div
break
# CASE 3: Logic for 4 and 9
if (num+(10**(pos-1)))/div == 1:
print(st[div-num], end="")
print(st[div], end="")
num-=div
break
Output Test
99 XCIX 499 CDXCIX 1954 MCMLIV 1990 MCMXC 2014 MMXIV 35 XXXV 994 CMXCIV
Method 9
Here’s what I tried:
def int_to_roman(num):
dict_of_known_val = {1000: 'M', 900: 'CM', 500: 'D', 400: 'CD', 100: 'C',
90: 'XC', 50: 'L', 40: 'XL', 10: 'X', 9: 'IX', 5: 'V', 4: 'IV', 1: 'I'}
result = ''
for key, value in dict_of_known_val.items():
x, y = divmod(num, key)
result += value * x
num = y
return result
This code works for me.
Method 10
This is my approach
def itr(num):
dct = { 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" }
if(num in dct):
return dct[num]
for i in [1000,100,10,1]:
for j in [9*i, 5*i, 4*i, i]:
if(num>=j):
return itr(j) + itr(num-j)
Method 11
Interesting question. Several approaches but haven’t seen this one yet. We can trade execution time for memory, and reduce potential for pesky arithmetic errors (off-by-one, int vs float div, etc).
Each arabic digit (1s, 10s, 100s, etc) translates to a unique Roman sequence. So just use a lookup table.
ROMAN = {
0 : ['', 'm', 'mm', 'mmm'],
1 : ['', 'c', 'cc', 'ccc', 'cd', 'd', 'dc', 'dcc', 'dccc', 'cm'],
2 : ['', 'x', 'xx', 'xxx', 'xl', 'l', 'lx', 'lxx', 'lxxx', 'xc'],
3 : ['', 'i', 'ii', 'iii', 'iv', 'v', 'vi', 'vii', 'viii', 'ix'],
}
def to_roman (num, lower = True):
'''Return the roman numeral version of num'''
ret = ''
digits = '%04d' % num
for pos, digit in enumerate (digits):
ret += ROMAN [pos] [int (digit)]
return lower and ret or ret.upper ()
You can add checks for 0 > num > 4000, but not necessary as lookup will fail on int conversion for ‘-‘ or index out of range. I prefer lowercase, but upper available as well.
Digit selection could be done arithmetically, but algorithm becomes a bit trickier. Lookups are simple and effective.
Method 12
You have to make the symbolCount a global variable. And use () in print method.
Method 13
Only 1 – 999
while True:
num = input()
def val(n):
if n == 1:
rom = 'I'
return rom
if n == 4:
rom = 'IV'
return rom
if n == 5:
rom = 'V'
return rom
if n == 9:
rom = 'IX'
return rom
if n == 10:
rom = 'X'
return rom
if n == 40:
rom = 'XL'
return rom
if n == 50:
rom = 'L'
return rom
if n == 90:
rom = 'XC'
return rom
if n == 100:
rom = 'C'
return rom
if n == 400:
rom = 'CD'
return rom
if n == 500:
rom = 'D'
return rom
if n == 900:
rom = 'CM'
return rom
def lastdigit(num02):
num02 = num % 10
num03 = num % 5
if 9 > num02 > 5:
return str('V' + 'I'*num03)
elif num02 < 4:
return str('I'*num03)
else:
return str(val(num02))
k3 = lastdigit(num)
def tensdigit(num12):
num12 = num % 100 - num % 10
num13 = num % 50
if 90 > num12 > 50:
return str('L' + 'X'*(num13/10))
elif num12 < 40:
return str('X'*(num13/10))
else:
return str(val(num12))
k2 = tensdigit(num)
def hundigit(num112):
num112 = (num % 1000 - num % 100)
num113 = num % 500
if 900 > num112 > 500:
return str('D' + 'C'*(num113/100))
elif num112 < 400:
return str('C'*(num113/100))
else:
return str(val(num112))
k1 = hundigit(num)
print '%s%s%s' %(k1,k2,k3)
Method 14
Another way to do this. separating out processing of number starting with 4 , 9 and others. it can be simplified further
def checkio(data):
romans = [("I",1),("V",5),("X",10),("L",50),("C",100),("D",500),("M",1000)]
romans_rev = list(sorted(romans,key = lambda x: -x[1]))
def process_9(num,roman_str):
for (k,v) in romans:
if (v > num):
current_roman = romans[romans.index((k,v))]
prev_roman = romans[romans.index((k,v)) - 2]
roman_str += (prev_roman[0] + current_roman[0])
num -= (current_roman[1] - prev_roman[1])
break
return num,roman_str
def process_4(num,roman_str):
for (k,v) in romans:
if (v > num):
current_roman = romans[romans.index((k,v))]
prev_roman = romans[romans.index((k,v)) - 1]
roman_str += (prev_roman[0] + current_roman[0])
num -= (current_roman[1] - prev_roman[1])
break
return num,roman_str
def process_other(num,roman_str):
for (k,v) in romans_rev:
div = num // v
if ( div != 0 and num > 0 ):
roman_str += k * div
num -= v * div
break
return num,roman_str
def get_roman(num):
final_roman_str = ""
while (num > 0):
if (str(num).startswith('4')):
num,final_roman_str = process_4(num,final_roman_str)
elif(str(num).startswith('9')):
num,final_roman_str = process_9(num,final_roman_str)
else:
num,final_roman_str = process_other(num,final_roman_str)
return final_roman_str
return get_roman(data)
print(checkio(number))
Method 15
def test(num):
try:
if type(num) != type(1):
raise Exception("expected integer, got %s" % type(num))
if not 0 < num < 4000:
raise Exception("Argument must be between 1 and 3999")
ints = (1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1)
nums = ('M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I')
result = ""
for i in range(len(ints)):
count = int(num / ints[i])
result += nums[i] * count
num -= ints[i] * count
print result
except Exception as e:
print e.message
Method 16
roman_map = [(1000, 'M'), (900, 'CM'), (500, 'D'), (400, 'CD'), (100, 'C'), (90, 'XC'),
(50, 'L'), (40, 'XL'), (10, 'X'), (9, 'IX'), (5, 'V'), (4, 'IV'), (1, 'I')]
def IntToRoman (xn):
x = xn
y = 0
Str = ""
for i, r in roman_map:
# take the number and divisible by the roman number from 1000 to 1.
y = x//i
for j in range(0, y):
# If after divisibility is not 0 then take the roman number from list into String.
Str = Str+r
# Take the remainder to next round.
x = x%i
print(Str)
return Str
Test case:
>>> IntToRoman(3251) MMMCCLI 'MMMCCLI'
Method 17
start subtracting 1000,900… to 1 from A and stops when it finds positive.add corresponding roman to ans and make A to A-i where i is (1,4,5,9,10…..) repeat while A does not become 0.
def intToRoman(self, A):
l=[[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']]
ans=""
while(A>0):
for i,j in l[::-1]:
if A-i>=0:
ans+=j
A=A-i
break
return ans
Method 18
This is my recursive function approach to convert a number to its roman equivalent
def solution(n):
# TODO convert int to roman string
string=''
symbol=['M','D','C','L','X','V','I']
value = [1000,500,100,50,10,5,1]
num = 10**(len(str(n))-1)
quo = n//num
rem=n%num
if quo in [0,1,2,3]:
string=string+symbol[value.index(num)]*quo
elif quo in [4,5,6,7,8]:
tem_str=symbol[value.index(num)]+symbol[value.index(num)-1]
+symbol[value.index(num)]*3
string=string+tem_str[(min(quo,5)-4):(max(quo,5)-3)]
else:
string=string+symbol[value.index(num)]+symbol[value.index(num)-2]
if rem==0:
return string
else:
string=string+solution(rem)
return string
print(solution(499))
print(solution(999))
print(solution(2456))
print(solution(2791))
CDXCIX
CMXCIX
MMCDLVI
MMDCCXCI
Method 19
I was working through this conversion as a kata exercise, and I came up with a solution that takes advantage of Python’s string operations:
from collections import namedtuple
Abbreviation = namedtuple('Abbreviation', 'long short')
abbreviations = [
Abbreviation('I' * 1000, 'M'),
Abbreviation('I' * 500, 'D'),
Abbreviation('I' * 100, 'C'),
Abbreviation('I' * 50, 'L'),
Abbreviation('I' * 10, 'X'),
Abbreviation('I' * 5, 'V'),
Abbreviation('DCCCC', 'CM'),
Abbreviation('CCCC', 'CD'),
Abbreviation('LXXXX', 'XC'),
Abbreviation('XXXX', 'XL'),
Abbreviation('VIIII', 'IX'),
Abbreviation('IIII', 'IV')
]
def to_roman(arabic):
roman = 'I' * arabic
for abbr in abbreviations:
roman = roman.replace(abbr.long, abbr.short)
return roman
I like its simplicity! No need for modulo operations, conditionals, or multiple loops. Of course, you don’t need namedtuples either; you can use plain tuples or lists instead.
Method 20
Another way.
I wrote recursive loop by roman symbols, so the max depth of recursion equals length of roman tuple:
ROMAN = ((1000, 'M'), (900, 'CM'), (500, 'D'), (400, 'CD'),
(100, 'C'), (90, 'XC'), (50, 'L'), (40, 'XL'),
(10, 'X'), (9, 'IX'), (5, 'V'), (4, 'IV'), (1, 'I'))
def get_romans(number):
return do_recursive(number, 0, '')
def do_recursive(number, index, roman):
while number >= ROMAN[index][0]:
number -= ROMAN[index][0]
roman += ROMAN[index][1]
if number == 0:
return roman
return check_recursive(number, index + 1, roman)
if __name__ == '__main__':
print(get_romans(7))
print(get_romans(78))
Method 21
Here is my approach to solving this. The given number is first converted to a string so that then we can easily iterate over every digit to get the Roman part for the corresponding digit. To get the roman part for each digit I have grouped the Roman letters into 1 and 5 for each decimal position so the list of roman characters grouped based on decimal positions would look like [[‘I’, ‘V’], [‘X’, ‘L’], [‘C’, ‘D’], [‘M’]], where characters follow the order or ones, tens, hundreds, and thousands.
So we have digits to loop over and the roman characters for each order of the decimal place, we just need to prepare digits 0-9 using the above character list. The variable “order” picks the correct set of characters based on the decimal position of current digit, this is handled automatically as we are going from highest decimal place to lowest. Following is the full code:
def getRomanNumeral(num):
# ---------- inner function -------------------
def makeRomanDigit(digit, order):
chars = [['I', 'V'], ['X', 'L'], ['C', 'D'], ['M']]
if(digit == 1):
return chars[order][0]
if(digit == 2):
return chars[order][0] + chars[order][0]
if(digit == 3):
return chars[order][0] + chars[order][0] + chars[order][0]
if(digit == 4):
return chars[order][0] + chars[order][1]
if(digit == 5):
return chars[order][1]
if(digit == 6):
return chars[order][1] + chars[order][0]
if(digit == 7):
return chars[order][1] + chars[order][0] + chars[order][0]
if(digit == 8):
return chars[order][1] + chars[order][0] + chars[order][0] + chars[order][0]
if(digit == 9):
return chars[order][0] + chars[order+1][0]
if(digit == 0):
return ''
#--------------- main -----------------
str_num = str(num)
order = len(str_num) - 1
result = ''
for digit in str_num:
result += makeRomanDigit(int(digit), order)
order-=1
return result
A few tests:
getRomanNumeral(112) 'CXII' getRomanNumeral(345) 'CCCXLV' getRomanNumeral(591) 'DXCI' getRomanNumeral(1000) 'M'
I know a lot can be improved in the code or approach towards the problem, but this was my first attempt at this problem.
Method 22
I have produced an answer that works for any int >= 0:
Save the following as romanize.py
def get_roman(input_number: int, overline_code: str = 'u0305') -> str:
"""
Recursive function which returns roman numeral (string), given input number (int)
>>> get_roman(0)
'N'
>>> get_roman(3999)
'MMMCMXCIX'
>>> get_roman(4000)
'MVu0305'
>>> get_roman(4000, overline_code='^')
'MV^'
"""
if input_number < 0 or not isinstance(input_number, int):
raise ValueError(f'Only integers, n, within range, n >= 0 are supported.')
if input_number <= 1000:
numeral, remainder = core_lookup(input_number=input_number)
else:
numeral, remainder = thousand_lookup(input_number=input_number, overline_code=overline_code)
if remainder != 0:
numeral += get_roman(input_number=remainder, overline_code=overline_code)
return numeral
def core_lookup(input_number: int) -> (str, int):
"""
Returns highest roman numeral (string) which can (or a multiple thereof) be looked up from number map and the
remainder (int).
>>> core_lookup(3)
('III', 0)
>>> core_lookup(999)
('CM', 99)
>>> core_lookup(1000)
('M', 0)
"""
if input_number < 0 or input_number > 1000 or not isinstance(input_number, int):
raise ValueError(f'Only integers, n, within range, 0 <= n <= 1000 are supported.')
basic_lookup = NUMBER_MAP.get(input_number)
if basic_lookup:
numeral = basic_lookup
remainder = 0
else:
multiple = get_multiple(input_number=input_number, multiples=NUMBER_MAP.keys())
count = input_number // multiple
remainder = input_number % multiple
numeral = NUMBER_MAP[multiple] * count
return numeral, remainder
def thousand_lookup(input_number: int, overline_code: str = 'u0305') -> (str, int):
"""
Returns highest roman numeral possible, that is a multiple of or a thousand that of which can be looked up from
number map and the remainder (int).
>>> thousand_lookup(3000)
('MMM', 0)
>>> thousand_lookup(300001, overline_code='^')
('C^C^C^', 1)
>>> thousand_lookup(30000002, overline_code='^')
('X^^X^^X^^', 2)
"""
if input_number <= 1000 or not isinstance(input_number, int):
raise ValueError(f'Only integers, n, within range, n > 1000 are supported.')
num, k, remainder = get_thousand_count(input_number=input_number)
numeral = get_roman(input_number=num, overline_code=overline_code)
numeral = add_overlines(base_numeral=numeral, num_overlines=k, overline_code=overline_code)
# Assume:
# 4000 -> MV^, https://en.wikipedia.org/wiki/4000_(number)
# 6000 -> V^M, see https://en.wikipedia.org/wiki/6000_(number)
# 9000 -> MX^, see https://en.wikipedia.org/wiki/9000_(number)
numeral = numeral.replace(NUMBER_MAP[1] + overline_code, NUMBER_MAP[1000])
return numeral, remainder
def get_thousand_count(input_number: int) -> (int, int, int):
"""
Returns three integers defining the number, number of thousands and remainder
>>> get_thousand_count(999)
(999, 0, 0)
>>> get_thousand_count(1001)
(1, 1, 1)
>>> get_thousand_count(2000002)
(2, 2, 2)
"""
num = input_number
k = 0
while num >= 1000:
k += 1
num //= 1000
remainder = input_number - (num * 1000 ** k)
return num, k, remainder
def get_multiple(input_number: int, multiples: iter) -> int:
"""
Given an input number(int) and a list of numbers, finds the number in list closest (rounded down) to input number
>>> get_multiple(45, [1, 2, 3])
3
>>> get_multiple(45, [1, 2, 3, 44, 45, 46])
45
>>> get_multiple(45, [1, 4, 5, 9, 10, 40, 50, 90])
40
"""
options = sorted(list(multiples) + [input_number])
return options[options.index(input_number) - int(input_number not in multiples)]
def add_overlines(base_numeral: str, num_overlines: int = 1, overline_code: str = 'u0305') -> str:
"""
Adds overlines to input base numeral (string) and returns the result.
>>> add_overlines(base_numeral='II', num_overlines=1, overline_code='^')
'I^I^'
>>> add_overlines(base_numeral='I^I^', num_overlines=1, overline_code='^')
'I^^I^^'
>>> add_overlines(base_numeral='II', num_overlines=2, overline_code='^')
'I^^I^^'
"""
return ''.join([char + overline_code*num_overlines if char.isalnum() else char for char in base_numeral])
def gen_number_map() -> dict:
"""
Returns base number mapping including combinations like 4 -> IV and 9 -> IX, etc.
"""
mapping = {
1000: 'M',
500: 'D',
100: 'C',
50: 'L',
10: 'X',
5: 'V',
1: 'I',
0: 'N'
}
for exponent in range(3):
for num in (4, 9,):
power = 10 ** exponent
mapping[num * power] = mapping[1 * power] + mapping[(num + 1) * power]
return mapping
NUMBER_MAP = gen_number_map()
if __name__ == '__main__':
import doctest
doctest.testmod(verbose=True, raise_on_error=True)
# Optional extra tests
# doctest.testfile('test_romanize.txt', verbose=True)
Here are some extra tests in case useful.
Save the following as test_romanize.txt in the same directory as the romanize.py:
The ``romanize`` module
=======================
The ``get_roman`` function
--------------------------
Import statement:
>>> from romanize import get_roman
Tests:
>>> get_roman(0)
'N'
>>> get_roman(6)
'VI'
>>> get_roman(11)
'XI'
>>> get_roman(345)
'CCCXLV'
>>> get_roman(989)
'CMLXXXIX'
>>> get_roman(989000000, overline_code='^')
'C^^M^^L^^X^^X^^X^^M^X^^'
>>> get_roman(1000)
'M'
>>> get_roman(1001)
'MI'
>>> get_roman(2000)
'MM'
>>> get_roman(2001)
'MMI'
>>> get_roman(900)
'CM'
>>> get_roman(4000, overline_code='^')
'MV^'
>>> get_roman(6000, overline_code='^')
'V^M'
>>> get_roman(9000, overline_code='^')
'MX^'
>>> get_roman(6001, overline_code='^')
'V^MI'
>>> get_roman(9013, overline_code='^')
'MX^XIII'
>>> get_roman(70000000000, overline_code='^')
'L^^^X^^^X^^^'
>>> get_roman(9000013, overline_code='^')
'M^X^^XIII'
>>> get_roman(989888003, overline_code='^')
'C^^M^^L^^X^^X^^X^^M^X^^D^C^C^C^L^X^X^X^V^MMMIII'
The ``get_thousand_count`` function
--------------------------
Import statement:
>>> from romanize import get_thousand_count
Tests:
>>> get_thousand_count(13)
(13, 0, 0)
>>> get_thousand_count(6013)
(6, 1, 13)
>>> get_thousand_count(60013)
(60, 1, 13)
>>> get_thousand_count(600013)
(600, 1, 13)
>>> get_thousand_count(6000013)
(6, 2, 13)
>>> get_thousand_count(999000000000000000000000000999)
(999, 9, 999)
>>> get_thousand_count(2005)
(2, 1, 5)
>>> get_thousand_count(2147483647)
(2, 3, 147483647)
The ``core_lookup`` function
--------------------------
Import statement:
>>> from romanize import core_lookup
Tests:
>>> core_lookup(2)
('II', 0)
>>> core_lookup(6)
('V', 1)
>>> core_lookup(7)
('V', 2)
>>> core_lookup(19)
('X', 9)
>>> core_lookup(900)
('CM', 0)
>>> core_lookup(999)
('CM', 99)
>>> core_lookup(1000)
('M', 0)
>>> core_lookup(1000.2)
Traceback (most recent call last):
ValueError: Only integers, n, within range, 0 <= n <= 1000 are supported.
>>> core_lookup(10001)
Traceback (most recent call last):
ValueError: Only integers, n, within range, 0 <= n <= 1000 are supported.
>>> core_lookup(-1)
Traceback (most recent call last):
ValueError: Only integers, n, within range, 0 <= n <= 1000 are supported.
The ``gen_number_map`` function
--------------------------
Import statement:
>>> from romanize import gen_number_map
Tests:
>>> gen_number_map()
{1000: 'M', 500: 'D', 100: 'C', 50: 'L', 10: 'X', 5: 'V', 1: 'I', 0: 'N', 4: 'IV', 9: 'IX', 40: 'XL', 90: 'XC', 400: 'CD', 900: 'CM'}
The ``get_multiple`` function
--------------------------
Import statement:
>>> from romanize import get_multiple
>>> multiples = [0, 1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000]
Tests:
>>> get_multiple(0, multiples)
0
>>> get_multiple(1, multiples)
1
>>> get_multiple(2, multiples)
1
>>> get_multiple(3, multiples)
1
>>> get_multiple(4, multiples)
4
>>> get_multiple(5, multiples)
5
>>> get_multiple(6, multiples)
5
>>> get_multiple(9, multiples)
9
>>> get_multiple(13, multiples)
10
>>> get_multiple(401, multiples)
400
>>> get_multiple(399, multiples)
100
>>> get_multiple(100, multiples)
100
>>> get_multiple(99, multiples)
90
The ``add_overlines`` function
--------------------------
Import statement:
>>> from romanize import add_overlines
Tests:
>>> add_overlines('AB')
'Au0305Bu0305'
>>> add_overlines('Au0305Bu0305')
'Au0305u0305Bu0305u0305'
>>> add_overlines('AB', num_overlines=3, overline_code='^')
'A^^^B^^^'
>>> add_overlines('A^B^', num_overlines=1, overline_code='^')
'A^^B^^'
>>> add_overlines('AB', num_overlines=3, overline_code='u0305')
'Au0305u0305u0305Bu0305u0305u0305'
>>> add_overlines('Au0305Bu0305', num_overlines=1, overline_code='u0305')
'Au0305u0305Bu0305u0305'
>>> add_overlines('A^B', num_overlines=3, overline_code='^')
'A^^^^B^^^'
>>> add_overlines('A^B', num_overlines=0, overline_code='^')
'A^B'
Method 23
The code for this roman numeral does not check for errors like wrong letters it is just for a perfect roman numeral letters
roman_dict = {'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 = input('Enter the roman numeral: ').upper()
roman_initial = roman # used to retain the original roman figure entered
lst = []
while roman != '':
if len(roman) > 1:
check = roman[0] + roman[1]
if check in roman_dict and len(roman) > 1:
lst.append(check)
roman = roman[roman.index(check[1])+1:]
else:
if check not in roman_dict and len(roman) > 1:
lst.append(check[0])
roman = roman[roman.index(check[0])+1:]
else:
if len(roman)==1:
check = roman[0]
lst.append(check[0])
roman = ''
if lst != []:
Sum = 0
for i in lst:
if i in roman_dict:
Sum += roman_dict[i]
print('The roman numeral %s entered is'%(roman_initial),Sum)
Method 24
Here is a simpler way, i followed the basic conversion method explained here
Please find the code below:
input = int(raw_input()) # enter your integerval from keyboard
Decimals =[1,4,5,9,10,40,50,90,100,400,500,900,1000]
Romans =['I','IV','V','IX','X','XL','L','XC','C','CD','D','CM','M']
romanletters= [] # empty array to fill each corresponding roman letter from the Romans array
while input != 0:
# we use basic formula of converstion, therefore input is substracted from
# least maximum similar number from Decimals array until input reaches zero
for val in range (len(Decimals)):
if input >= Decimals[val]:
decimal = Decimals[val]
roman = Romans[val]
difference = input - decimal
romanletters.append(roman)
input = difference
dec_to_roman = ''.join(romanletters) # concatinate the values
print dec_to_roman
Method 25
I think this is the simplest approach. Roman Numerals have a range 1 to 3999.
def toRomanNumeral(n):
NumeralMatrix = [
["", "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"]
]
# The above matrix helps us to write individual digits of the input as roman numeral.
# Rows corresponds to the place of the digit (ones, tens, hundreds, etc).
# Column corresponds to the digit itself.
rn = ""
d = []
# d is an array to store the individual digits of the input
while (n!=0):
d.append(n%10)
n =int(n/10)
for i in range(len(d), 0, -1):
rn += NumeralMatrix[i-1][d[i-1]]
# [i-1] is the digit's place (0 - ones, 1 - tens, etc)
# [d[i-1]] is the digit itself
return rn
print(toRomanNumeral(49))
# XLIX
Method 26
I grabbed this from my GitHub (you can find a more detailed version there). This function has a fixed computation time and makes use of no external libraries. It works for all integers between 0 and 1000.
def to_roman(n):
try:
if n >= 0 and n <= 1000:
d = [{'0':'','1':'M'},
{'0':'','1':'C','2':'CC','3':'CCC','4':'DC','5':'D',
'6':'DC','7':'DCC','8':'DCCC','9':'MC'},
{'0':'','1':'X','2':'XX','3':'XXX','4':'XL','5':'L',
'6':'LX','7':'LXX','8':'LXXX','9':'CX'},
{'0':'','1':'I','2':'II','3':'III','4':'IV','5':'V',
'6':'VI','7':'VII','8':'VIII','9':'IX'}]
x = str('0000' + str(n))[-4:]
r = ''
for i in range(4):
r = r + d[i][x[i]]
return r
else:
return '`n` is out of bounds.'
except:
print('Is this real life?nIs `n` even an integer?')
Output from entering n=265:
>>> to_roman(265)
'CCLXV'
Method 27
def convert_to_roman(number, rom_denom, rom_val):
'''Recursive solution'''
#Base case
if number == 0:
return ''
else:
rom_str = (rom_denom[0] * (number//rom_val[0])) + convert_to_roman(number %
rom_val[0], rom_denom[1:], rom_val[1:]) #Recursive call
return rom_str
rom_denom = ['M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I']
rom_val = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]
number = int(input("Enter numeral to convert: "))
rom_str = convert_to_roman(number, rom_denom, rom_val)
print(rom_str)
Method 28
def convert_to_roman(num):
# print(num)
roman=""
while(num!=0):
if num in range(1000,5000):
roman+="M"*(num//1000)
num-=(num//1000)*1000
if num in range(900,1000): #range(a,b) works from a to b-1 here from 900->999
roman+="CM"
num-=900
if num in range(500,900):
roman+="D"
num-=500
if num in range(400,500):
roman+="CD"
num-=400
if num in range(100,400):
roman+="C"*(num//100)
num-=100*(num//100)
if num in range(90,100):
roman+="XC"
num-=90
if num in range(50,90):
roman+="L"
num-=50
if num in range(40,50):
roman+="XL"
num-=40
if num in range(10,40):
roman+="X"*(num//10)
num-=10*(num//10)
if num==9:
roman+="IX"
num-=9
if num in range(5,9):
roman+="V"
num-=5
if num ==4:
roman+="IV"
num-=4
if num ==3:
roman+="III"
num-=3
if num ==2:
roman+="II"
num-=2
if num ==1:
roman+="I"
num-=1
# print(num)
return roman
#Start writing your code here
num=888
print(num,":",convert_to_roman(num))
Method 29
A simple string concatenation through iteration:
def roman(x: int) -> str:
"""Convert into to roman numeral"""
# Iterative
L = [(1000, 'M'), (900, 'CM'), (500, 'D'), (400, 'CD'),
(100, 'C'), (90, 'XC'), (50, 'L'), (40, 'XL'),
(10, 'X'), (9, 'IX'), (5, 'V'), (4, 'IV'), (1, 'I')]
if x >= 1:
y = ""
for val, sym in L:
y += sym*(x//val)
x -= val*(x//val)
else:
return None
return y
>>> roman(399) 'CCCXCIX'
Method 30
class Solution:
def intToRoman(self, num: int) -> str:
if num>3999:
return
value = [1000,900,500,400,100,90,50,40,10,9,5,4,1]
sym = ["M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"]
index = 0
result = ""
while num>0:
res = num//value[index]
num = num%value[index]
while res>0:
result = result+sym[index]
res =res-1
index+=1
return result
if __name__ == '__main__':
obj = Solution()
print(obj.intToRoman(1994))
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

