Skip to content

Instantly share code, notes, and snippets.

@pwaller
Forked from davidtweaver/Numbers to Roman Numerals
Last active December 18, 2015 08:09
Show Gist options
  • Save pwaller/5752073 to your computer and use it in GitHub Desktop.
Save pwaller/5752073 to your computer and use it in GitHub Desktop.
#! /usr/bin/env python
"""
Convert roman numerals. To test: `nosetests roman.py`.
"""
import nose.tools
KNOWN_VALUES = [
(1, 'I'),
(5, 'V'),
(10,'X'),
(50,'L'),
(100,'C'),
(500,'D'),
(1000,'M'),
(2, "II"),
(3, "III"),
(4, "IV"),
(200,"CC"),
(9, "IX"),
(19, "XIX"),
(49, "XLIX"),
(99, "XCIX"),
(207, "CCVII"),
(1904, "MCMIV"),
(1954, "MCMLIV"),
(2013, "MMXIII"),
(400, "CD"),
(900, "CM"),
(10000, "MMMMMMMMMM"),
]
SYMBOLS = [
(1, 'I'),
(5, 'V'),
(10,'X'),
(50,'L'),
(100,'C'),
(500,'D'),
(1000,'M'),
]
def convert(number):
big_string = number*'I'
return big_string_to_small(big_string)
def big_string_to_small(big_string):
"""
>>> big_string_to_small('I'*1000)
'M'
>>> big_string_to_small('I'*500)
'D'
>>> big_string_to_small('I'*100)
'C'
>>> big_string_to_small('I'*105)
'CV'
"""
result = big_string
for (number, text) in reversed(SYMBOLS):
if number*'I' in result:
# Replace all runs of "I" with the appropriate symbol
result = result.replace(number*'I',text)
return replace_repeats(result)
def replace_repeats(roman):
"""
>>> replace_repeats('IIII')
'IV'
>>> replace_repeats('XXXX')
'XL'
>>> replace_repeats(convert(404))
'CDIV'
"""
result = roman
# iterate over all of the symbols, looking two ahead each iteration
symbols = zip(SYMBOLS, SYMBOLS[1:], SYMBOLS[2:])
for (_, smaller), (_, bigger), (_, BIGGER) in symbols:
# Substitute four consecutive symbols (IIII) for a more simple
# representation (IV).
if 4*smaller in result:
target = "{}{}".format(smaller, bigger)
result = result.replace(4*smaller, target)
# If a pattern like "VIV" exists, there is a more compact
# representation: IX.
aba = "{0}{1}{0}".format(bigger, smaller)
if aba in result:
target = "{}{}".format(smaller, BIGGER)
result = result.replace(aba, target)
result = replace_repeats(result)
return result
def test_known_value():
for input_, output in KNOWN_VALUES:
yield _check_known_value, input_, output
def _check_known_value(input_, output):
nose.tools.eq_(convert(input_), output)
@fawkesley
Copy link

Awesome!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment