整数をローマ数字に

生田 陸人
LuaGate エンジニア / 現役エンジニア
編集 LuaGate編集部

整数をローマ数字に

このレッスンで分かること

  • タプルのリスト で値表を作り、forwhile の二重ループで処理します
  • 基本記号は 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 つでした。しかし、これだけだと 4IIII のように繰り返してしまいます。ローマ数字 の規約では同じ記号は 3 回まで までしか並べられないので、4IV, 9IX のように 減算表記 を使います。

そこで、減算表記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

この順序で 大きい順 に並べることが重要です。

貪欲アルゴリズム

アルゴリズム本体はとてもシンプルです。

diagram (will load when visible)

図のポイント (テキスト併記)

  • 値表を上から見て、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)

タプルのリスト で値表を作り、forwhile の二重ループで処理します。外側は値表を 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 個の記号として扱うことで 貪欲法 が成立するわけです。

よくある間違い

  • 値表の順序を間違える。大きい順 にしないと 1000D + D のように 2 個書いてしまう。
  • 減算表記 を入れ忘れる。4IIII になる。
  • whileif にしてしまい、3000 = MMM のような連続記号が出ない。

やってみよう

intToRoman(num) を実装してください。num1 から 3999 までの整数です。貪欲法 と 13 個の値表を使い、対応する ローマ数字文字列 を返してください。

よくある質問

Q. このトピックは実務でどう役立ちますか?

A. DB のクエリ最適化、API 設計、データ構造の選択など、設計判断の根拠になります。表面的にライブラリを使うだけでなく「なぜそれが速いのか」を理解できると、性能問題を未然に防げます。コーディング面接でも頻出のテーマです。

Q. 計算量はどう求めれば良いですか?

A. ループのネストごとに掛け算する、再帰なら漸化式から解く、というのが基本です。Big-O 表記は定数倍と低次の項を無視するため、n の指数(n²、n log n など)に注目してください。最悪・平均・最良の 3 つを意識すると説得力が増します。

Q. 覚えるべき定番アルゴリズムは何ですか?

A. 二分探索、クイックソート/マージソート、BFS/DFS、ダイクストラ、DP の基本問題(フィボナッチ・ナップサック)が必修です。これらを「白紙から書ける」状態にすると、応用問題が一気に解けるようになります。

次のレッスン

次は 雨水を溜める で、配列上の各位置に溜まる雨水量を 双方向ポインタ で求める手法を学びます。

事前確認 — 進む前に次の 3 つができることを確認しましょう。

  1. 整数→ローマ数字 の要点を自分の言葉で説明できる
  2. このレッスンの最小コード (または操作手順) を見ずに書ける
  3. 練習問題やクイズで間違えた箇所を読み直して理解した

理解度チェック (30 秒)

Q. 整数→ローマ数字 とは何か、1 文で説明してください。

この章のポイント

A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。

関連レッスン

要件

  1. 関数 intToRoman(num) を実装し、ローマ数字文字列 を返す
  2. 減算表記 を含む 13 個の値表を使い、貪欲法 で削っていく
  3. 値表は 大きい順 に並べ、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"

ヒント

main.py
main.py
学習モード

メモ

整数をローマ数字に

⌘S で保存