Skip to content

Instantly share code, notes, and snippets.

@kanet77
Last active October 22, 2018 13:48
Show Gist options
  • Save kanet77/8819243 to your computer and use it in GitHub Desktop.
Save kanet77/8819243 to your computer and use it in GitHub Desktop.
Convert from Decimal to Roman Numerals in Ruby
class RomanNumerals
RN = {
1000 => 'M',
500 => 'D',
100 => 'C',
50 => 'L',
10 => 'X',
5 => 'V',
1 => 'I'
}
def self.to_roman(decimal)
return nil if decimal > 3000 || decimal < 1
return to_roman_loop(decimal)
# return to_roman_recursive(decimal, 1000)
end
def self.to_roman_loop(dec)
roman_base = 1000
result = ''
while (dec > 0) do
factor = dec/roman_base
result << multiple_of(factor, roman_base)
dec -= (factor*roman_base)
roman_base /= 10
end
result
end
def self.to_roman_recursive(dec, roman_base)
return '' if dec < 1 || roman_base < 1
return multiple_of(dec/roman_base, roman_base) + to_roman_recursive(dec % roman_base, roman_base/10)
end
def self.multiple_of(factor, roman_base)
# p "#{factor}, #{roman_base}"
case factor
when 0
''
when 1..3
RN[roman_base]*factor
when 4
RN[roman_base]+RN[roman_base*5]
when 5
RN[roman_base*5]
when 6..8
RN[roman_base*5]+(RN[roman_base]*(factor-5))
when 9
RN[roman_base] + RN[roman_base*10]
else
raise "Nonsense! Factor is #{factor}. Should be in (0..9)."
end
end
end
require_relative 'roman'
describe RomanNumerals, '#to_roman' do
it 'returns nil for numbers not in (1..3000)' do
RomanNumerals.to_roman(0).should be_nil
RomanNumerals.to_roman(3001).should be_nil
end
it 'handles powers of ten' do
RomanNumerals.to_roman(1000).should eq 'M'
RomanNumerals.to_roman(100).should eq 'C'
RomanNumerals.to_roman(10).should eq 'X'
RomanNumerals.to_roman(1).should eq 'I'
end
it 'handles 5x powers of ten' do
RomanNumerals.to_roman(500).should eq 'D'
RomanNumerals.to_roman(50).should eq 'L'
RomanNumerals.to_roman(5).should eq 'V'
end
it 'handles (1..10) -> (I..X)' do
RomanNumerals.to_roman(1).should eq 'I'
RomanNumerals.to_roman(2).should eq 'II'
RomanNumerals.to_roman(3).should eq 'III'
RomanNumerals.to_roman(4).should eq 'IV'
RomanNumerals.to_roman(5).should eq 'V'
end
it 'is not afraid of larger, more complex numbers' do
RomanNumerals.to_roman(1221).should eq 'MCCXXI'
RomanNumerals.to_roman(43).should eq 'XLIII'
RomanNumerals.to_roman(55).should eq 'LV'
RomanNumerals.to_roman(2222).should eq 'MMCCXXII'
RomanNumerals.to_roman(5).should eq 'V'
end
it "knows how to party like it's 1999" do
RomanNumerals.to_roman(1999).should eq 'MCMXCIX'
end
end
@kanet77
Copy link
Author

kanet77 commented Feb 5, 2014

Use to_roman(decimal) to convert a decimal number into roman numerals. (e.g. to convert 2222, call RomanNumerals.to_roman(2222).)

This method can only handle numbers in the range (1..3000). After guarding against numbers outside that range, to_roman() returns either to_roman_loop(decimal) or to_roman_recursive(decimal, 1000). (Note: there is no attempt to disallow non-integer or nil arguments.)

Both the loop and recursive functions utilize the multiple_of(factor, roman_base) function. They handle each decimal digit in turn by starting with the largest supported power of ten (1000, in this case) and dividing by ten with each iteration. Perhaps "roman_base" here could be renamed "power_of_ten" since it will always be one of [1000, 100, 10, 1].

The multiple_of() function finds a suitable way to represent a multiple of the roman_base. It references the RN lookup table to find the character for each the roman_base and, if necessary, 5 * roman_base.

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