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


Tags:

Updated: