ローマ数字をどこまでを寛容するのか、なかなか厄介な問題ではある。
ネット上にはローマ数字とアラビア数字の対照表や、変換プログラムなどが溢れているが、私のお眼鏡にかなうものを見つけるに至らなかった。
例えば、8をローマ数字にすると、VIIIとなる。
しかし、私はIIXも8だと考えている。
例えば、999をローマ数字にすると、CMXCIXとなる。
しかし、私はIMも999だと考えている。
もしかするとできるだけ効率の良い文字数が短いものを模索しているのかもしれない。
例えば、私の生まれた1968のローマ数字を考えると、
1000+900+60+8なら、MDCCCCLXVIIIで12文字
2000-100+60+8なら、MCMLXVIIIで9文字
2000-50+20-2なら、MLMXIIXで7文字
2000-40+8なら、MXLMVIIIで8文字
2000-30-2なら、MXXXIIMで7文字
私の中ではどれも不正解ではないと考えている。
アラビア数字の表記方法は一対一対応、つまり一意性が保たれているが、ローマ数字には加算則や減算則といった複数の表記方法が存在する。
ローマ数字に不慣れな人は、その文字列を見て直ぐ様に数値を連想することが出来なかったりする。
言い換えれば、ローマ数字からアラビア数字への変換、もっと言えば翻訳に近い作業なのかもしれないが、そういうプログラムの需要があるということでもある。
ウィキペディアのローマ数字のページでは、ローマ数字の記述規則について、いろいろと御託を並べては、認められていないと書かれている項目が多々ある。
しかし、認められていなくとも、使われてしまっている過去の例とかがあるわけで、そのページにも申し訳無さそうに異表記の項目がある通りです。
ローマ数字には厳密な規則が定義されていないということでもある。
そういうこともあって、かなり寛容に解釈するための記述規則を、論理的に考えてみました。
ローマ数字を多項式として考えてみる。
これはローマ数字に限ったことではなく、アラビア数字でも漢数字でも同じである。
a・10^5 + b・10^4 + c・10^3 + d・10^2 + e・10^1 + f・10^0
アラビア数字ならば、abcdef
漢数字ならば、a十b万c千d百e十f
ということを表しており、
変数であるa~fにはそれぞれ10進1桁の値、
アラビア数字であれば、0~9
漢数字であれば、零~九
ということです。
では、ローマ数字において、項はどのように表現するべきなのだろうか。
私は、同じ記号が連続して現れているものを項と考えました。
例えば、2016という値をローマ数字にするとMMXVIとなります。
MM X V I
の様に項に分けることができます。
たまたま、加算則しか使われていないので、降べきの順に並んでいます。
2・M + 1・X + 1・V + 1・I
多項式の形になりましたね。
減算則が入って注意するべきことは、名前の通り負符号が現れることや、降べきの順に並んでいないということ。
ということで、左から右へと降べきの順とはならないということを踏まえて考えます。
まずは、一通り目を通し、一番大きな項を探します。
その項を仮にBとし、それ以外の項は順序を入れ替えずに計算もせずに一塊のAとします。
AやBは複数登場することもあるでしょう。
しかし、AもBも一塊として考えたので、それぞれは交互に並ぶことになります。
プログラム内の条件分岐で扱いやすいようにと、A=1、B=2として、3進法で考え、10進数を判定に用いる。
文字列 | 3進数 | 10進数 | 詳細 |
000000 | 0 | 文字列の長さが0、つまり存在しない。 | |
A | 000001 | 1 | 検索値の文字が存在しない。 |
B | 000002 | 2 | 検索値の文字しか存在しない。 |
AB | 000012 | 5 | 減算則(A,B)=B-Aが適用される。 |
BA | 000021 | 7 | 加算則(B,A)=B+Aが適用される。 |
ABA | 000121 | 16 | 加算則(減算則(A,B),A)が適用される。 |
BAB | 000212 | 23 | 加算則(B,減算則(A,B))が適用される。 |
ABAB | 001212 | 50 | 2番目のAの計算方法が未定義で計算不能。 |
BABA | 002121 | 70 | 加算則(B,加算則(減算則(A,B),A))が適用される。 |
ABABA | 012121 | 151 | 2番目のAの計算方法が未定義で計算不能。 |
BABAB | 021212 | 212 | 2番目のAの計算方法が未定義で計算不能。 |
その他 | その他 | その他 | 計算不能。 |
8通りの正常な分岐と、それ以外というように分類できました。
if文でやってもいいが、switch文を使える言語であれば、容易に条件分岐が可能である。
Aについては、検索値を1段下げて、Aの部分を再帰処理させ、値が返ってくるのを待てばいい。
Bについては、検索値を文字数倍して容易に求められる。
実にシンプルである。
この方法で、かなり寛容なプログラムができるはずである。
例えば、VVといった一般的な使い方では有り得ないようなものも、寛容に受け取り、加算則と判断して10とします。
あとは、どの程度の文字に対して計算を遂行してくれるかというところではある。
I, i, I, i, Ⅰ, ⅰ | 1 |
Ⅱ, ⅱ | 2 |
Ⅲ, ⅲ | 3 |
Ⅳ, ⅳ | 4 |
V, v, V, v, Ⅴ, ⅴ | 5 |
Ⅵ, ⅵ | 6 |
Ⅶ, ⅶ | 7 |
Ⅷ, ⅷ | 8 |
Ⅸ, ⅸ | 9 |
X, x, X, x, Ⅹ, ⅹ | 10 |
Ⅺ, ⅺ | 11 |
Ⅻ, ⅻ | 12 |
L, l, L, l, Ⅼ, ⅼ | 50 |
C, c, C, c, Ⅽ, ⅽ | 100 |
D, d, D, d, Ⅾ, ⅾ, |Ɔ | 500 |
M, m, M, m, Ⅿ, ⅿ, ↀ, C|Ɔ | 1000 |
ↁ, |ƆƆ | 5000 |
ↂ, CC|ƆƆ | 10000 |
ↇ, |ƆƆƆ | 50000 |
ↈ, CCC|ƆƆƆ | 100000 |
とりあえず、この表に記したローマ数字は対応させてあります。
さて、このプログラムが出力する如何なる値も、貴方が欲する値かどうかは何とも言えません。
あくまでも、上記ロジックによって導き出された一つの答えに過ぎません。
一番の問題は、500以降に登場する|Ɔを含む記号の曖昧さです。
例えば、CCC|ƆƆとccc|ƆƆでは、前者は9900、後者は4700と答えが分かれるようになっています。
これは、入力する側が意図的に異なる文字コードを使っていると判断しているからです。
これが正しいのか正しくないのかは、私にはまだまだ判断出来ません。
というわけで、このプログラムによる如何なる結果による不利益も当方は一切の責任を持たないものとします。