三年級小學生計算大法
211606301 蔡振翼 211605240 謝孟軒
一、預估與實際
PSP2.1 | Personal Software Process Stages | 預估耗時(分鐘) | 實際耗時(分鐘) |
---|---|---|---|
Planning | 計劃 | ||
? Estimate | ? 估計這個任務需要多少時間 | 5 | 5 |
Development | 開發 | ||
? Analysis | ? 需求分析 (包括學習新技術) | 30 | 60 |
? Design Spec | ? 生成設計文檔 | 10 | 10 |
? Design Review | ? 設計復審 | 5 | 10 |
? Coding Standard | ? 代碼規範 (為目前的開發制定合適的規範) | 60 | 60 |
? Design | ? 具體設計 | 120 | 150 |
? Coding | ? 具體編碼 | 600 | 1200 |
? Code Review | ? 代碼復審 | 10 | 20 |
? Test | ? 測試(自我測試,修改代碼,提交修改) | 60 | 30 |
Reporting | 報告 | ||
? Test Repor | ? 測試報告 | 30 | 20 |
? Size Measurement | ? 計算工作量 | 10 | 5 |
? Postmortem & Process Improvement Plan | ? 事後總結, 並提出過程改進計劃 | 20 | 10 |
合計: | 1055 | 1570 |
二、需求分析
以下需求以人教版小學數學課本為準
一年級:
(1) 內容包含一百以內的加減法,大多數例題的答案也不存在超過一百的數字
(2)加減混合運算為兩步計算試題
(3)不涉及負數。
二年級:
(1) 開始涉及萬以內的加減法
(2) 乘法和除法都是表內乘除法
(3)除法中除數是9以內的,被除數為一百以內。
(4)出現三位參數的混合運算,兩步計算試題
(5)含有余數的除法運算不涉及混合運算。
三年級:
(1)出現小數加減法,參數至多兩位
(2)乘法開始出現兩位數乘以兩位數的乘法
(3)除法要求除數至多為兩位數,被除數最多為三位
家長要求:
(1)運算符在2~4個
(2)可以加括號
(3)減法運算的結果不能有負數
(4)除法運算除數不能為0,不能有余數(5) 至少存在兩個不相同的運算符
經過分析,我認為,這個程序應當:
(1)對一二年級的代碼進行完善補全
三年級應家長要求:
(1)小數部分的題目忽略不計
(2)計算參數在3~5個
(3)數字大小在1000以內,不存在負數
(4)舍棄含有余數的除法算數題
三、設計
1. 設計思路
說明你如何設計這個程序
設計思路①:窮舉所有題目,將每道題目分別儲存在String類型的數組中。按輸入要求隨機選擇序號抽取題目按要求完成輸出。這樣避免了抽取重復題目等問題,但題目量巨大並不適合。
設計思路②:隨機生成數字與符號利用逆波蘭函數求得題目的答案寫入文本。
設計思路③:隨機本次抽取題目所需要的運算符的個數,隨機抽取數字和符號進行運算,並把答案存入answer數組。抽取下一個符號,比較與上一個符號的運算等級判斷是否需要加擴號重復如上不驟。一旦達到運算符要求結束題目寫入文檔。
2. 實現方案
寫出具體實現的步驟
- 準備工作:先在Github上創建倉庫,克隆到本地,之後在倉庫中創建文件夾,編寫代碼之後通過git傳到github
- 技術關鍵點:
- 括號的選取
- 除法無余數
- 查重的實現
- 有耐心看代碼= =
四、編碼
問題1:如何隨機括號的位置(哪裏需要括號,生成的括號是否合理,是否必要)
解決過程:從哪裏需要括號,生成的括號是否合理,是否必要的角度進行思考,認為當乘除符號遇到加減符號時我們需要給加減的部分添加括號來保證運算順利進行。這也是我們開始思考整體代碼的開端。盡管在最後認真讀了一遍例題才發現這一點並不必要。
問題2:如何公平隨機生成多個運算符
解決過程:因為我們發現加法運算的數字在萬以內,而乘法除法的數字在千以內,添加隨機要求會導致題目中生成四個符號的概率並不平均。於是我們改小了加法和減法的範圍,這個改變最明顯的效果就是大量減少了“0÷(A+B)”這類題目的產生。沒有辦法達到絕對的公平只能盡力去靠近。
問題3:如何簡化題目不重復
解決過程:解決這個問題的第一個想法是思路①,窮舉所有題目進行抽取。之後我們通過百度查找各種解決“查重問題”方法。例如利用hashmap的映射。並不是看的很懂,但從中找到了解決問題的靈感。存儲題目中的參數和答案到list中通過list.contains()方法進行查重。玩過24點的只到或許這並不是一個特別完美的方法,但這個方法從一方面上杜絕了這一現象,將錯誤查重的概率減到了很小。
問題4:如何解決除法方法無余數的運算
解決過程:最初我們采取逆向思維,隨機除數和商相乘得到被除數來輸出題目保證得到題目和答案不存在余數,解決了當我們隨機除號在前時的問題。隨機除號在我們已經得到的數字的後面,如何隨機出適合的除數我們在掙紮過窮舉被除數的所有因數時,得到Q群裏一個同學提供的方法。隨機除數,得到的余數用被除數減去它形成新的表達式解決了問題。
問題5:分工
解決過程:隊友專門負責編寫輸入以及查重方面的代碼,對其進行調試,而我負責運算方面。運算方面的代碼對我來說有點頗困難,在她完成她的代碼之後,我們互相討論運算代碼中除法的難點,一起解決了它。
1. 調試日誌
記錄編碼調試的日誌,請記錄下開發過程中的 debug 歷程
- 在二年級除法運算中,因為需要保留上一次的要求,所以導致這次的余數不好解決
解決方案:用一個數組存余數,在題數的for循環下判斷,如果是二年級的除法,就在這個答案的後面增加余數 - 三年級除法運算的括號問題
解決方案:先規定運算符的優先級後,隨機抽取運算符,這個運算符比上個運算符優先級弱的話,在上個運算符的前後加上括號 - 三年級除法要求沒余數
解決方案:隨機一個除數,將被除數與它相模,如果有余數則將被除數減去余數
2. 關鍵代碼
請展示一段程序的關鍵代碼,並解釋代碼的作用
以下代碼是運算的代碼,包括調用運算方法以及運算方法的代碼:
//加法
private static void add(int i,StringBuffer word,int[] answer, int grade,int[] sub, int m) {
// TODO 自動生成的方法存根
if(grade==1) {
num1 = (int) (Math.random()*(answer[i]-1));
sub[m]=num1;
}else {
num1 = (int) (Math.random()*99);
sub[m]=num1;
}
word.insert(word.length()," + "+num1);
answer[i] = answer[i]+num1;
}
//減法
private static void sub(int i,StringBuffer word, int[] answer,int[] sub, int m) {
// TODO 自動生成的方法存根
num1 = (int) (Math.random()*(answer[i]-1));
sub[m]=num1;
word.insert(word.length(),"-"+num1);
answer[i] = answer[i]-num1;
}
//乘法
private static void mul(int i,StringBuffer word, int[] answer, int grade,int[] sub, int m){
// TODO 自動生成的方法存根
if(grade==2) {
num1 = (int) (Math.random()*9);
sub[m]=num1;
}
else {
num1 = (int) (Math.random()*99);
sub[m]=num1;
}
word.insert(word.length(), "x"+num1);
answer[i]= answer[i]*num1;
}
//除法
private static String div(int i,int j,int f, StringBuffer word, int[] answer,int grade,int[] sub, int m, int[] remb, int k) {
// TODO 自動生成的方法存
if(grade==2) {
num1 = 1+(int)(Math.random()*9);
rem=answer[i]%num1;
remb[k] = rem;
sub[m]=num1;
word.insert(word.length(),"÷"+num1);
answer[i]=answer[i]/num1;
}
else{
test = 1+(int)(Math.random()*2);
if(answer[i]==0 && f-j<=2) return "false";
if((test==2 || answer[i]==0) && (f-j>2)) {
num1 = 1+(int)(Math.random()*(answer[i]-1)); //除數
if(answer[i]>99) {num1 = 1+(int)(Math.random()*99);}
rem=answer[i]%num1;
sub[m]=num1;
if(rem>0) {word.insert(0, "(");
word.insert(word.length(), "-"+rem+") ÷"+num1);
answer[i]=(answer[i]-rem)/num1;
}else {
word.insert(word.length(),"÷"+num1);
answer[i]=answer[i]/num1;}
return "j++";
}
num1 = (int)(Math.random()*((1000/answer[i])+1)); //被除數
sub[m]=num1*answer[i];m++;
word.insert(0,(num1*answer[i])+"÷");
answer[i] = num1;
return "true";
}
return null;
}
private static void operation(int n, int grade) {
// TODO 自動生成的方法存根
//str數組儲存了運算符的等級和符號;answer數組包含式子答案;word數組儲存題目;f是隨機運算符的個數;num是隨機的第一個數;g是選擇第g個運算符;i是第i道題;j是當前符號數量
int k=0;//設置等級
int[] str= {0,0,1,1}; // 0為+-,1為*/
int[] answer = new int[n]; //存答案的數組
int[] sub = new int[6];//存數字的數組
int[] remb = new int[n];
File file = new File("out.txt");
try {
p = new PrintWriter(new FileOutputStream(file.getAbsolutePath()));
out = new FileOutputStream(file);
id = new StringBuffer("");
for(int i=0;i<n;i++) {//出n道題目的循環
int m=0; //存數字個數
int level=1;
if(grade==1) {
g = (int)(Math.random()*(str.length-2));
answer[i]=(int) (Math.random()*99);
sub[m]=answer[i];m++;
word= new StringBuffer(answer[i]+"");
if(g==1) {
add(i,word,answer,g,sub,m);m++;
}
else {
sub(i,word, answer,sub,m);m++;
}
}
else if(grade==2) {
g = (str.length-2)+(int)(Math.random()*str.length);
if(g==3) {
answer[i]=(int) (Math.random()*99);
sub[m]=answer[i];m++;
word= new StringBuffer(answer[i]+"");
div(i,0,0,word, answer,grade,sub,m,remb,k);
m++;k++;
}
else {
answer[i]=(int) (Math.random()*9);
sub[m]=answer[i];m++;
word= new StringBuffer(answer[i]+"");
mul(i,word, answer,grade,sub,m);
m++;
}
}
else {//三年級
f = 2+(int) (Math.random()*3); //f:隨機抽取運算符的個數
g = (int)(Math.random()*(str.length-1));//第一個運算符
answer[i]= (int) (Math.random()*99);
sub[m]=answer[i];m++;
word= new StringBuffer(answer[i]+"");
final int g1=g;
for(int j=0;j<f;j++) {
if(level<str[g] && j>f-2 && j>=2) break;//在需要加括號的情況下運算符不夠用
if(level<str[g]) {
//加括號
word.insert(0, "(");
word.insert(word.length(), ")");
j++;
}
level=str[g];
//隨機選擇符號
if(g==0) {
add(i,word, answer,g,sub,m);m++;
}
else if(g==1) {
sub(i,word, answer,sub,m);m++;
}
else if(g==2) {
if(div(i,j,f,word, answer,grade,sub,m,remb,k).equals("j++")){
j=j+2;
}else if(div(i,j,f,word, answer,grade,sub,m,remb,k).equals("false")) {
i--;break;
}
m++;k++;
}
else {
mul(i,word, answer,grade,sub,m);m++;
}
g = (int)(Math.random()*(str.length));
if(answer[i]>=100) {
g = (int)(Math.random()*(str.length-1));
}
while(g==g1 && j==0)
{
g = (int)(Math.random()*(str.length));
if(answer[i]>=100) {
g = (int)(Math.random()*(str.length-1));}
}
}
}
word.insert(0, "("+(i+1)+")");
if(grade==2 && g==3 && remb[k-1]!=0) {
id.insert(id.length(), word+"="+answer[i]+"..."+remb[k-1]+"\n");
}
else {
id.insert(id.length(), word+"="+answer[i]+"\n");
}
word.insert(word.length(),"\n");
System.out.println(word);
System.out.println(answer[i]);
p.write(word.toString());
}
p.write("\n");
p.write(id.toString());
p.close();
out.close();
} catch (IOException e) {
// TODO 自動生成的 catch 塊
e.printStackTrace();
}
}
3. 代碼規範
請給出本次實驗使用的代碼規範:
- 第一條:常量命名全部大寫,單詞鍵用下劃線隔開,力求語義表達完整,不要嫌名字長。
- 第二條:類型與中括號緊挨相連來定義數組。
- 第三條:左小括號和字符之間不出現空格;同樣的,有小括號和字符之間也不出現空格。詳見第5條下面正例提示。
- 第四條: if/for/while/switch/do等保留字與括號之間都必須加空格。
- 第五條:采用4個空格縮進,禁止使用tab字符
五、測試
請思考並記錄你認為必要的測試點,並記錄測試用例與測試結果
測試用例 | 結果 |
---|---|
java MathExam6301 -n 10 -grade 1 | 將1年級的加減法正確地輸入到out.txt文件中 |
java MathExam6301 -n 10 -grade 2 | 將2年級的乘除法正確地輸入到out.txt文件中 |
java MathExam6301 -n 10 -grade 3 | 將3年級的混合四則運算法正確地輸入到out.txt文件中 |
java MathExam6301 -grade 1 -n 10 | 程序正常運行 |
java MathExam6301 | 輸入的參數應為4個 |
java MathExam6301 -n as -grade sfas | 輸入的年級數與題數應為數字 |
java MathExam6301 1 1 | 查找不到-n或者-grade |
java MathExam6301 -n 1 -grade 5 | 輸入年級應在一到三年級 |
java MathExam6301 -n 1000000 -grade 1 | 輸入題目數量應該在1~1000 |
六、總結
不得不說,這一次結對作業對我的幫助還是很大的。分工之後,雙方有更多的時間溝通交流,在敲代碼初期便把準備工作做好了;打代碼中,不會的問題還可以互相交流,雖然我的問題真多,可是慢慢學之後我又進步了。
一度地敲代碼,讓我只想用呵呵兩字。又能表示自己的吐槽之意,也表示我的開心想法。打不出來,一直盯著電腦,眼睛都快炸了,依舊 賣呆咯。但是,突破困難之後,舒服。。。。是真的舒服。
我還有很長的路要走,我一直在拖隊友的後腿,不甘心,真的很不甘心。
最後,送上我和我隊友的表情包哈!
三年級小學生計算大法