、GitHubのプロジェクトアドレス
https://github.com/lyjkekeke/Arithmetic
プロジェクトメンバー:劉ゆう6月、パンBeiwen
二、PSP用フォーム
PSP2.1 |
パーソナルソフトウェアプロセス段階 |
推定時間がかかる(分) |
実際の時間がかかる(分) |
プランニング |
計画 |
30 |
30 |
・見積り |
•このタスクが必要と推定どのくらいの時間 |
30 |
30 |
開発 |
開発 |
2340 |
2565 |
・分析 |
・分析(新しい技術を学ぶ含む)が必要 |
210 |
180 |
・デザインスペック |
設計ドキュメントの生成 |
90 |
120 |
・デザインレビュー |
・デザインレビュー(と彼の同僚は、設計文書を見直し) |
60 |
30 |
・コーディング標準 |
・コードの仕様(現在の開発のための適切な規範の開発) |
60 |
45 |
・ 設計 |
・具体的な設計 |
500 |
400 |
・コーディング |
・具体的なコーディング |
1000年 |
1200 |
・コードレビュー |
・コードレビュー |
120 |
90 |
・テスト |
・テスト(セルフテスト、コードを変更し、変更を提出) |
300 |
500 |
報告 |
レポート |
275 |
240 |
・ 試験報告書 |
・テストレポート |
240 |
200 |
・サイズ測定 |
・コンピューティングのワークロード |
15 |
15 |
・死後&プロセス改善計画 |
・後知恵、およびプロセス改善計画を提案します |
20 |
25 |
トータル |
|
2610 |
2835 |
第三に、パフォーマンス分析
当初、我々は機能がランダムに対象に生成実装と同時に答えを作り出す、我々は2つの式の後置式は、彼と同じである考慮に入れるので、もし私たちが、接尾式ビルダーの回答を使用しているため、この時点で過ごした時間は、あまりないです計算手順のすべてのステップは、HashSetの貯蔵後置記法を再生成するために、同じであり、各タスクはそのaddメソッドを使用することで生成されることは成功していない時間があまりない過ごしたが、我々は後置式評価を考慮することを忘れものの、この時点で追加した場合+に×と同じ問題について交流ので、私たちは、元のコードを再最適化し、限り式は識別することが最初のと同じであるとして、再確認するために、式に基づいて接尾辞の前に彼の計算で表現を生成交換は、将来的に何度も持ち上げながら続けて、あなたはより正確な結果を得るが、この時の複雑×+の後や比較については、我々はさらに最適化するであろう異なるものになる場合
プログラムは、最大の機能を消費します:
生成万100は4つの数式で繰り返されることはありません
第四に、設計と実装プロセス
メインクラスAri_mainクラスは、4つの3ボタンとグラフィカルなインターフェイスのテキスト領域を含むクラスの初期化Jfraグラフィカルインターフェースを呼び出す:機能に対応するボタンは、「タイトルを生成すること」であり、「ファイルを保存」と「答えをチェックし、」テキスト「ファイルをアップロード」左から右の機能領域への回答で答えを入力するか、アップロードされたファイルの内容を表示し、生成されたタイトルを表示され、右または間違った答え、正解と解答状況トピックを表示します。再チェック、最後のコールformJudgeクラス計算の答えを表現したボタンの最初の呼び出しクリエータークラスの生成問題「と題するの生成」、その後、生成されたトピックにDupCheckクラスを呼び出すをクリックして、ファイルに件名を生成するために、「ファイルを保存」ボタンをクリックしてください選択したファイル、およびテキスト領域の中央に印刷中のファイルの内容を読み取るために、ボタンをクリックし、「答えをアップロード」;と同じパスに保存され、対応する応答ファイルを、生成しながら、ローカルに保存された「答えをチェック」ボタンをクリックしてください2番目のテキストエリアの内容がリストにそれらの中に、行ずつ読み出し、それが正しい答えに確認してください。
図に示すように、4.1のクラスおよび機能は、プログラムに含まれます。
4.2以下では重要な機能を示すフローチャートであります
4.2.1タスクが生成される(括弧を含む生成)
4.2.2生成の回答
4.2.3重複チェック
V.コード説明
5.1ランダムに生成されたタイトル
//创建一个新题目 /*创建思路:首先通过随机数来确定生成的题目中有几个数字,然后通过for循环 * 循环生成2*n个随机数,这时每两个随机数可以组成一个分数,并且根据要求的 * 形式化简,接着通过随机数来判断生成的运算符从而随机生成n-1个运算符,然后将 * 这些按顺序连接成字符串返回*/ public numItem pro_creater(int range) { //创建rc,rn,ro来存储随机数从而随即得到 int rc,rn,ro; numItem str = new numItem(); String [] ch = new String[] {"+","-","×","÷"}; rc = (int)(Math.random()*3+2);//随机生成2~4的数来判断生成几个数 int [] a = new int[2*rc]; for(int i=0; i < 2*rc; i++) { a[i] = 1; } String [] num_str = new String[rc]; String [] num_str1 = new String[rc]; String [] b = new String[rc-1]; for(int j = 0; j < rc; j++) { rn = (int)(Math.random()*(2-0));//随机生成0和1判断生成真分数或整数和带分数 if(rn == 1) { a[2*j+1] = (int)(Math.random()*range+1); a[2*j] = (int)(Math.random()*(a[2*j+1]-1)+1); num_str[j] = fj.proSimple(a[2*j],a[2*j+1]); } else{ a[2*j] = (int)(Math.random()*range+1); a[2*j+1] = (int)(Math.random()*(a[2*j]-1)+1); num_str[j] = fj.improSimple(a[2*j],a[2*j+1]); } num_str1[j] = a[2*j] + "/" + a[2*j+1]; } for(int k =0; k < rc-1; k++) { ro = (int)(Math.random()*(4-0));//生成0到3的随机数来判断生成什么运算符 b[k] = ch[ro]; } //加括号 str = parent_creater(rc,num_str,num_str1,b); return str; }
5.1.1创建题目中有生成括号的功能,以下为生成括号的代码
//添加括号 /*思路:生成一个随机数来判断生成多少个括号,左括号和右括号必须成对出现,构建一个 * 最终字符串数组,一个左括号位置数组和一个右括号位置数组,如果是两个数字则不用生 * 成括号,如果是两个以上则随机生成最多n/2+1个括号,生成该范围内的随机数来判断生 * 成多少括号,先生成随机左括号的位置,然后根据左括号位置随机生成右括号位置,并将 * 两个同时放入数组,若右括号位置找出范围则停止,最后将他们和已有字符连接生成最终式子*/ public numItem parent_creater(int rc, String [] num_str, String [] num_str1, String [] b) { numItem str = new numItem(); String [] spl = new String[rc]; String [] spr = new String[rc]; for(int i = 0; i < rc; i++) { spl[i] = ""; spr[i] = ""; } int rp = (int)(Math.random()*(rc/2+1-0)); if(rc == 2) { str.newstr = num_str[0] + " " + b[0] + " " + num_str[1]; str.oldstr = num_str1[0] + " " + b[0] + " " + num_str1[1]; } else { int lmin = 0; for(int i = 0; i < rp; i++) { int rpl = (int)(Math.random()*(rc-1-lmin)+lmin);//生成左括号位置 int rpr = (int)(Math.random()*(rc-rpl-1)+rpl+1);//根据左括号位置生成右括号位置 if(rpl == 0 && rpr ==rc-1) { i--; continue; } if(rpr > rc-1) break; spl[rpl] = "("; spr[rpr] = ")"; lmin = rpr+1; } if(spl[0] == "") { str.newstr = num_str[0] + " " + spr[0]; str.oldstr = num_str1[0] + " " + spr[0]; } else if(spr[0] == "") { str.newstr = spl[0] + " " + num_str[0]; str.oldstr = spl[0] + " " + num_str1[0]; } else{ str.newstr = spl[0] + " " + num_str[0] + " " + spr[0]; str.oldstr = spl[0] + " " + num_str1[0] + " " + spr[0]; } for( int i = 1; i < rc; i++) { str.newstr = str.newstr + " " + b[i-1] + " " + spl[i] + " " + num_str[i] + " " + spr[i]; str.oldstr = str.oldstr + " " + b[i-1] + " " + spl[i] + " " + num_str1[i] + " " + spr[i]; } } return str; }
5.2 生成答案
5.2.1中缀表达式转后缀表达式
//通过后缀表达式计算数值 /* 1. 从左到右遍历表达式的每个数字和符号 * 1.1 若是数字则进栈 * 1.2 若是运算符则将栈顶两个元素出栈,进行运算并将运算结果进栈 * 2. 遍历完后缀表达式,此时栈中剩余的数字就是运算结果 */ private String calculateByPostfix(List<Item> postfixes) { Stack<String> stack = new Stack<String>(); for (Item item : postfixes) {//遍历后缀表达式 if (item.isNumber()) {//若为数则直接入栈 stack.push(item.value); } else {//若为操作符则将栈顶两个元素取出并进行相应运算 //将得到的两个字符串分数转化成Number型 String num_1,num_2; Number num1 = new Number(); Number num2 = new Number(); Number result = new Number(); num_1 = stack.pop(); num_2 = stack.pop(); num1 = returnNum(num_1); num2 = returnNum(num_2); if (item.isAdd()) {//计算加法 result.a = num2.a * num1.b + num1.a * num2.b; result.b = num2.b * num1.b; } else if (item.isSub()) {//计算减法 result.a = num2.a * num1.b - num1.a * num2.b; result.b = num2.b * num1.b; if(result.a < 0) return null;//若为负数则不生成答案 } else if (item.isMul()) {//计算乘法 result.a = num2.a * num1.a; result.b = num2.b * num1.b; } else if (item.isDiv()) {//计算除法 result.a = num2.a * num1.b; result.b = num2.b * num1.a; } else { throw new IllegalArgumentException("Operator invalid : " + item.value); } stack.push(result.a + "/" + result.b); } }
5.2.2后缀表达式计算结果
//通过后缀表达式计算数值 /* 1. 从左到右遍历表达式的每个数字和符号 * 1.1 若是数字则进栈 * 1.2 若是运算符则将栈顶两个元素出栈,进行运算并将运算结果进栈 * 2. 遍历完后缀表达式,此时栈中剩余的数字就是运算结果 */ private String calculateByPostfix(List<Item> postfixes) { Stack<String> stack = new Stack<String>(); for (Item item : postfixes) {//遍历后缀表达式 if (item.isNumber()) {//若为数则直接入栈 stack.push(item.value); } else {//若为操作符则将栈顶两个元素取出并进行相应运算 //将得到的两个字符串分数转化成Number型 String num_1,num_2; Number num1 = new Number(); Number num2 = new Number(); Number result = new Number(); num_1 = stack.pop(); num_2 = stack.pop(); num1 = returnNum(num_1); num2 = returnNum(num_2); if (item.isAdd()) {//计算加法 result.a = num2.a * num1.b + num1.a * num2.b; result.b = num2.b * num1.b; } else if (item.isSub()) {//计算减法 result.a = num2.a * num1.b - num1.a * num2.b; result.b = num2.b * num1.b; if(result.a < 0) return null;//若为负数则不生成答案 } else if (item.isMul()) {//计算乘法 result.a = num2.a * num1.a; result.b = num2.b * num1.b; } else if (item.isDiv()) {//计算除法 result.a = num2.a * num1.b; result.b = num2.b * num1.a; } else { throw new IllegalArgumentException("Operator invalid : " + item.value); } stack.push(result.a + "/" + result.b); } } //返回化简成分数或整数或带分数的结果 Number num = new Number(); String ans = stack.pop(); num = returnNum(ans); if(num.a < num.b) return proSimple(num.a,num.b); else return improSimple(num.a,num.b); }
5.3生成答案
5.3.1后缀表达式转化成通过计算过程产生的查重表达式
//将后缀表达式转化为可以查重的表达式的形式,即按照计算过程排列的表达式 /*由于运算结果时会用到后缀表达式,在这里将后缀表达式转换成可以查重的表达式 * (过程运用堆栈且基本和计算后缀表达式一的做法).思路主要是以后缀表达式的 * 长度循环,如果遇到数字就压栈,遇到运算符就连续出栈两个数字字符,出栈后将“#” * 压入数字栈,这个字符就类似计算后缀表达式时的结果,完成循环后可得到查重的表达式*/ public List<String> getDupExpression(List<Item> postfixes) { List<String> dup = new ArrayList<String>(); Stack<String> stack = new Stack<String>(); for (Item item : postfixes) { if (item.isNumber()) { stack.push(item.value); } else { String result = "#",num1,num2; dup.add(item.value); num1 = stack.pop(); num2 = stack.pop(); if(num1 != "#") { dup.add(num1); }else { dup.add("null"); } if(num2 != "#") { dup.add(num2); }else { dup.add("null"); } stack.push(result); } } return dup; }
5.3.2通过查重表达式查重
//判断两个字符串是否重复 public boolean dupCheck(String str1,String str2) { formJudge jd = new formJudge(); List<String> s1 = getDupExpression(jd.infix2postfix(jd.parse(str1)));//将字符串s1转化成字符列表 List<String> s2 = getDupExpression(jd.infix2postfix(jd.parse(str2)));//将字符串s2转化成字符列表 if(s1.equals(s2)) return true;//若两列表内容相同返回true for(int j = 0; j < s2.size()-1; j++) {//若不同进行循环将+或×后的两个字符串交换看能否相同 if(s2.get(j).equals("+")||s2.get(j).equals("×")) { String temp = s2.get(j+1); s2.set(j+1,s2.get(j+2)); s2.set(j+2,temp); } j*=3; if(s1.equals(s2)) return true;//若交换后相同则返回true } return false; }
5.4核对答案
//点击核对答案的事件 b4.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // TODO Auto-generated method stub a3.setText(""); String [] ansline = a2.getText().split("\n"); int correct = 0,error = 0; for(int i = 0; i < ansline.length ; i++) { if(anslist.get(i).equals(ansline[i])){ a3.append("√" + " " + "正答:" + " " + anslist.get(i) + '\n'); correct++; }else { a3.append("×" + " " +"正答:" + " " + anslist.get(i)+ '\n'); error++; } } a3.append("正确数目为:" + correct + '\n'); a3.append("错误数目为:" + error + '\n'); } });
六、测试运行
我们能够确定生成的答案的正确性是通过多次测试得到的,我们计算过每个式子的答案是否真正正确,也填过错误的答案来判断核对答案功能是否成功,同理,其他功能的正确性也是如此
6.1打开后进入图形界面
6.2点击生成题目
6.3点击保存文件
6.4点击上传答案
6.5点击核对答案
6.6 测试查重
七、项目小结
通过这次项目,我们对结对编程有了一个更深的了解。通过结对,在代码设计的过程中,在编程过程能够更快地找到代码中的漏洞,并且比较容易找到解决之法。当然因为双方的思考方式是不一样的,因此在讨论的过程中会出现思想的碰撞,从而拖延了项目的进度。我们了解到在合作的过程中,要多从对方的角度考虑,不要固执己见。