roman numerals

\![Ubuntu-20.04.1](<https://img.shields.io/badge/Ubuntu-20.04.1-brightgreen>) \![ruby-2.7.1p83](<https://img.shields.io/badge/ruby-2.7.1p83-brightgreen>) 


# お題

アラビア数字(arabic numerals)を受け取って，ローマ数字(roman numerals)を返すmethodを書きなさい


# ローマ数字は記号の組み合わせ

ローマ数字は1,5,10,50,100,500,1000を1文字で表現できるらしく、その組み合わせで数字を表現する<br>じゃあ、受け取った数字を大きい数から割っていったらいいだけじゃん、と思ってまずは一気に書き上げた

```ruby
def roman_numerals(num)
  m, modulo = div(num,1000)
  d, modulo = div(modulo,500)
  c, modulo = div(modulo,100)
  l, modulo = div(modulo,50)
  x, modulo = div(modulo,10)
  v, modulo = div(modulo,5)

  val = 'M' * m + 'D' * d + 'C' * c + 'L' * l + 'X' * x + 'V' * v + 'I' * modulo
  return val
end

def div(num, waru)
  div = num / waru
  modulo = num % waru

  return div, modulo
end

[1,2,4,5,6,9,10,11,14,15,19,38,42,49,51,97,99,439,483,499,732,961,999,1999].each do |number|
  puts "#{number} : #{roman_numerals(number)}"
end
```

結果

```ruby
1 : I
2 : II
4 : IIII
5 : V
6 : VI
9 : VIIII
10 : X
11 : XI
14 : XIIII
15 : XV
19 : XVIIII
38 : XXXVIII
42 : XXXXII
49 : XXXXVIIII
51 : LI
97 : LXXXXVII
99 : LXXXXVIIII
439 : CCCCXXXVIIII
483 : CCCCLXXXIII
499 : CCCCLXXXXVIIII
732 : DCCXXXII
961 : DCCCCLXI
999 : DCCCCLXXXXVIIII
1999 : MDCCCCLXXXXVIIII
```

これで完成と思うじゃないですか。ところがそうはいかないのがローマ数字<br>3はIII、38はXXXVIIIなのはいい。しかし、4はIV、9はIX、99はXCIXなのである。誰だよこんなめんどくさい数字思いついた奴は


# 4と9は例外

プログラムというよりローマ数字の勉強になってきたが、このややこしいルールを理解するキーは「4」と「9」である。上述のとおり4がIV(5-1)、9がIX(10-1)。4と9はVやXという基本の数字の左側に端数のIが来て、基本の数字から端数を牽くという構造になってる<br>なので、14はXIV(10+(5-1))、42はXLII((50-10)+2)、49はXLIX((50-10)+(10-1))。49はIL(50-1)じゃないことに注意。同様に99はIC(100-1)ではなくXCIX((100-10)+(10-1))なのである。もーややこしい<br>つまり、一桁一桁ごとに計算していけばなんとかなりそうだ

```ruby
def roman_numerals(num)
  m, modulo  = calcDiv(num,1000)
  d, modulo, cm = calcDiv(modulo,500)
  c, modulo, cd = calcDiv(modulo,100)
  l, modulo, xc = calcDiv(modulo,50)
  x, modulo, xl = calcDiv(modulo,10)
  v, modulo, ix = calcDiv(modulo,5)
  i, modulo, iv = calcDiv(modulo,1)

  val = 'M' * m + 'CM' * cm + 'D' * d + 'CD' * cd + 'C' * c + 'XC' * xc + 'L' * l + 'XL' * xl + 'X' * x + 'IX' * ix + 'V' * v + 'IV' * iv + 'I' * i
  return val
end

def calcDiv(num, waru)
  div = num / waru
  modulo = num % waru
  reigai = 0

  if waru == 500
    if div == 1 and num - 900 >= 1
      div = 0
      reigai = 1
    end
  elsif waru == 100
    if div == 4
      div = 0
      reigai = 1
    end
  elsif waru == 50
    if div == 1 and num - 90 >= 1
      div = 0
      reigai = 1
    end
  elsif waru == 10
    if div == 4
      div = 0
      reigai = 1
    end
  elsif waru == 5
    if div == 1 and num == 9
      div = 0
      reigai = 1
    end
  elsif waru == 1
    if div == 4
      div = 0
      reigai = 1
    end
  end

  return div, modulo, reigai
end
```

汚いコードだが、reigaiという変数が4や9の例外的な表記をするかどうかの判定用の変数とした。とりあえずこれで条件を分けられたでしょう。実行。

```ruby
1 : I
2 : II
4 : IV
5 : V
6 : VI
9 : IXIV
10 : X
11 : XI
14 : XIV
15 : XV
19 : XIXIV
38 : XXXVIII
42 : XLII
49 : XLIXIV
51 : LI
97 : XCXLVII
99 : XCXLIXIV
439 : CDXXXIXIV
483 : CDLXXXIII
499 : CDXCXLIXIV
732 : DCCXXXII
961 : CMCDLXI
999 : CMCDXCXLIXIV
1999 : MCMCDXCXLIXIV
```

ん？なんか色々おかしいぞ。おかしいのは9、19、49、97、99、439、499、961、999、1999。つまり9絡みで何かが起きてる。<br>よく見ると、9はIXが出てほしいが、IVというのが余計についてる。19も同じく。つまり4が余分についてるんだな。<br>プログラムを見直すとなるほど、例えば7行目でcalcDivを呼び出して、9の時は例外的な表記(IX)しますよーとしてるのはいいのだけど、それで処理を終えるべきところを次の行に進んでまたcalcDivを呼んでしまい、9%5の答えは4だからIVを表記しなければ！となっているなので、roman\_numeralsのvalの前に

```ruby
 if cm == 1
   cd = 0
 end
 if xc == 1
   xl = 0
 end
if ix == 1
   iv = 0
 end
```

を追加して、それぞれの位で9を表記することになったら、4はもう表記するなと定義してやった。すると

```ruby
1 : I
2 : II
4 : IV
5 : V
6 : VI
9 : IX
10 : X
11 : XI
14 : XIV
15 : XV
19 : XIX
38 : XXXVIII
42 : XLII
49 : XLIX
51 : LI
97 : XCVII
99 : XCIX
439 : CDXXXIX
483 : CDLXXXIII
499 : CDXCIX
732 : DCCXXXII
961 : CMLXI
999 : CMXCIX
1999 : MCMXCIX
```

と、ちゃんと出力できた。どうだ古代ローマ人見たか。refactoringとかはもう体力がないので一旦ここで勘弁してください


# 参考記事

-   [roman numerals](<https://qiita.com/daddygongon/items/2d0a73a51ddab2d9da1b>)



------
 - **source** ~/MasahiroOba/grad_members_20f/members/MasahiroOba/roman_numerals.org