LoginSignup
1
0

More than 3 years have passed since last update.

roman numerals

Posted at

!Ubuntu-20.04.1 !ruby-2.7.1p83

お題

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

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

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

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

結果

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

これで完成と思うじゃないですか。ところがそうはいかないのがローマ数字
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が来て、基本の数字から端数を牽くという構造になってる
なので、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))なのである。もーややこしい
つまり、一桁一桁ごとに計算していけばなんとかなりそうだ

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の例外的な表記をするかどうかの判定用の変数とした。とりあえずこれで条件を分けられたでしょう。実行。

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絡みで何かが起きてる。
よく見ると、9はIXが出てほしいが、IVというのが余計についてる。19も同じく。つまり4が余分についてるんだな。
プログラムを見直すとなるほど、例えば7行目でcalcDivを呼び出して、9の時は例外的な表記(IX)しますよーとしてるのはいいのだけど、それで処理を終えるべきところを次の行に進んでまたcalcDivを呼んでしまい、9%5の答えは4だからIVを表記しなければ!となっているなので、roman_numeralsのvalの前に

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

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

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とかはもう体力がないので一旦ここで勘弁してください

参考記事


  • source ~/MasahiroOba/grad_members_20f/members/MasahiroOba/roman_numerals.org
1
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0