!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