Convert numbers to Roman numerals in Ruby

!macOS-11.1!ruby-2.7.2p137

Preface (Introduction)

I had the task of creating a method or class that receives Arabic numerals and returns Roman numerals. Let's solve it using Ruby.

Roman numerals

I only see Roman numerals like fashionable watches. How do you represent numbers over 12?

The correspondence table between Arabic numerals and Roman numerals is shown below with reference to Roman numerals & # x2013; Wikipedia.

Arabic numerals Roman numerals
1 I
5 V
10 X
50 L
100 C
500 D
1000 M

The numbers in each digit are decomposed into the sum of the above numbers and written in descending order. For example, 3 is III, 2020 is MMXX.

However, Roman numerals have troublesome rules, Generally, when representing numbers like 4 and 99, they are not represented like IIII and LXXXXVIIII.

Such numbers follow the rule of subtraction, which writes a small number to the left of a large number to mean subtracting from right to left. Specifically, 4 is IV and 99 is XCIX. Masu.

Also, due to these rules, Roman numerals can only represent values ​​from 1 to 3999. (Did the ancient Romans have trouble with this usage?)

Ruby

From the information so far, let's implement the conversion from Arabic numerals to Roman numerals in Ruby.

If you implement it without any twist, you can do it by using a lot of conditional branches. However, such a program will be hard to see, and over time it will become a moving swelling-like program.

So, I'm going to make a Ruby program with a little ingenuity.

law

Finding the law is the way to make a program that is easy to read. If you create a method using the law, you can use it to create an easy-to-read program.

So what is the law of Roman numerals?

Rough law

Looking at the correspondence table above, the letters of the Roman numerals change five times as many as each number, and then when the number is twice as many, it repeats alternately (although up to three times).

From this, we can find out which Roman numeral should be converted by finding the quotient and remainder of 5 and 2 for each digit. The following shows the behavior when the Arabic numeral is 87. (Sorry for the confusing explanation and table & # x2026;)

divisor quotient Surplus Roman numerals to convert
5 17 2 I
2 8 1 V
5 1 3 X
2 0 1 L
5 0 0 C
2 0 0 D
0 M

From the table, the Arabic numeral 87 can be expressed as L1, X3, V1, I2, that is, ** LXXXVII **. This is the correct notation.

I will implement it based on this

class Integer
  def to_roman
    @@arabic = self
    @@roman = ''
    @@a2r = []
    @@symbol = ['I', 'V', 'X', 'L', 'C', 'D', 'M']

    for i in 1..3 do
      devide(5)
      devide(2)
    end
    for i in 1..6 do
      @@roman = @@symbol[i]*@@a2r[i] + @@roman
    end
    @@roman = @@symbol[-1]*@@arabic + @@roman
  end

  def devide n
    @@a2r << @@arabic % n
    @@arabic /= n
  end
end

Detailed law

However, if the above rule is adopted as it is, IIII will be returned at 4. So we also have to find a law that corresponds to the subtraction law.

First, let's think about ** concrete numbers **.

It's easy to think of it as a single digit number, so let's consider the Roman numeral conversion between 4 and 9. If the Arabic numeral to be converted is 4, it will be IV, and if it is 9, it will be IX.

Did you notice that the first Roman numeral is ** I ** in common? Also, the difference between the two is the Roman numeral behind, ** V ** which represents 5 in 4, and ** X ** which represents 10 in 9.

Let's treat it with numbers to make it easier to find the law. What they have in common in numbers is that both 4 and 9 have a remainder of 5 of 4, and the difference is that the quotient of 5 is 0 for 4 and 1 for 9.

This seems to be able to be incorporated into the above rough law. More generalized, if the remainder of 5 for each digit is 4,

--If the remainder of the next 2 is 0, the Roman numeral on the right is the Roman numeral one before the Roman numeral on the left (e.g. I → V). --If the remainder of the next 2 is 1, the Roman numeral on the right is the Roman numeral one before the Roman numeral on the left (e.g. I → X).

Will be.

Now, let's implement it by incorporating this detailed law.

Code

roman_number.rb


class Integer
  def to_roman
    @@arabic = self
    @@roman = ''
    @@a2r = []
    @@symbol = ['I', 'V', 'X', 'L', 'C', 'D', 'M']

    for i in 0..2 do
      devide(5)
      devide(2)
      k = 2*i
      if @@a2r[k] == 4
        tmp = @@a2r[k+1]
        @@roman = @@symbol[k] + @@symbol[k+1+tmp] + @@roman
      else
        @@roman = @@symbol[k+1]*@@a2r[k+1] + @@symbol[k]*@@a2r[k] + @@roman
      end
    end
    @@roman = @@symbol[-1]*@@arabic + @@roman
  end

  def devide n
    @@a2r << @@arabic % n
    @@arabic /= n
  end
end

It's longer than I expected, but I managed to implement it.

Test

Since it is troublesome to check manually, I would like to use the assert \ _equal function created in this article for testing.

Below is the test code

test_rn.rb


require './roman_number'
require './assert_equal'

[
  ['I', 1],
  ['II', 2],
  ['III', 3],
  ['IV', 4],
  ['V', 5],
  ['VI', 6],
  ['IX', 9],
  ['X', 10],
  ['XI', 11],
  ['XIV', 14],
  ['XV', 15],
  ['XIX', 19],
  ['XXXVIII', 38],
  ['XLII', 42],
  ['XLIX', 49],
  ['LI', 51],
  ['XCVII', 97],
  ['XCIX', 99],
  ['CDXXXIX', 439],
  ['CDLXXXIII', 483],
  ['CDXCIX', 499],
  ['DCCXXXII', 732],
  ['CMLXI', 961],
  ['CMXCIX', 999],
  ['MCMXCIX', 1999]
].each do |expected, index|
  assert_equal(expected, index.to_roman)
end

Let's test it.

> ruby test_rn.rb

Now the output is & # x2026;

expected :: I
result   :: I
succeeded in assert_equal.
expected :: II
result   :: II
succeeded in assert_equal.
expected :: III
result   :: III
succeeded in assert_equal.
expected :: IV
result   :: IV
succeeded in assert_equal.
expected :: V
result   :: V
succeeded in assert_equal.
expected :: VI
result   :: VI
succeeded in assert_equal.
expected :: IX
result   :: IX
succeeded in assert_equal.
expected :: X
result   :: X
succeeded in assert_equal.
expected :: XI
result   :: XI
succeeded in assert_equal.
expected :: XIV
result   :: XIV
succeeded in assert_equal.
expected :: XV
result   :: XV
succeeded in assert_equal.
expected :: XIX
result   :: XIX
succeeded in assert_equal.
expected :: XXXVIII
result   :: XXXVIII
succeeded in assert_equal.
expected :: XLII
result   :: XLII
succeeded in assert_equal.
expected :: XLIX
result   :: XLIX
succeeded in assert_equal.
expected :: LI
result   :: LI
succeeded in assert_equal.
expected :: XCVII
result   :: XCVII
succeeded in assert_equal.
expected :: XCIX
result   :: XCIX
succeeded in assert_equal.
expected :: CDXXXIX
result   :: CDXXXIX
succeeded in assert_equal.
expected :: CDLXXXIII
result   :: CDLXXXIII
succeeded in assert_equal.
expected :: CDXCIX
result   :: CDXCIX
succeeded in assert_equal.
expected :: DCCXXXII
result   :: DCCXXXII
succeeded in assert_equal.
expected :: CMLXI
result   :: CMLXI
succeeded in assert_equal.
expected :: CMXCIX
result   :: CMXCIX
succeeded in assert_equal.
expected :: MCMXCIX
result   :: MCMXCIX
succeeded in assert_equal.

All successful! You got the results you expected.

Reference material

Recommended Posts

Convert numbers to Roman numerals in Ruby
Find Roman numerals in Ruby
[Ruby] How to batch convert strings in an array to numbers
Convert to Ruby Leet string
[Ruby] Find numbers in arrays
I wrote a code to convert numbers to romaji in TDD
Convert ruby object to JSON format
How to iterate infinitely in Ruby
Try to implement Yubaba in Ruby
How to install Bootstrap in Ruby
roman numerals
[Ruby] How to count even or odd numbers in an array
roman numerals
roman numerals
[Ruby] I want to put an array in a variable. I want to convert to an array
roman numerals
roman numerals
roman numerals
Roman Numerals
roman numerals
roman numerals
How to get date data in Ruby
Convert request parameter to Enum in Spring
Convert SVG files to PNG files in Java
Convert an array of strings to numbers
Convert JSON to TSV and TSV to JSON with Ruby
[Ruby] Returns characters in a pyramid shape according to the entered numbers
[Ruby] How to convert from lowercase to uppercase and from uppercase to lowercase
Introduction to Ruby 2
[Ruby] Basic key to be strong in refactoring
Convert to a tag to URL string in Rails
[Ruby] How to convert CSV file to Yaml (Yml)
[Ruby] Learn how to use odd? Even? And count the even and odd numbers in the array!
I want to use arrow notation in Ruby
Class in Ruby
Ruby's "1101" .to_i (2) does not convert decimal numbers to decimal numbers.
Heavy in Ruby! ??
[Ruby on Rails] How to install Bootstrap in Rails
How to build the simplest blockchain in Ruby
Ruby How to convert between uppercase and lowercase
EX1: roman numerals
How to implement Pagination in GraphQL (for ruby)
I want to get the value in Ruby
[Ruby] How to use standard output in conditional branching
Sample code to convert List to List <String> in Java Stream
[Ruby on Rails] How to write enum in Japanese
Calculate the difference between numbers in a Ruby array
Converting TSV files to CSV files (with BOM) in Ruby
[Ruby On Rails] How to reset DB in Heroku
How to launch another command in a Ruby program
How to convert A to a and a to A using AND and OR in Java
How to handle TSV files and CSV files in Ruby
How to convert a file to a byte array in Java
roman numerals (I tried to simplify it with hash)
How to resolve SSL_connect error in PayPal Ruby SDK
Safe numbers (ruby edition)
To debug in eclipse
Ruby Learning # 9 Math & Numbers
From Java to Ruby !!
Output triangle in Ruby
Variable type in ruby