AC實作筆記 — 羅馬數字轉換器

班尼楊
3 min readMay 16, 2021

--

這次的實作是 2–1技術驗收的第三題,這題感覺是在考驗我們設計演算法的能力,而我在開始做這題時,一看到羅馬數字的規則也是馬上拖延症發作,馬上打開 Youtube壓壓驚一下

先小小介紹一下羅馬數字主要的排列規則方式 :

1. 羅馬符號 :

首先羅馬符號有七種,這七個符號都各自代表一個數字,如下
I (1) 、V(5)、 X(10) 、Ⅼ(50)、Ⅽ(100)、Ⅾ(500)和Ⅿ(1000)

2. 右加左減 :

右加就是把小的數字放在右邊表示我們想表達的數字為左 + 右 右加數字不得超過三位,也就是說9不能寫作VIIII
而右加也是大部分羅馬數字的表示方式
Ex. 6 = VI 、8 = VIII、27 = XXVII
左減基本上就是跟右加相反,把小的數字放在左邊,這時候我們想表示的數字即為 右 - 左 ,有個特殊的規則是左減只能減一個數字,也就是說數字8我不能寫成IIX
Ex. 4 = IV、9 = IX、40 = XL

上述兩個規則就可以涵蓋幾乎全部1~3999的羅馬數字了,剩下一些小規則我就不一一贅述了

從以上的規則中我們可以稍稍的看出來羅馬數字偏向是將數字按照不同位數由上而下組合起來的規則

Ex. 3213 = MMM (3000) + CC (200) + X (10) + III (3) = MMMCCXIII

於是我便開始思考說 : “ 我需要那些符號才可以組合出來全部1~3999的羅馬數字 ? ”

const numeral = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]const symbol = [‘M’, ‘CM’, ‘D’, ‘CD’, ‘C’, ‘XC’, ‘L’, ‘XL’, ‘X’, ‘IX’, ‘V’, ‘IV’, ‘I’]

這邊我為了避免在後面的程序處理到左減的數字,於是我決定把左減的羅馬數字也放到symbol的陣列裡,而陣列裡的每一個符號都會對應到上面numeral陣列的數字
因為我們是要從較大的位數往下處理,所以我陣列的排序方式是由大而小
我們也可以換一個資料結構來做,如下:

const roman = {
M: 1000,
CM: 900,
D: 500,
CD: 400,
C: 100,
XC: 90,
L: 50,
XL: 40,
X: 10,
IX: 9,
V: 5,
IV: 4,
I: 1
};

最後,寫個簡單的迴圈就大功告成囉

for ( i = 0 ; i < numeral.length ; i++) {while ( numeral[i] <= number ) {roman += symbol[i]number -= numeral[i]}}

這邊可以看到說我們每次的迴圈都是在處理一個位數的羅馬符號,number就是我們丟進來處理的阿拉伯數字,假如說我們丟進來的數字是2345,那我們就會跑4次迴圈

第二層的while就是在找我們設定的numeral陣列裡最大最接近2345的元素,所以理所當然是從第一個元素1000開始著手,裡面while迴圈的任務就是確定 1000 < 2345之後,我們就可以把一個M符號放進我們要轉換的羅馬數字裡,接下來再把丟進來的2345減掉1000變成1345,因為這個1000已經成功轉換成符號了

完成第一圈後我們發現1345還是可以滿足while的條件式,所以我們還可以再執行一次while迴圈,這時候羅馬符號變成MM了,剩下要處理的數字就是345

有看出來這個程式在幹嘛了嗎 ? 這其實不是一個太複雜的程式,但當我們看到一個不熟悉的東西同時又要把它轉換成演算法的時候,卻會一下子轉換不過來,當遇到這種情況時,不妨拿出紙跟筆,把自己的想法寫下來,就會發現題目沒有自己想的那麼複雜了~

--

--

班尼楊

Backend Engineer | Taipei | Mathematic Background | Trying my best to record what I’ve learned