2017年4月17日 星期一

Python - 數字轉羅馬數字

題目是由checkio網站所出,最近都在使用這個網站重新訓練自己的程式邏輯
Python 是簡單又強大的語言,因此拿來訓練非常方便
希望能夠一個禮拜寫兩題的速度紀錄每次解題的過程
除此之外,也會抽空研究python較重要的幾個模組

本次的題目網址
https://py.checkio.org/mission/roman-numerals/
建議可以先自行練習完再來看以下的邏輯解析







=========================================================
羅馬數字的記法如題目所描述
數字轉羅馬數字對照
數字羅馬數字
1I
5V
10X
50L
100C
500D
1000M

這樣看起來直觀來說
如果題目給了3567
可以寫迴圈和if的判斷式  從最大的開始扣

3000 - 1000*3 (扣了1000三次)  ==> 結果用空字串紀錄 MMM
567 - 500*1 (扣了500一次) ==> 結果用空字串紀錄 MMMD
67 < 100 跳過 100的轉換
67 - 50*1 (扣了50一次) ==> 結果用空字串紀錄 MMMDL
17 - 10*1 (扣了10一次) ==> 結果用空字串紀錄 MMMDLX
7  -  5*1 (扣了5一次) ==> 結果用空字串紀錄 MMMDLXV
2  -  1*2 (扣了1兩次) ==> 結果用空字串紀錄 MMMDLXVII

得到最後答案  3567 的羅馬數字轉換結果為  MMMDLXVII

但是依照這個邏輯解題之後發現某些題目竟然是錯誤的轉換
原因在於羅馬數字表示有個限制
就是同樣的符號不可重複超過三次 (但題目上沒有明確表示)
因此 如果遇到 4  或者 900
依照我們的邏輯會表示成 IIII  以及 LXXXX
但在維基百科裡 https://en.wikipedia.org/wiki/Roman_numerals
4 和 900 的表示方法是 IV 和 XC

就維基百科的解釋是 為了不呈現重複超過四次以上的羅馬數字
運用了另一套機制
例如 4 地表示法為 IV
I 比 V 還小而且放在V的左邊 代表相減的意思
同理 900 地表示法為 XC

因此在寫這提的時候,最重要的關鍵就是除了這幾個羅馬數字以外
還要考慮這個問題,會出現問題的相關數字
例如 4 (IV), 9 (IX) , 40 (XL) , 90 (XC),400 (CD), 900 (CM) 的對應也要考慮進去

如此一來,思考用迴圈的方式和if搭配
就算是最土炮的方式也能夠暴力解出來
def roman(data):
 result= ""
 while data >= 0:
  if data >= 1000:
   data = data - 1000
   result.append('M')
  if data >= 900 and data <1000:
   data = data - 900
   result.append('CM')
  ...

 return result

雖然直觀,但是程式碼冗長且效率不佳
因此最後我的做法是先建立一個 dictionary 來存所有應考慮的轉換數字
再用迴圈判斷
以下是我最後的程式碼
def roman(number):
    ROMANS = (('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))
    
    result=""

    for roman,value in ROMANS:
        while number>=value:
            number-=value
            result+=roman
    return result

總結一下這題學到的新技巧

  1. 需要轉換項目時,建立dictionary是個好選擇
  2. 羅馬數字有不重複4次的特性要注意,並利用左小右大的方式表示解決




沒有留言:

張貼留言