Roman Numeral Programmer's Golf

Challenge: Write a Python program to convert numbers to Roman numerals in as few characters as possible.

I’ve been going through old issues of Linux Voice, and they had a challenge for programmer’s golf (use as few characters as possible). The requirements of this challenge:

  • Write a Python program that takes an input, and converts it to Roman numerals
  • You are not allowed to import anything
  • Python 2 or 3 is allowed


So, my final product:

print ''.join(map(lambda d,a,b,c:['',a,a*2,a*3,a+b,b,b+a,b+2*a,b+3*a,a+c][int(d)],
raw_input().zfill(4),'MCXI','*DLV','*MCX'))

This only works into the low thousands, as Roman numerals beyond that are a bit ugly.

The Idea

The strategy is to translate each digit. For example, 317 becomes CCCXVII, so I approached this as:

  • 3 -> CCC
  • 1 -> X
  • 7 -> VII


If you look at what happens by each digit based on the place value (e.g. 7, 70, 700), you’ll notice something useful:

Digit Ones Tens Hundreds
1 I X C
2 II XX CC
3 III XXX CCC
4 IV XL CD
5 V L D
6 VI LX DC
7 VII LXX DCC
8 VIII LXXX DCCC
9 IX XC CM

What’s important? They all follow the same pattern. The only thing that is different is the set of numerals they’re using - either IVX, XLC, or CDM.

The Implementation

map(...)

The core Python function used is map. This function take two or more arguments; the first is a function, and the remaining arguments are sequences. It will return a sequence that has the same length as the sequences in the arguments, where each element is the result of calling the function from the first argument with the nth element of each sequence as its arguments.

lambda d,a,b,c:['',a,a*2,a*3,a+b,b,b+a,b+2*a,b+3*a,a+c][int(d)]

The function I pass is a lambda function. It takes four arguments; the first is the digit to be translated, and the remaining arguments are the characters to use (IVX, XLC, or CDM).

...,'MCXI','*DLV','*MCX'...

The arguments are grouped so that the first element of each group goes together, the second elements, and so on. This looks odd, but think about how the map function works.

raw_input().zfill(4)

This obtains the number to translate from the user, and left pads it with zeros to ensure place value is appropriately dealt with.

''.join(...)

This function takes a sequence of arguments and joins them as a string with '' as the delimiter. This is what puts the different numerals together.

Reflection

This was my first programmer’s golf challenge, and I’m going to try to do a few more. Beyond just being fun, I learned how the map function works - something I’ve been meaning to figure out. More importantly, it made me think about the problem in many different ways.