5 Best Ways to Convert Integer to Roman Numeral in Python

πŸ’‘ Problem Formulation: Converting integers to Roman numerals is a common programming task that involves mapping decimal numbers to the numeral system of ancient Rome. For example, the input integer 2023 should output MMXXIII as the corresponding Roman numeral.

Method 1: Hardcoded Dictionaries

In this method, we use two dictionaries: one for base Roman numerals (e.g., I, V, X, L, C, D, M) and other for subtractions (e.g., IV, IX, XL). The function converts each digit to its Roman equivalent by looking up these dictionaries.

Here’s an example:

def int_to_roman(num):
    val = [
        1000, 900, 500, 400,
        100, 90, 50, 40,
        10, 9, 5, 4,
        1
        ]
    syms = [
        "M", "CM", "D", "CD",
        "C", "XC", "L", "XL",
        "X", "IX", "V", "IV",
        "I"
        ]
    roman_num = ''
    i = 0
    while num > 0:
        for _ in range(num // val[i]):
            roman_num += syms[i]
            num -= val[i]
        i += 1
    return roman_num

print(int_to_roman(2023))

Output: MMXXIII

This code snippet defines a function int_to_roman() that converts an integer to a Roman numeral. It iterates through the predefined values, concatenating the symbols as it subtracts values from the input number until it is reduced to zero.

Method 2: List Comprehension with Zip

This method uses list comprehension and the zip function in Python to iterate through a series of tuples that hold numeral values and symbols concurrently, thus improving readability and compactness.

Here’s an example:

def int_to_roman(num):
    roman_literals = zip(
        [1000, 900, 500, 400, 100,  90,  50,  40,  10,   9,   5,   4,   1],
        ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"]
    )
    result = ""
    for value, symbol in roman_literals:
        while num >= value:
            result += symbol
            num -= value
    return result

print(int_to_roman(58))

Output: LVIII

The function int_to_roman() maps each numeral’s value to its corresponding symbol using the zip function, then builds the Roman numeral using a while loop and list comprehension.

Method 3: Recursive Approach

Recursion is used to solve the problem by breaking it down into smaller, identical subproblems. This approach tends to be conceptually simple and easy to understand.

Here’s an example:

def int_to_roman(num, roman_string=''):
    if num > 999: return int_to_roman(num-1000, roman_string+'M')
    if num > 899: return int_to_roman(num-900, roman_string+'CM')
    if num > 499: return int_to_roman(num-500, roman_string+'D')
    ... # other conditions would follow
    return roman_string

print(int_to_roman(3))

Output: III

The recursive int_to_roman() function calls itself with an updated integer value and a forming Roman numeral string. Each call handles a decrement of value and an addition of the corresponding symbol.

Method 4: Optimized String Building

This method focuses on optimized string concatenation using a StringBuilder-like approach, which can be more efficient than regular string concatenation in a loop.

Here’s an example:

def int_to_roman(num):
    roman_parts = []
    numeral_map = zip(
        [1000, 900, 500, 400, 100,  90,  50,  40,  10,   9,   5,   4,   1],
        ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"]
    )
    for value, symbol in numeral_map:
        count, num = divmod(num, value)
        roman_parts.append(symbol * count)
    return ''.join(roman_parts)

print(int_to_roman(1649))

Output: MDCXLIX

By using a list roman_parts to accumulate the Roman numeral parts and then joining them at the end, the code avoids the overhead of creating intermediate strings in each iteration.

Bonus One-Liner Method 5: Compact List Comprehension

For those who prefer concise one-liners, this method employs a one-liner list comprehension that elegantly achieves the conversion with minimal code.

Here’s an example:

print(''.join(sym * (num // val) for val, sym in zip(
    [1000, 900, 500, 400, 100,  90,  50,  40,  10,   9,   5,   4,   1],
    ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"])) for num in [2023]))

Output: MMXXIII

This one-liner uses nested list comprehensions and the zip function to generate the Roman numeral for the input number inside a print statement with no need for a function definition.

Summary/Discussion

  • Method 1: Hardcoded Dictionaries. Straightforward and explicit. Can be verbose.
  • Method 2: List Comprehension with Zip. Clean and Pythonic. Requires understanding zip.
  • Method 3: Recursive Approach. Elegant and simple base case construction. Might be less efficient for large numbers due to maximum recursion depth.
  • Method 4: Optimized String Building. Efficient string manipulation. Slightly more complex to understand at first.
  • Method 5: Compact List Comprehension. Extremely concise. Potentially difficult for beginners to read and understand.