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_
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
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))
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
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
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.