整数をローマ数字に
整数をローマ数字に
このレッスンで分かること
タプルのリストで値表を作り、forとwhileの二重ループで処理します- 基本記号は
I=1,V=5,X=10,L=50,C=100,D=500,M=1000の 7 つでした- そこで、
減算表記を 1 つの記号として扱う ように 13 個の値表を作ります
整数をローマ数字に とは
1〜3999 の整数を
貪欲法でローマ数字に変換する。減算表記を含む 13 個の値表でグリーディに削っていく。
前のレッスンで ローマ数字 → 整数 の変換を扱いました。今回は逆向きで、整数 → ローマ数字 の変換に挑戦します。やってみると分かりますが、こちらの方が少し設計力が問われます。鍵は 貪欲法 (Greedy) の発想と、減算表記 を含む 13 個の値表を使うことです。
整数 → ローマ数字は大きい記号から順に貪欲に削っていくのがコツ。減算表記も 1 つの記号として扱う。
値表をどう作るか
基本記号は I=1, V=5, X=10, L=50, C=100, D=500, M=1000 の 7 つでした。しかし、これだけだと 4 を IIII のように繰り返してしまいます。ローマ数字 の規約では同じ記号は 3 回まで までしか並べられないので、4 は IV, 9 は IX のように 減算表記 を使います。
そこで、減算表記 を 1 つの記号として扱う ように 13 個の値表を作ります。
プレーンテキスト
1000 → M
900 → CM
500 → D
400 → CD
100 → C
90 → XC
50 → L
40 → XL
10 → X
9 → IX
5 → V
4 → IV
1 → Iこの順序で 大きい順 に並べることが重要です。
貪欲アルゴリズム
アルゴリズム本体はとてもシンプルです。
図のポイント (テキスト併記)
- 値表を上から見て、
num以下の最初の値が見つかったらその記号を結果に追加し、numから差し引く
値表を上から見て、num 以下の最初の値が見つかったらその 記号 を結果に追加し、num から差し引く。これを num == 0 まで繰り返すだけです。
Python での実装
Python
def intToRoman(num):
values = [
(1000, 'M'), (900, 'CM'), (500, 'D'), (400, 'CD'),
(100, 'C'), (90, 'XC'), (50, 'L'), (40, 'XL'),
(10, 'X'), (9, 'IX'), (5, 'V'), (4, 'IV'), (1, 'I'),
]
result = []
for value, symbol in values:
while num >= value:
result.append(symbol)
num -= value
return ''.join(result)タプルのリスト で値表を作り、for と while の二重ループで処理します。外側は値表を 13 回回り、内側は同じ記号を最大 3 回 (3000 の M なら 3 回) 使うので、合計反復回数は 定数オーダー です。実質 O(1) と言えます。
JavaScript での実装
JavaScript
function intToRoman(num) {
const values = [
[1000, 'M'], [900, 'CM'], [500, 'D'], [400, 'CD'],
[100, 'C'], [90, 'XC'], [50, 'L'], [40, 'XL'],
[10, 'X'], [9, 'IX'], [5, 'V'], [4, 'IV'], [1, 'I'],
];
let result = '';
for (const [value, symbol] of values) {
while (num >= value) {
result += symbol;
num -= value;
}
}
return result;
}Java でも int[][] と String[] の 2 つで持つか、int[] + 文字列配列のペアで持つかのお好みで実装します。
貪欲法 が成立する理由
値表に
IV = 4,IX = 9等を入れたことで、「大きい順に削っても表現ミスにならない」性質が保証される。
貪欲法は一般には最適解を保証しないが、値表が綺麗にネスト構造になっているケースでは最適解になる。ローマ数字の値表はその好例。
もし 減算表記 を入れず I=1, V=5, ... の 7 個だけにすると、4 のときに V - I = 4 という発想が必要になり、貪欲法 ではうまく解けません。減算表記 を 1 個の記号として扱うことで 貪欲法 が成立するわけです。
よくある間違い
- 値表の順序を間違える。
大きい順にしないと1000をD + Dのように 2 個書いてしまう。 減算表記を入れ忘れる。4がIIIIになる。whileをifにしてしまい、3000 = MMMのような連続記号が出ない。
やってみよう
intToRoman(num) を実装してください。num は 1 から 3999 までの整数です。貪欲法 と 13 個の値表を使い、対応する ローマ数字文字列 を返してください。
よくある質問
Q. このトピックは実務でどう役立ちますか?
A. DB のクエリ最適化、API 設計、データ構造の選択など、設計判断の根拠になります。表面的にライブラリを使うだけでなく「なぜそれが速いのか」を理解できると、性能問題を未然に防げます。コーディング面接でも頻出のテーマです。
Q. 計算量はどう求めれば良いですか?
A. ループのネストごとに掛け算する、再帰なら漸化式から解く、というのが基本です。Big-O 表記は定数倍と低次の項を無視するため、n の指数(n²、n log n など)に注目してください。最悪・平均・最良の 3 つを意識すると説得力が増します。
Q. 覚えるべき定番アルゴリズムは何ですか?
A. 二分探索、クイックソート/マージソート、BFS/DFS、ダイクストラ、DP の基本問題(フィボナッチ・ナップサック)が必修です。これらを「白紙から書ける」状態にすると、応用問題が一気に解けるようになります。
次のレッスン
次は 雨水を溜める で、配列上の各位置に溜まる雨水量を 双方向ポインタ で求める手法を学びます。
事前確認 — 進む前に次の 3 つができることを確認しましょう。
- 整数→ローマ数字 の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. 整数→ローマ数字 とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン
要件
- 関数
intToRoman(num)を実装し、ローマ数字文字列を返す 減算表記を含む 13 個の値表を使い、貪欲法で削っていく- 値表は
大きい順に並べ、while num >= valueで同じ記号を必要回数追加する
入出力例
test-cases.txt
intToRoman(1) → "I"
intToRoman(3) → "III"
intToRoman(4) → "IV"
intToRoman(9) → "IX"
intToRoman(58) → "LVIII"
intToRoman(1994) → "MCMXCIV"
intToRoman(3999) → "MMMCMXCIX"