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_
"""
# 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! ")
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)
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
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)
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 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)
'MV\u0305'
>>> 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')
'A\u0305B\u0305'
>>> add_overlines('A\u0305B\u0305')
'A\u0305\u0305B\u0305\u0305'
>>> 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')
'A\u0305\u0305\u0305B\u0305\u0305\u0305'
>>> add_overlines('A\u0305B\u0305', num_overlines=1, overline_code='\u0305')
'A\u0305\u0305B\u0305\u0305'
>>> add_overlines('A^B', num_overlines=3, overline_code='^')
'A^^^^B^^^'
>>> add_overlines('A^B', num_overlines=0, overline_code='^')
'A^B'