Basic program to convert integer to Roman numerals?
Question:
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:
You have to make the symbolCount a global variable. And use () in print method.
When you use the def keyword, you just define a function, but you don’t run it.
what you’re looking for is something more like this:
def generate_all_numerals(n):
...
def to_roman(n):
...
print "This program ..."
n = raw_input("Enter...")
print to_roman(n)
welcome to python 🙂
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
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'
"""
# This program will allow the user to input a number from 1 - 3999 (in english) and will translate it to Roman numerals.
# sources: http://romannumerals.babuo.com/roman-numerals-100-1000
Guys the reason why I wrote this program like that so it becomes readable for everybody.
Let me know if you have any questions...
"""
while True:
try:
x = input("Enter a positive integer from 1 - 3999 (without spaces) and this program will translated to Roman numbers: ")
inttX = int(x)
if (inttX) == 0 or 0 > (inttX):
print("Unfortunately, the smallest number that you can enter is 1 ")
elif (inttX) > 3999:
print("Unfortunately, the greatest number that you can enter is 3999")
else:
if len(x) == 1:
if inttX == 1:
first = "I"
elif inttX == 2:
first = "II"
elif inttX == 3:
first = "III"
elif inttX == 4:
first = "IV"
elif inttX == 5:
first = "V"
elif inttX == 6:
first = "VI"
elif inttX == 7:
first = "VII"
elif inttX == 8:
first = "VIII"
elif inttX == 9:
first = "IX"
print(first)
break
if len(x) == 2:
a = int(x[0])
b = int(x[1])
if a == 0:
first = ""
elif a == 1:
first = "X"
elif a == 2:
first = "XX"
elif a == 3:
first = "XXX"
elif a == 4:
first = "XL"
elif a == 5:
first = "L"
elif a == 6:
first = "LX"
elif a == 7:
first = "LXX"
elif a == 8:
first = "LXXX"
elif a == 9:
first = "XC"
if b == 0:
first1 = "0"
if b == 1:
first1 = "I"
elif b == 2:
first1 = "II"
elif b == 3:
first1 = "III"
elif b == 4:
first1 = "IV"
elif b == 5:
first1 = "V"
elif b == 6:
first1 = "VI"
elif b == 7:
first1 = "VII"
elif b == 8:
first1 = "VIII"
elif b == 9:
first1 = "IX"
print(first + first1)
break
if len(x) == 3:
a = int(x[0])
b = int(x[1])
c = int(x[2])
if a == 0:
first12 = ""
if a == 1:
first12 = "C"
elif a == 2:
first12 = "CC"
elif a == 3:
first12 = "CCC"
elif a == 4:
first12 = "CD"
elif a == 5:
first12 = "D"
elif a == 6:
first12 = "DC"
elif a == 7:
first12 = "DCC"
elif a == 8:
first12 = "DCCC"
elif a == 9:
first12 = "CM"
if b == 0:
first = ""
elif b == 1:
first = "X"
elif b == 2:
first = "XX"
elif b == 3:
first = "XXX"
elif b == 4:
first = "XL"
elif b == 5:
first = "L"
elif b == 6:
first = "LX"
elif b == 7:
first = "LXX"
elif b == 8:
first = "LXXX"
elif b == 9:
first = "XC"
if c == 1:
first1 = "I"
elif c == 2:
first1 = "II"
elif c == 3:
first1 = "III"
elif c == 4:
first1 = "IV"
elif c == 5:
first1 = "V"
elif c == 6:
first1 = "VI"
elif c == 7:
first1 = "VII"
elif c == 8:
first1 = "VIII"
elif c == 9:
first1 = "IX"
print(first12 + first + first1)
break
if len(x) == 4:
a = int(x[0])
b = int(x[1])
c = int(x[2])
d = int(x[3])
if a == 0:
first1 = ""
if a == 1:
first1 = "M"
elif a == 2:
first1 = "MM"
elif a == 3:
first1 = "MMM"
if b == 0:
first12 = ""
if b == 1:
first12 = "C"
elif b == 2:
first12 = "CC"
elif b == 3:
first12 = "CCC"
elif b == 4:
first12 = "CD"
elif b == 5:
first12 = "D"
elif b == 6:
first12 = "DC"
elif b == 7:
first12 = "DCC"
elif b == 8:
first12 = "DCCC"
elif b == 9:
first12 = "CM"
if c == 0:
first3 = ""
elif c == 1:
first3 = "X"
elif c == 2:
first3 = "XX"
elif c == 3:
first3 = "XXX"
elif c == 4:
first3 = "XL"
elif c == 5:
first3 = "L"
elif c == 6:
first3 = "LX"
elif c == 7:
first3 = "LXX"
elif c == 8:
first3 = "LXXX"
elif c == 9:
first3 = "XC"
if d == 0:
first = ""
elif d == 1:
first = "I"
elif d == 2:
first = "II"
elif d == 3:
first = "III"
elif d == 4:
first = "IV"
elif d == 5:
first = "V"
elif d == 6:
first = "VI"
elif d == 7:
first = "VII"
elif d == 8:
first = "VIII"
elif d == 9:
first = "IX"
print(first1 + first12 + first3 + first)
break
except ValueError:
print(" Please enter a positive integer! ")
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
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)
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))
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)
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
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
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 the generate_all_of_numeral
function 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.
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)
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'
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’
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.
You have to make the symbolCount a global variable. And use () in print method.
When you use the def keyword, you just define a function, but you don’t run it.
what you’re looking for is something more like this:
def generate_all_numerals(n):
...
def to_roman(n):
...
print "This program ..."
n = raw_input("Enter...")
print to_roman(n)
welcome to python 🙂
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
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'
"""
# This program will allow the user to input a number from 1 - 3999 (in english) and will translate it to Roman numerals.
# sources: http://romannumerals.babuo.com/roman-numerals-100-1000
Guys the reason why I wrote this program like that so it becomes readable for everybody.
Let me know if you have any questions...
"""
while True:
try:
x = input("Enter a positive integer from 1 - 3999 (without spaces) and this program will translated to Roman numbers: ")
inttX = int(x)
if (inttX) == 0 or 0 > (inttX):
print("Unfortunately, the smallest number that you can enter is 1 ")
elif (inttX) > 3999:
print("Unfortunately, the greatest number that you can enter is 3999")
else:
if len(x) == 1:
if inttX == 1:
first = "I"
elif inttX == 2:
first = "II"
elif inttX == 3:
first = "III"
elif inttX == 4:
first = "IV"
elif inttX == 5:
first = "V"
elif inttX == 6:
first = "VI"
elif inttX == 7:
first = "VII"
elif inttX == 8:
first = "VIII"
elif inttX == 9:
first = "IX"
print(first)
break
if len(x) == 2:
a = int(x[0])
b = int(x[1])
if a == 0:
first = ""
elif a == 1:
first = "X"
elif a == 2:
first = "XX"
elif a == 3:
first = "XXX"
elif a == 4:
first = "XL"
elif a == 5:
first = "L"
elif a == 6:
first = "LX"
elif a == 7:
first = "LXX"
elif a == 8:
first = "LXXX"
elif a == 9:
first = "XC"
if b == 0:
first1 = "0"
if b == 1:
first1 = "I"
elif b == 2:
first1 = "II"
elif b == 3:
first1 = "III"
elif b == 4:
first1 = "IV"
elif b == 5:
first1 = "V"
elif b == 6:
first1 = "VI"
elif b == 7:
first1 = "VII"
elif b == 8:
first1 = "VIII"
elif b == 9:
first1 = "IX"
print(first + first1)
break
if len(x) == 3:
a = int(x[0])
b = int(x[1])
c = int(x[2])
if a == 0:
first12 = ""
if a == 1:
first12 = "C"
elif a == 2:
first12 = "CC"
elif a == 3:
first12 = "CCC"
elif a == 4:
first12 = "CD"
elif a == 5:
first12 = "D"
elif a == 6:
first12 = "DC"
elif a == 7:
first12 = "DCC"
elif a == 8:
first12 = "DCCC"
elif a == 9:
first12 = "CM"
if b == 0:
first = ""
elif b == 1:
first = "X"
elif b == 2:
first = "XX"
elif b == 3:
first = "XXX"
elif b == 4:
first = "XL"
elif b == 5:
first = "L"
elif b == 6:
first = "LX"
elif b == 7:
first = "LXX"
elif b == 8:
first = "LXXX"
elif b == 9:
first = "XC"
if c == 1:
first1 = "I"
elif c == 2:
first1 = "II"
elif c == 3:
first1 = "III"
elif c == 4:
first1 = "IV"
elif c == 5:
first1 = "V"
elif c == 6:
first1 = "VI"
elif c == 7:
first1 = "VII"
elif c == 8:
first1 = "VIII"
elif c == 9:
first1 = "IX"
print(first12 + first + first1)
break
if len(x) == 4:
a = int(x[0])
b = int(x[1])
c = int(x[2])
d = int(x[3])
if a == 0:
first1 = ""
if a == 1:
first1 = "M"
elif a == 2:
first1 = "MM"
elif a == 3:
first1 = "MMM"
if b == 0:
first12 = ""
if b == 1:
first12 = "C"
elif b == 2:
first12 = "CC"
elif b == 3:
first12 = "CCC"
elif b == 4:
first12 = "CD"
elif b == 5:
first12 = "D"
elif b == 6:
first12 = "DC"
elif b == 7:
first12 = "DCC"
elif b == 8:
first12 = "DCCC"
elif b == 9:
first12 = "CM"
if c == 0:
first3 = ""
elif c == 1:
first3 = "X"
elif c == 2:
first3 = "XX"
elif c == 3:
first3 = "XXX"
elif c == 4:
first3 = "XL"
elif c == 5:
first3 = "L"
elif c == 6:
first3 = "LX"
elif c == 7:
first3 = "LXX"
elif c == 8:
first3 = "LXXX"
elif c == 9:
first3 = "XC"
if d == 0:
first = ""
elif d == 1:
first = "I"
elif d == 2:
first = "II"
elif d == 3:
first = "III"
elif d == 4:
first = "IV"
elif d == 5:
first = "V"
elif d == 6:
first = "VI"
elif d == 7:
first = "VII"
elif d == 8:
first = "VIII"
elif d == 9:
first = "IX"
print(first1 + first12 + first3 + first)
break
except ValueError:
print(" Please enter a positive integer! ")
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
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)
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))
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)
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
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
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_numeral
function 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.
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)
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'
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’