Github: https://github.com/holidaysss
小組:龍天堯(代碼實現),林毓植(浮點轉分數函數,代碼審查)
PSP2.1 |
Personal Software Process Stages |
預估耗時(分鍾) |
實際耗時(分鍾) |
Planning |
計划 |
30 | 30 |
· Estimate |
· 估計這個任務需要多少時間 |
30 | 30 |
Development |
開發 |
540 | 540 |
· Analysis |
· 需求分析 (包括學習新技術) |
60 | 60 |
· Design Spec |
· 生成設計文檔 |
30 | 30 |
· Design Review |
· 設計復審 (和同事審核設計文檔) |
60 | 60 |
· Coding Standard |
· 代碼規范 (為目前的開發制定合適的規范) |
30 | 30 |
· Design |
· 具體設計 |
120 | 120 |
· Coding |
· 具體編碼 |
240 | 240 |
· Code Review |
· 代碼復審 |
60 | 60 |
· Test |
· 測試(自我測試,修改代碼,提交修改) |
60 | 60 |
Reporting |
報告 |
90 | 90 |
· Test Report |
· 測試報告 |
30 | 30 |
· Size Measurement |
· 計算工作量 |
30 | 30 |
· Postmortem & Process Improvement Plan |
· 事后總結, 並提出過程改進計划 |
30 | 30 |
合計 |
780 | 780 |
設計實現過程:思路:四則運算題庫的功能應該包括 出題,計算答案, 對比答案 這三個主要功能。
代碼說明:
題目生成函數:problem() ,調用natural(),fraction()
def problem(area=10): # 隨機生成一道題目(自然數四則運算或分數運算),運算符不超過3個 try: if random.choice([1, 2]) == 1: # 隨機生成 自然數或分數 的四則運算 expression, print_expression = natural(area) # 生成一個自然數運算 results = demical_to_fraction(eval(expression)) # 運算結果通過demical_to_fraction()轉成分數 else: # 分數四則運算 和上面流程大致相同 expression, print_expression = fraction(area) # 生成一個分數運算 results = demical_to_fraction(eval(expression)) if not results: # 無法轉分數 problem(area) return 0 # print_expression_nums = list(filter(str.isdigit, print_expression)) # ['2','+',1'] print_expression_nums = print_expression.replace('(', '').replace(')', '').split() # 將輸出表達式拆解 print_expression_nums.sort() # ['+', 1', '2'] if results < 0 or ((str(results)in answers) and (print_expression_nums in str_num)): # 去負答案,去重復 problem(area) else: results = turn_fracrtion(results) # 轉化 prints.append(print_expression) answers.append(results) # 答案列表 str_num.append(print_expression_nums) except Exception: # 過濾分母為0的題目 problem(area)
自然數運算生成 natural():
def natural(area): # 生成一個自然數運算 operator_num = random.randint(1, 3) # 隨機運算符 expression = print_expression = num = str(random.randint(1, area)) # 第一個數 bracket = (random.choice(['(', '']) if not operator_num == 1 else '') # 非單運算符 可加括號 for i in range(operator_num): # 隨機個運算符 op = str(random.choice(operators)) # 隨機選擇運算符 (+ - * /) if op == '-': # 若為'-',生成數字小於前一個數字 num = str(random.randint(1, int(num))) else: num = str(random.randint(1, area)) # 隨機數值,不超過area if bracket == ')': # 右括號在數字右邊 print_expression += ' ' + change(op) + ' ' + num + bracket # 用於輸出的表達式 例:1×2 expression += op + num + bracket # 用於eval()計算的表達式 例:1*2 else: # 左括號在數字左邊 print_expression += ' ' + change(op) + ' ' + bracket + num expression += op + bracket + num bracket = (')' if bracket == '(' else '') # 左括號配右括號, 空配空 return expression, print_expression
分數運算生成 fraction(), gen_fraction():
def gen_fraciton(area): # 生成一個規范分數 while True: a = random.randint(1, area) b = random.randint(1, area) if '/' in str(Fraction(a, b)): return Fraction(a, b) def fraction(area): # 生成一個分數運算 operator_num = random.randint(1, 3) # 隨機運算符 num = gen_fraciton(area) expression = print_expression = str(num) # 第一個分數 bracket = (random.choice(['(', '']) if operator_num != 1 else '') # 超過一個運算符才需要加括號 for i in range(operator_num): op = str(random.choice(operators)) if op == '-': # 若為'-',生成分數小於等於前一個分數 while True: next_num = gen_fraciton(area) if next_num <= num: break num = next_num else: num = gen_fraciton(area) if bracket == ')': if float(num) > 1: # 假分數轉帶分數 例:8/3 -> 2'2/3 print_expression += ' ' + change(op) + ' ' + turn_fracrtion(num) + bracket else: print_expression += ' ' + change(op) + ' ' + str(num) + bracket expression += op + str(num) + bracket else: if float(num) >= 1: print_expression += ' ' + change(op) + ' ' + bracket + turn_fracrtion(num) else: print_expression += ' ' + change(op) + ' ' + bracket + str(num) expression += op + bracket + str(num) bracket = (')' if bracket == '(' else '') # 左括號配右括號 return expression, print_expression
假分數轉化函數 turn_fraction():
def turn_fracrtion(results): # 假分數轉帶分數 if isinstance(eval(str(results)), int) or (eval(str(results)) < 1): # 整數和真分數 return str(results) else: return str(int(results)) + "'" + str(results - int(results)) # 假分數
符號轉化函數 change():
def change(a): # *,/ 轉成 ×,÷ if a == '*': a = '×' elif a == '/': a = '÷' return a
eval函數處理的結果出來是浮點數,不符合要求,苦想不解,讓毓植寫了個浮點轉化分數的函數:
def find_cycle(demical): # 找小數的循環體(參數為小數部分) for i in range(1, 17): cycle_part = demical[:i] # 截取小數部分的前i位,假設為循環體 if len(cycle_part) < 4: # 如果循環體較短 if (cycle_part*3) == demical[:3*i]: # 需要滿足4次重復 return cycle_part # 滿足才認定為循環體 else: # 如果循環體較長 if (cycle_part*2) == demical[:2*i]: # 滿足2次重復 return cycle_part return 0 # 找不到循環體,返回0 def demical_to_fraction(n, zero_num=0): # 小數轉化分數 n = str(n) # 規范輸入為字符串形式 if len(n) < 16: # 如果是有限小數,直接返回 return Fraction(n) real_num, dot_area = n.split('.') # 獲取整數 和 小數 float_num = float(n) # 轉化一個浮點數用於計算 for i in range(len(n)): cycle_start = dot_area[i:] # 從第i位開始,開始截取字符串 result = find_cycle(cycle_start) # 從截取的字符串中找到循環體 length = len(str(result)) # 判斷循環體的長度 if result: # 如果存在循環體 if i != 0: # 如果循環體的開始不是小數點后第一位 eg 0.13888888 new_number = float_num*(10**i) # 移位數使循環體是小數點后的開始 eg 13.8888 demical_to_fraction(new_number, i) # 將新生成的數遞歸使用 break else: # 如果循環體直接在小數點后的第一位 fraction = Fraction(int(result), int('9'*length)) # 小數部分轉化為分數 /數學知識需要了解 final_num = int(real_num) + fraction # 小數點前的部分需要從重新加上 return final_num/(10**zero_num) # 回退移的位數
以上是主要函數
具體源碼后面會上傳到github
運行結果:
生成一萬道題:
小學生就可以在記事本上做題啦
一年后。。
感覺良好
開始對答案
還行
為了方便家長了解學生的成績, 在 答案對比 處增加了水型球比例圖。(具體代碼Github已更新,這里就不修改了)
好了。。。
問題記錄:1.出現了溢出錯誤,發現是條件判斷的問題,已修改。 2.優化邏輯,分解problem()函數。 3.修改去重邏輯,優化程序運行速度. 4.拓展對比答案功能,增加水型球比例圖。 5.需求看錯了,過程不能出現負數看出結果不出現負數, 已修改。
6. 忘記吧結果的假分數轉化了。。已補上。 7.日常優化。。。。。。。。 8.優化答案對比 ( 最后一次改了,再改我
項目小結:這個項目挺有意思的,主要運用到”隨機性“,小邏輯特別多,主要思路還是有跡可循的,繼續努力,加油。