Let’s say you wanted to write two functions that, when given any integer from 1 through 1000, could:
- Translate Roman numerals into the corresponding integers.
- Convert integers into Roman numerals.
Here’s how you could do it.
Functions
The numerals-to-number converter looks at a character, determines if it’s corresponding value is less than or greater than its neighbor, and determines if the value should be added or subtracted from the total. For example, XXIV becomes [10, 10, -1, 5], which sums to 24.
def str_to_int(numeral_string):
roman_numerals_dict = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D':500, 'M':1000}
# Divide the numeral string into a list of characters
numbers = []
for numeral in numeral_string:
numbers.append(roman_numerals_dict[numeral])
for index in range(len(numbers) - 1):
if numbers[index] < numbers[index + 1]:
numbers[index] = numbers[index] * -1
return(sum(numbers))
The number-to-numeral converter is a little more complicated. When you look at roman numerals, you can see that each value is 1 or 5 multiplied by a base-10 number:
Numeral | Value |
---|---|
I | 1 |
X | 10 |
C | 100 |
M | 1000 |
V | 5 |
L | 50 |
D | 500 |
This program takes advantage of this. The function builds a dictionary where the keys are 10 ^ i
for i
in [0,3]
and 5 * 10 ^ i
for i
in [0,2]
. It takes in an integer and reads the digits right to left, and translates the value using the dictionary and the place of the digit (ones, tens, hundreds, thousands). The only complication is how 4 and 9 are represented, since placement of the symbols matters to indicate reduced values. (That is to say, for numerals A
and B
, "AB" = B - A
.)
def int_to_string(integer_number, index = 0):
number_to_roman_nums_dict = {1: 'I', 5: 'V', 10: 'X', 50: 'L', 100:'C', 500: 'D', 1000: 'M'}
result_string = str()
# Convert the input values into a string
integer_number_as_string = str(integer_number)
number_of_digits = len(integer_number_as_string)
# Determine the last numeral in the string
ending_number = int(integer_number_as_string[-1])
# Convert the last numeral into a roman numeral
if ending_number < 4:
# Ex. 3 -> resulting_string = 3 * "I" = "III"
result_string = ending_number * number_to_roman_nums_dict[1 * (10 ** index)]
if ending_number == 4:
# Ex. 4 -> resulting_string = "I" + "V" = "IV" = 4
result_string = number_to_roman_nums_dict[1 * (10 ** index)] + number_to_roman_nums_dict[5 * (10 ** index)]
if ending_number > 4 and ending_number < 9:
# Ex. 8 -> resulting_string = "V" + "I" + "I"= "VII" = 8
result_string = number_to_roman_nums_dict[5 * (10 ** index)] + (ending_number % 5) * number_to_roman_nums_dict[1 * (10 ** index)]
if ending_number == 9:
# Ex. 90 -> ending_number = 9, index = 1.
# resulting_string = number_to_roman_nums_dict[1 * 10^1] + number_to_roman_nums_dict[10 * 10^1]
# = number_to_roman_nums_dict[10] + number_to_roman_nums_dict[100]
# = "X" + "C" = "XC"
result_string = number_to_roman_nums_dict[1 * (10 ** index)] + number_to_roman_nums_dict[10 * (10 ** index)]
# Use recursion to evaluate numerals in the tens, hundreds, and thousands places
if number_of_digits > 1:
index += 1
result_string = int_to_string(int(integer_number_as_string[0:-1]), index) + result_string
return(result_string)
Unit testing
It’s always important to test programs. Here, I made sure the two functions worked by feeding the results from one into the other, and repeating this for all integers between 1 and 1000. Then I printed out 100 random numbers to individually inspect them.
from random import randint
import pandas as pd
from numpy import vectorize
success = True
for number in range(1,1000):
if str_to_int(int_to_string(number)) != number:
success = False
print("Failed on {}.".format(number))
if success:
print("All numbers converted correctly.")
All numbers converted correctly.
# Randomly select 100 numbers from 1 to 1000
random_integers = [randint(1, 1000) for _ in range(100)]
dataframe = pd.DataFrame(random_integers, columns = ["integer value"])
dataframe["integers converted to roman numeral"] = dataframe["integer value"].apply(vectorize(int_to_string))
dataframe["numerals converted back to integer"] = dataframe["integers converted to roman numeral"].apply(vectorize(str_to_int))
print(dataframe)
integer value | integers converted to roman numeral | numerals converted back to integer | |
---|---|---|---|
0 | 539 | DXXXIX | 539 |
1 | 353 | CCCLIII | 353 |
2 | 1000 | M | 1000 |
3 | 182 | CLXXXII | 182 |
4 | 239 | CCXXXIX | 239 |
5 | 869 | DCCCLXIX | 869 |
6 | 378 | CCCLXXVIII | 378 |
7 | 497 | CDXCVII | 497 |
8 | 980 | CMLXXX | 980 |
9 | 459 | CDLIX | 459 |
10 | 23 | XXIII | 23 |
11 | 146 | CXLVI | 146 |
12 | 427 | CDXXVII | 427 |
13 | 158 | CLVIII | 158 |
14 | 423 | CDXXIII | 423 |
15 | 669 | DCLXIX | 669 |
16 | 538 | DXXXVIII | 538 |
17 | 981 | CMLXXXI | 981 |
18 | 830 | DCCCXXX | 830 |
19 | 9 | IX | 9 |
20 | 21 | XXI | 21 |
21 | 170 | CLXX | 170 |
22 | 788 | DCCLXXXVIII | 788 |
23 | 718 | DCCXVIII | 718 |
24 | 200 | CC | 200 |
25 | 531 | DXXXI | 531 |
26 | 468 | CDLXVIII | 468 |
27 | 203 | CCIII | 203 |
28 | 642 | DCXLII | 642 |
29 | 312 | CCCXII | 312 |
... | ... | ... | ... |
70 | 929 | CMXXIX | 929 |
71 | 648 | DCXLVIII | 648 |
72 | 26 | XXVI | 26 |
73 | 7 | VII | 7 |
74 | 246 | CCXLVI | 246 |
75 | 272 | CCLXXII | 272 |
76 | 261 | CCLXI | 261 |
77 | 4 | IV | 4 |
78 | 523 | DXXIII | 523 |
79 | 220 | CCXX | 220 |
80 | 134 | CXXXIV | 134 |
81 | 368 | CCCLXVIII | 368 |
82 | 983 | CMLXXXIII | 983 |
83 | 767 | DCCLXVII | 767 |
84 | 2 | II | 2 |
85 | 356 | CCCLVI | 356 |
86 | 355 | CCCLV | 355 |
87 | 499 | CDXCIX | 499 |
88 | 464 | CDLXIV | 464 |
89 | 786 | DCCLXXXVI | 786 |
90 | 975 | CMLXXV | 975 |
91 | 518 | DXVIII | 518 |
92 | 798 | DCCXCVIII | 798 |
93 | 414 | CDXIV | 414 |
94 | 835 | DCCCXXXV | 835 |
95 | 246 | CCXLVI | 246 |
96 | 243 | CCXLIII | 243 |
97 | 888 | DCCCLXXXVIII | 888 |
98 | 614 | DCXIV | 614 |
99 | 99 | XCIX | 99 |
100 rows × 3 columns