碼市的鏈接:https://coding.net/u/NianQiFeng/p/C-yunsuan/git
一、需求分析
現在家長和老師們都十分注重對小學生計算思維的培養,都希望自己的孩子可以贏在起跑線上,所以大部分老師都會給學生布置不少家庭作業。而老師也往往會利用家長的力量,囑咐家長在家里為小孩子出題目。但是,大部分家長工作繁忙或者對小學的知識早已淡忘,所以不少家長選擇為孩子找家教來做這個工作。
為了讓家長可以輕輕松松為小孩子出題,不用特地破費請家教,就需要我們研發出一個操作簡單、可以隨機生成任意題目數的四則運算自動生成器。這款自動生成器不僅局限於整數運算,分數運算才是它的特點,由於數字是隨機生成的,因此分數計算的比重遠遠大於整數運算,而整數運算只是分數運算的基礎,因此只要能夠熟練掌握分數運算,則整數運算也就不在話下。
這款運算生成器將大概率出現分數運算,提高了整個題目的難度,對學生的計算提高有很大的幫助。另外,在學生作答之后,立即就會顯示出正確的答案,判斷學生的答案是否正確,在最終答題結束之后,還會計算出正確率,利於家長對孩子掌握情況的了解。
二、功能設計
基礎功能:
1、除了整數以外,還要支持真分數的四則運算,真分數的運算,例如:1/6 + 1/8 = 7/24;
2、運算符為 +, −, ×, ÷;
3、要求能處理用戶的輸入,並判斷答案是否正確,並統計正確率;
4、要求能處理用戶輸入的真分數,用戶輸入的答案為分數時,能同樣判斷對錯,如 1/2, 5/12 等;
5、可以使用 -n 參數控制生成題目的個數,例如執行下面命令將生成10個題。如:Myapp.exe -n 10;
擴展功能:
6、對於隨機生成的分數,利用輾轉相除法改進過來的遞歸算法來計算最大公約數,使之最簡;
7、在答題者輸入一道題的答案之后,程序會立即顯示該題的正確答案,告訴答題者該題是否正確。
三、代碼實現
這個實驗我一開始就用的是C語言來寫,所以從頭到尾也都是通過C語言來完成。
int a, b, c, d, op;
a = rand() % 100;
while (1) {
b = rand() % 100;
if (b != 0) {
break;
}
}
c = rand() % 100;
while (1) {
d = rand() % 100;
if (d != 0) {
break;
}
}
op = rand() % 4;
以上這段代碼是用來實現隨機生成數字的,其中b和d作為除數,不能為0,所以只有當b和d不為0時,才可以break出來,進行后續的計算。
int m, n;
if(op == 0){
m = a * d + b * c;
n = b * d;
}
else if (op == 1) {
m = a * d - b * c;
n = b * d ;
}
else if (op == 2) {
m = a * c;
n = b * d;
}
else {
m = a * d;
n = b * c;
}
這一段代碼是用來計算題目的正確答案的,其中op=0,1,2,3分別對應着“+”、“-”、“*”、“/”。
int gcd(int a, int b)
{
if (b == 0)
return a;
return gcd(b, a % b);
}
int tmp = abs(gcd(a, b));
a /= tmp;
b /= tmp;
tmp = abs(gcd(c, d));
c /= tmp;
d /= tmp;
這兩小段代碼雖然不長但卻很精練,作用在於分數的化簡,gcd函數用來求兩個數的最大公約數。
四、測試運行


如圖,可以指定自動生成十道題,在輸入每道題的答案之后,程序會顯示該題的正確答案,並判斷該題是答對還是答錯。在十道題答完之后,最后會顯示正確幾道題,錯誤幾道題,正確率是多少。
五、psp展示
| PSP2.1 |
Personal Software Process Stages |
Time (m) Senior Student |
Time (m) |
| Planning |
計划 |
6 |
4 |
| · Estimate |
估計這個任務需要多少時間 |
8 |
10 |
| Development |
開發 |
60 |
88 |
| · Analysis |
需求分析 (包括學習新技術) |
6 |
6 |
| · Design Spec |
生成設計文檔 |
5 |
6 |
| · Design Review |
設計復審 |
4 |
6 |
| · Coding Standard |
代碼規范 |
3 |
3 |
| · Design |
具體設計 |
10 |
16 |
| · Coding |
具體編碼 |
24 |
50 |
| · Code Review |
代碼復審 |
5 |
12 |
| · Test |
測試(自我測試,修改代碼,提交修改) |
9 |
10 |
| Reporting |
報告 |
9 |
6 |
| · |
測試報告 |
3 |
2 |
| · |
計算工作量 |
2 |
3 |
| · |
並提出過程改進計划 |
3 |
3 |
由上面的psp表可以看出,我在開發和具體編碼這兩項上面時間與預計差別巨大。首先,在開發上,由於我一開始審題不夠認真,導致對分數運算這個概念了解不夠透徹,再加上這只是第一次的作業,讓我以為只是寫一個整數的四則運算那樣的簡單程序,所以導致對開發難度預計不足,在開發過程中遇到了不少棘手的問題。
其次,在具體寫代碼的時候,我才深切得認識到隨機的分數運算讓整個程序難了好幾個層次,因為隨機生成的數字不能直接參與計算,而要再經過一次甚至兩次的再隨機,才能得出分數。另外,分數的計算過程也跟往常的不一樣,不論是加減乘除,都要經過不同地變化,才能得出正確的答案。同時,分數還存在化簡的問題,種種困難堆在一起,一方面消磨了我的耐心,另一方面也確實需要多花很多時間來找資料解決這些問題,所以在編寫代碼的過程比預計多花了相當多的時間。
六、小結
剛剛拿到這個題目的時候,由於我審題不夠認真,誤以為第一次作業老師就是先布置簡單的,以為這次作業就是簡單的整數四則運算自動生成器,所以一開始沒有太在意,以為很快就能做完,所以我在距離deadline三天的時候才開始着手做,在做完整數四則運算程序之后,在於同學的交流中才發現原來自己做錯了,我還自以為是地以為這么簡單的題目大家怎么做的這么久。這讓我深刻地認識到拿到題目之后一定要認真地審題,否則一切的努力將白費;其次就是要謙遜地與同學多交流,可能會發現很多自己一個人所意識不到的問題。
在意識到錯誤,重新回歸正軌之后,也遇到了很多棘手的問題。首先值得我思考的是,如何去隨機產生分數和整數?經過一段時間的思考,我的想法是:先只產生分數,如果可以被化簡成整數,就用整數輸出。在學霸的幫助下,我寫了一個用輾轉相除法改成遞歸的算法來計算最大公約數以此來化簡。在之后與同學的交流中,我發現了同學們一個更好的辦法,那就是先隨機產生整數,再隨機產生加減乘除符號,再用這些整數和符號隨機搭配,就可以自動生成分數的運算。
在糾結如何檢驗答案正確的過程中,我的思路就是以我們編程計算的正確答案和用戶輸入的答案進行對比,但是如何對比呢?是用數字間相等還是用字符之間的比較,最后我選擇了用字符串的方式來存儲用戶的輸入,再以字符串之間的比較來判斷用戶的答案。
經過這次的學習,讓我重新拾起了兩年前的C語言,陌生又熟悉,感覺有趣但是又懶,這個作業真的花了超級多的時間,寫出一個程序已經很累了,可是還要寫博客、設置git,感覺浪費了很多時間,我也不知道這個時間花的值不值,希望老師們可以再考慮考慮我的吐槽。寫到這里已經凌晨12:56分了,可是我的git還沒弄成功,還卡在一個地方,如果最后我的應用程序來不及上傳到碼市上,還請老師手下留情!
七、附錄(源程序)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <conio.h>
#include <math.h>
int cnt1, cnt2;
int gcd(int a, int b)
{
if (b == 0)
return a;
return gcd(b, a % b);
}
int main(int argc, char const *argv[])
{
int n = 0;
for (int i = 0; i < strlen(argv[2]); i++) {
n = n * 10 + (argv[2][i] - '0');
}
printf("%d\n", n);
srand(time(NULL));
char input[100], ans1[100], ans2[100];
while (n--) {
memset(input, 0, sizeof(input));
memset(ans1, 0, sizeof(ans1));
memset(ans2, 0, sizeof(ans2));
fflush(stdin);
printf("輸入q退出,按其他任意鍵繼續\n");
char ch = getch();
if (ch == 'q') {
break;
}
int a, b, c, d, op;
a = rand() % 100;
while (1) {
b = rand() % 100;
if (b != 0) {
break;
}
}
c = rand() % 100;
while (1) {
d = rand() % 100;
if (d != 0) {
break;
}
}
op = rand() % 4;
int tmp = abs(gcd(a, b));
a /= tmp;
b /= tmp;
tmp = abs(gcd(c, d));
c /= tmp;
d /= tmp;
printf("%d", a);
if (b != 1) {
printf("/%d", b);
}
if (op == 0) printf(" + ");
else if (op == 1) printf(" - ");
else if (op == 2) printf(" * ");
else printf(" / ");
printf("%d", c);
if (d != 1) {
printf("/%d", d);
}
printf(" = ");
int m, n;
if(op == 0){
m = a * d + b * c;
n = b * d;
}
else if (op == 1) {
m = a * d - b * c;
n = b * d ;
}
else if (op == 2) {
m = a * c;
n = b * d;
}
else {
m = a * d;
n = b * c;
}
tmp = abs(gcd(m, n));
m /= tmp;
n /= tmp;
sprintf(ans1, "%d", m);
if (n != 1) {
sprintf(ans2, "/%d", n);
}
strcat(ans1, ans2);
gets(input);
puts(ans1);
if (strcmp(ans1, input) == 0) {
printf("right\n");
cnt1++;
} else {
printf("wrong\n");
cnt2++;
}
}
int zql;
zql = (100 * cnt1) / (cnt1 + cnt2);
printf("共答了%d道題,答對%d道題,答錯%d道題,正確率為%d%%。\n", cnt1 + cnt2, cnt1, cnt2, zql);
return 0;
}
