Merchant』s Guide To The Galaxy筆試題解析 python解決 羅馬數字轉阿拉伯數字
- 2019 年 11 月 27 日
- 筆記
1.說明
1.題目應在本周5中午十二點之前提交
2.題目測試考察包含對題意的理解以及代碼整潔度和簡易度,測試的數據會變動
3.最好有相關的單元測試,如果能達到100%測試覆蓋率且能正確的使用mock對象最好.如果時間不夠或者不熟悉測試方法否,這一部分可忽略.功能完整性是首要考察.
4.建議使用github或其它git託管工具託管一份,提交時直接給出鏈接.合理的commit,如果符合小步更改提交的準則會有加分.
2.Merchant』s Guide To The Galaxy
Python
Description You decided to give up on earth after the latest financial collapse left 99.99% of the earth's population with 0.01% of the wealth. Luckily, with the scant sum of money that is left in your account, you are able to afford to rent a spaceship, leave earth, and fly all over the galaxy to sell common metals and dirt (which apparently is worth a lot). Buying and selling over the galaxy requires you to convert numbers and units, and you decided to write a program to help you. The numbers used for intergalactic transactions follows similar convention to the roman numerals and you have painstakingly collected the appropriate translation between them. Numbers are formed by combining symbols together and adding the values. For example, MMVI is 1000 + 1000 + 5 + 1 = 2006. Generally, symbols are placed in order of value, starting with the largest values. When smaller values precede larger values, the smaller values are subtracted from the larger values, and the result is added to the total. For example MCMXLIV = 1000 + (1000 − 100) + (50 − 10) + (5 − 1) = 1944. The symbols "I", "X", "C", and "M" can be repeated three times in succession, but no more. (They may appear four times if the third and fourth are separated by a smaller value, such as XXXIX.) "D", "L", and "V" can never be repeated. "I" can be subtracted from "V" and "X" only. "X" can be subtracted from "L" and "C" only. "C" can be subtracted from "D" and "M" only. "V", "L", and "D" can never be subtracted. Only one small-value symbol may be subtracted from any large-value symbol. A number written in Arabic numerals can be broken into digits. For example, 1903 is composed of 1, 9, 0, and 3. To write the Roman numeral, each of the non-zero digits should be treated separately. In the above example, 1,000 = M, 900 = CM, and 3 = III. Therefore, 1903 = MCMIII. Input to your program consists of lines of text detailing your notes on the conversion between intergalactic units and roman numerals. You are expected to handle invalid queries appropriately. INPUT: glob is I prok is V pish is X tegj is L glob glob Silver is 34 Credits glob prok Gold is 57800 Credits pish pish Iron is 3910 Credits how much is pish tegj glob glob ? how many Credits is glob prok Silver ? how many Credits is glob prok Gold ? how many Credits is glob prok Iron ? OUTPUT pish tegj glob glob is 42 glob prok Silver is 68 Credits glob prok Gold is 57800 Credits glob prok Iron is 782 Credits
1234567891011121314151617181920212223242526272829303132 |
Description You decided to give up on earth after the latest financial collapse left 99.99% of the earth's population with 0.01% of the wealth. Luckily, with the scant sum of money that is left in your account, you are able to afford to rent a spaceship, leave earth, and fly all over the galaxy to sell common metals and dirt (which apparently is worth a lot). Buying and selling over the galaxy requires you to convert numbers and units, and you decided to write a program to help you. The numbers used for intergalactic transactions follows similar convention to the roman numerals and you have painstakingly collected the appropriate translation between them. Numbers are formed by combining symbols together and adding the values. For example, MMVI is 1000 + 1000 + 5 + 1 = 2006. Generally, symbols are placed in order of value, starting with the largest values. When smaller values precede larger values, the smaller values are subtracted from the larger values, and the result is added to the total. For example MCMXLIV = 1000 + (1000 − 100) + (50 − 10) + (5 − 1) = 1944. The symbols "I", "X", "C", and "M" can be repeated three times in succession, but no more. (They may appear four times if the third and fourth are separated by a smaller value, such as XXXIX.) "D", "L", and "V" can never be repeated. "I" can be subtracted from "V" and "X" only. "X" can be subtracted from "L" and "C" only. "C" can be subtracted from "D" and "M" only. "V", "L", and "D" can never be subtracted. Only one small-value symbol may be subtracted from any large-value symbol. A number written in Arabic numerals can be broken into digits. For example, 1903 is composed of 1, 9, 0, and 3. To write the Roman numeral, each of the non-zero digits should be treated separately. In the above example, 1,000 = M, 900 = CM, and 3 = III. Therefore, 1903 = MCMIII. Input to your program consists of lines of text detailing your notes on the conversion between intergalactic units and roman numerals. You are expected to handle invalid queries appropriately. INPUT:glob is Iprok is Vpish is Xtegj is Lglob glob Silver is 34 Creditsglob prok Gold is 57800 Creditspish pish Iron is 3910 Creditshow much is pish tegj glob glob ?how many Credits is glob prok Silver ?how many Credits is glob prok Gold ?how many Credits is glob prok Iron ? OUTPUTpish tegj glob glob is 42glob prok Silver is 68 Creditsglob prok Gold is 57800 Creditsglob prok Iron is 782 Credits |
---|
3.題意解析
3.1 題目說明
省略前言就是說,幫助商人去進行羅馬數字和阿拉伯數字的轉換
羅馬字母有: I,V,X,L,C,D,M
規則有:
0.如果小數字的出現在大數字的前面則表示減.例如MCMXLIV = 1000 + (1000 − 100) + (50 − 10) + (5 − 1) = 1944.
1. I:可以表示十進制數字1,V:可以表示十進制數字5,X:可以表示十進制數字10,L:可以表示十進制數字50,C:可以表示十進制數字100,D:可以表示十進制數字500,M:可以表示十進制數字1000;
2.I, X, C, and M可以重複出現最多3次;(這個不好處理一點)
3.一般是從後往前排列:即MDCLXVI的順序,當然也允許相鄰的兩個倒序,但是需要符合以下規則:
I :只能組合IV ,IX
X:只能組合XL,XC
C:只能組合CD,CM
V,L,D不能倒序
3.2 測試數據解析
Python
glob is I prok is V pish is X tegj is L glob glob Silver is 34 Credits glob prok Gold is 57800 Credits pish pish Iron is 3910 Credits how much is pish tegj glob glob ? how many Credits is glob prok Silver ? how many Credits is glob prok Gold ? how many Credits is glob prok Iron ?
1234567891011 |
glob is Iprok is Vpish is Xtegj is Lglob glob Silver is 34 Creditsglob prok Gold is 57800 Creditspish pish Iron is 3910 Creditshow much is pish tegj glob glob ?how many Credits is glob prok Silver ?how many Credits is glob prok Gold ?how many Credits is glob prok Iron ? |
---|
Python
OUTPUT pish tegj glob glob is 42 glob prok Silver is 68 Credits glob prok Gold is 57800 Credits glob prok Iron is 782 Credits
12345 |
OUTPUTpish tegj glob glob is 42glob prok Silver is 68 Creditsglob prok Gold is 57800 Creditsglob prok Iron is 782 Credits |
---|
首先是若干行.代表某些單詞對應的羅馬字母.
然後是若干行,這部分出現的單詞需要用前面的進行翻譯.多少的(銀金鐵)幣是多少的貨幣.這裡則是給出了交易幣以及交易單位的價格比.
再若干行,這部分會出現兩種問法,
問how much則是直接在is後面給出一串字符串,照之前進行羅馬數轉化成阿拉伯數.
問how many則是計算多少的(銀金鐵)幣是多少的貨幣.
例如:
Python
glob glob Silver is 34 Credits #說明II(2)個銀幣是34元(基礎貨幣單位)則可以得出一個銀幣是17元
12 |
glob glob Silver is 34 Credits#說明II(2)個銀幣是34元(基礎貨幣單位)則可以得出一個銀幣是17元 |
---|
Python
how many Credits is glob prok Silver ? #問.IV(4)個銀幣是多少元,可以得出34/2*4=68,符合輸出結果
12 |
how many Credits is glob prok Silver ?#問.IV(4)個銀幣是多少元,可以得出34/2*4=68,符合輸出結果 |
---|
別的就不再解析.
3.2 難點分析:
首先必然有一個基礎的羅馬轉數字.之後是字典的構建,將單詞與羅馬字母對應起來.
進行字符串解析.因為不限制每一行的長度,所以複雜起來,只能用將模塊裂開進行解析.其中每一個部分是沒有條數限制的,所以只能通過讀取一行再分析出這一行是幹嘛的.
第一部分的是字典對應過去,所以可以判斷最後一個字母是不是在羅馬字母中,而且是大寫.
第二部分則是以credits結尾
第三部分則是問號結尾以how mach/many區別
4.模塊化
4.1 Roma 數字轉阿拉伯數字
解析在代碼中.建議將內部的#test的注釋取消,來看看逐步的結果就明白了
Python
#coding:utf-8 import re #正則表達式 def getRomanNum(RomanStr): ''' 輸入羅馬數字字符串,輸出轉換後的阿拉伯字符串 邏輯說明:原本是使用所有規則一個個if來判斷處理的,但是這樣的代碼非常垃圾,學習後改進. 使用正則的類似思想,對輸入串判斷romanpattern中規則出現的情況, 例如以MXCVI為例,則在pattern的結果是:^M{1}C{0}XCX{0}VI{1} 1000+90+6這裡就可以按照千百十分位來分別判斷處理 其中retnum則是存放轉化後的阿拉伯數字,用於返回. ''' #正則表達式進行匹配,判斷輸入是否合法 if re.search('^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$',RomanStr)!=None: NumDic = {"pattern":"","retNum":0}#值記錄區 # 如測試數為 XCVI 則最後retNum 96 pattern ^M{0}C{0}XCX{0}VI{1} 展示出現數次 RomanPattern = { "0":('','','','M'), # 1000 "1":('CM','CD','D','C',100), #900 400 500 100 "2":('XC','XL','L','X',10), #90 40 50 10 "3":('IX','IV','V','I',1)#9 4 5 1 } i = 3 NumItems = sorted(RomanPattern.items())#對字典先排序返回元組 for RomanItem in NumItems: if RomanItem[0] != '0':#先統計千內的 patstr = NumDic["pattern"].join(['',RomanItem[1][0]]) #print "R "+RomanItem[1][0] if re.search(patstr,RomanStr) != None:#先判斷Romanstr中是否存在parstr NumDic["retNum"] += 9*RomanItem[1][4] # +=90 NumDic["pattern"] = patstr #存放正則信息 else: patstr = NumDic["pattern"].join(['',RomanItem[1][1]]) #print patstr if re.search(patstr,RomanStr) != None: NumDic["retNum"] += 4*RomanItem[1][4] #+=40 NumDic["pattern"] = patstr else: patstr = NumDic["pattern"].join(['',RomanItem[1][2]]) if re.search(patstr,RomanStr) != None: NumDic["retNum"] += 5*RomanItem[1][4] # += 50 NumDic["pattern"] = patstr # test #print "retNum " + str(NumDic["retNum"]) , #print "pattern " +NumDic["pattern"] if NumDic["pattern"] == '': NumDic["pattern"] = '^'#標誌 tempstr = '' sum = 0 for k in range(0,4):#處理連續出現幾次 pstr = RomanItem[1][3].join(['','{']).join(['',str(k)]).join(['','}']) patstr = NumDic["pattern"].join(['',pstr]) if re.search(patstr,RomanStr) != None: sum = k*(10**i) tempstr = patstr if tempstr <> '':#不等於即依舊為原始值 NumDic["pattern"] = tempstr else: NumDic["pattern"] = patstr NumDic['retNum'] += sum i -= 1 return NumDic['retNum'] #return NumDic['pattern'] else: print 'String is not a valid Roman numerals' print getRomanNum('MXCVI')
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869 |
#coding:utf-8import re #正則表達式 def getRomanNum(RomanStr): ''' 輸入羅馬數字字符串,輸出轉換後的阿拉伯字符串 邏輯說明:原本是使用所有規則一個個if來判斷處理的,但是這樣的代碼非常垃圾,學習後改進. 使用正則的類似思想,對輸入串判斷romanpattern中規則出現的情況, 例如以MXCVI為例,則在pattern的結果是:^M{1}C{0}XCX{0}VI{1} 1000+90+6這裡就可以按照千百十分位來分別判斷處理 其中retnum則是存放轉化後的阿拉伯數字,用於返回. ''' #正則表達式進行匹配,判斷輸入是否合法 if re.search('^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$',RomanStr)!=None: NumDic = {"pattern":"","retNum":0}#值記錄區 # 如測試數為 XCVI 則最後retNum 96 pattern ^M{0}C{0}XCX{0}VI{1} 展示出現數次 RomanPattern = { "0":('','','','M'), # 1000 "1":('CM','CD','D','C',100), #900 400 500 100 "2":('XC','XL','L','X',10), #90 40 50 10 "3":('IX','IV','V','I',1)#9 4 5 1 } i = 3 NumItems = sorted(RomanPattern.items())#對字典先排序返回元組 for RomanItem in NumItems: if RomanItem[0] != '0':#先統計千內的 patstr = NumDic["pattern"].join(['',RomanItem[1][0]]) #print "R "+RomanItem[1][0] if re.search(patstr,RomanStr) != None:#先判斷Romanstr中是否存在parstr NumDic["retNum"] += 9*RomanItem[1][4] # +=90 NumDic["pattern"] = patstr #存放正則信息 else: patstr = NumDic["pattern"].join(['',RomanItem[1][1]]) #print patstr if re.search(patstr,RomanStr) != None: NumDic["retNum"] += 4*RomanItem[1][4] #+=40 NumDic["pattern"] = patstr else: patstr = NumDic["pattern"].join(['',RomanItem[1][2]]) if re.search(patstr,RomanStr) != None: NumDic["retNum"] += 5*RomanItem[1][4] # += 50 NumDic["pattern"] = patstr # test #print "retNum " + str(NumDic["retNum"]) , #print "pattern " +NumDic["pattern"] if NumDic["pattern"] == '': NumDic["pattern"] = '^'#標誌 tempstr = '' sum = 0 for k in range(0,4):#處理連續出現幾次 pstr = RomanItem[1][3].join(['','{']).join(['',str(k)]).join(['','}']) patstr = NumDic["pattern"].join(['',pstr]) if re.search(patstr,RomanStr) != None: sum = k*(10**i) tempstr = patstr if tempstr <> '':#不等於即依舊為原始值 NumDic["pattern"] = tempstr else: NumDic["pattern"] = patstr NumDic['retNum'] += sum i -= 1 return NumDic['retNum'] #return NumDic['pattern'] else: print 'String is not a valid Roman numerals' print getRomanNum('MXCVI') |
---|
4.2 輸入的字符串解析
這部分就簡單多了,說明在代碼中
Python
#coding:utf-8 from Roma_to_num_test import getRomanNum Romanarray=['I','V','X','L','C','D','M'] word_dic = {}#以字典來對應單詞和羅馬數字 coin_dic = {}#以字典來對應貨幣和價值 def str_Resolve(input_line): ''' 輸入每一行的字符串類型信息:如題目中的測試數據.返回分類機處理後的信息. 程序代碼說明: 結構很明顯,按照末尾字符是否在Romanarray中,判斷是否是第一類定義單詞輸入 否則判斷是否以小寫s結束,判斷是否是作為第二類金幣銀幣類型輸入 否則判斷末尾是否是?號為第三類輸入.進行按照空格劃分之後判斷第二個單詞 按照many和mach進行分別返回處理 ''' if input_line[-1] in Romanarray: input_line_array = input_line.split(' ')#以空格截取 word_dic[input_line_array[0]] = input_line_array[2]#取第一個和最後一個元素 return #print 'word_dic:',word_dic: elif input_line[-1] == 's':#以小寫s做為金幣銀幣那些測試數據的識別符號 input_line_array = input_line.split(' ')#以空格截取 temp_str = '' for i in range(len(input_line_array)-4): temp_str += word_dic[input_line_array[i]] temp_num = getRomanNum(temp_str) #print "input_line_array[-4]",input_line_array[-4] coin_dic[input_line_array[-4]] = int(input_line_array[-2])/int(temp_num) return #print 'coin_dic:',coin_dic elif input_line[-1] == '?': #以?為標誌,判斷是否是第三四類輸入 input_line_array = input_line.split(' ') #print "input_line_array:",input_line_array if input_line_array[1] == 'much': temp_str1 = '' temp_str3 = '' for i in range(3,len(input_line_array)-1):#抽取第四個到最後一個數組元素 temp_str3 += input_line_array[i]+' ' temp_str1 += word_dic[input_line_array[i]] return temp_str3+"is "+str(getRomanNum(temp_str1))#轉化成數字並且輸出 elif input_line_array[1] == 'many':#處理many temp_str2 = '' temp_str4 = '' for i in range(4,len(input_line_array)-2):#取第5個到倒數第三個之間 temp_str4 += input_line_array[i]+' ' temp_str2 += word_dic[input_line_array[i]]#轉化為字符進行roma查詢 return temp_str4+input_line_array[-2]+' is '+ str(coin_dic[input_line_array[-2]]*getRomanNum(temp_str2) )+ ' Credits'#輸出數量*單位價值
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859 |
#coding:utf-8from Roma_to_num_test import getRomanNum Romanarray=['I','V','X','L','C','D','M'] word_dic = {}#以字典來對應單詞和羅馬數字coin_dic = {}#以字典來對應貨幣和價值 def str_Resolve(input_line): ''' 輸入每一行的字符串類型信息:如題目中的測試數據.返回分類機處理後的信息. 程序代碼說明: 結構很明顯,按照末尾字符是否在Romanarray中,判斷是否是第一類定義單詞輸入 否則判斷是否以小寫s結束,判斷是否是作為第二類金幣銀幣類型輸入 否則判斷末尾是否是?號為第三類輸入.進行按照空格劃分之後判斷第二個單詞 按照many和mach進行分別返回處理 ''' if input_line[-1] in Romanarray: input_line_array = input_line.split(' ')#以空格截取 word_dic[input_line_array[0]] = input_line_array[2]#取第一個和最後一個元素 return #print 'word_dic:',word_dic: elif input_line[-1] == 's':#以小寫s做為金幣銀幣那些測試數據的識別符號 input_line_array = input_line.split(' ')#以空格截取 temp_str = '' for i in range(len(input_line_array)-4): temp_str += word_dic[input_line_array[i]] temp_num = getRomanNum(temp_str) #print "input_line_array[-4]",input_line_array[-4] coin_dic[input_line_array[-4]] = int(input_line_array[-2])/int(temp_num) return #print 'coin_dic:',coin_dic elif input_line[-1] == '?': #以?為標誌,判斷是否是第三四類輸入 input_line_array = input_line.split(' ') #print "input_line_array:",input_line_array if input_line_array[1] == 'much': temp_str1 = '' temp_str3 = '' for i in range(3,len(input_line_array)-1):#抽取第四個到最後一個數組元素 temp_str3 += input_line_array[i]+' ' temp_str1 += word_dic[input_line_array[i]] return temp_str3+"is "+str(getRomanNum(temp_str1))#轉化成數字並且輸出 elif input_line_array[1] == 'many':#處理many temp_str2 = '' temp_str4 = '' for i in range(4,len(input_line_array)-2):#取第5個到倒數第三個之間 temp_str4 += input_line_array[i]+' ' temp_str2 += word_dic[input_line_array[i]]#轉化為字符進行roma查詢 return temp_str4+input_line_array[-2]+' is '+ str(coin_dic[input_line_array[-2]]*getRomanNum(temp_str2) )+ ' Credits'#輸出數量*單位價值 |
---|
4.3 使用文件讀取測試數據的main.py
Python
#coding:utf-8 from str_Resolve import * import fileinput ''' 測試/使用說明: 請將新的測試數據,使用文本的形式存放在test-data文件夾下,同時修改文件讀取的路徑 ''' for line in fileinput.input("../test-data/input_2.txt"): str_line=line[:-1]#去除行結尾的換行符號,未知為何會多出一個換行 ,根據環境不同可能有差別 #print str_line try: str_return = str_Resolve(str_line) if str_return: print str_return except: print "I have no idea what you are talking about"
1234567891011121314151617 |
#coding:utf-8from str_Resolve import *import fileinput'''測試/使用說明:請將新的測試數據,使用文本的形式存放在test-data文件夾下,同時修改文件讀取的路徑''' for line in fileinput.input("../test-data/input_2.txt"): str_line=line[:-1]#去除行結尾的換行符號,未知為何會多出一個換行 ,根據環境不同可能有差別 #print str_line try: str_return = str_Resolve(str_line) if str_return: print str_return except: print "I have no idea what you are talking about" |
---|
數據放在github上,見文章頭部
在1,2,3組測試數據下正確得出了結果
但是第一組感覺是題目錯誤 不是782而是780.手算也是這個答案.