一、题目描述
- 输入参数count控制生成题目的个数。该参数必须给定。
- 使用 参数range控制题目中数值(自然数、真分数和真分数分母)的范围,如果range=10将生成10以内(不包括10)的四则运算题目。该参数可以设置为1或其他自然数。该参数必须给定。
- 计算结果要求将浮点数转为真分数。
- 每道题目中出现的运算符个数不超过3个。
- 程序应能支持一万道题目的生成。
- 每生成一道题目必须在控制台输入一次答案才能继续答题。
- 真分数在输出时采用如下格式,真分数五分之三表示为3/5,真分数二又八分之三表示为2’3/8。
- 答题记录保存在exercises.txt文件中,里面包括当时时间,正确和错误题号还有正确率。
二、 需求分析
随机生成四则运算题目看似不难,实则想要达到对应目的以及附加条件也不乏思考,面对即将大四的关键时刻,我们有时候更多的考虑考研或者实习方面的问题,从而忘记了对基本知识的巩固与探索。四则运算生成软件的社会需求可以考虑到小学数学方面出卷出题方面的需要,尤其是在此加入了查重、随机符号、真分数与最简分数方面的附加条件之后,这倒比较简单的题目也变得复杂了起来。此次试验不仅是对社会需求方面的分析,更是在这个关键时期对自己所学知识的再次掌握与强化实践,相信这会成为一次很好的自我检测的课程实验,对应此次实验所运用的思维方法必然更加牢记于心,从长远说,这次课题实验的必要性不言而喻,这不仅符合当下小学课题需要,更加是一次必要的自我认知与挑战。在我看来,通过这次实验完全能够达到对自我编程水平划分的目的,见微知著,每个人在计算机行业所表现出的天赋与技巧高低明显,同样暴露出的问题也更加珍贵,在以后的日子里也需要不断鞭策自我进行完善。
三、功能设计
在该程序中实现了四则运算题目的产生和计算。根据输入的题数num、数字的大小范围range来产生相应的题目。做题记录和答案会存放在exercise.txt文件夹下,显示出正确错误的题号,还有正确率和答题时间。
四、设计实现
Arithmetic类用于生成表达式和完成后缀表达式的转换,Fraction类完成对分数的操作包括四则运算。
五、代码说明
//中缀转后缀
public void trans(char[] exp){
Stack op=new Stack();
op.push("=");
int i=0;
while(i<exp.length){
if(!isop(exp[i]))
{
while(exp[i]>='0' && exp[i]<='9')
{
postexp=postexp+exp[i];
int tmp=i;
i=i+1;
if(i>=exp.length){
break;
}
}
postexp=postexp+"#";
}
else
{
char[] tmp=op.peek().toString().toCharArray();
switch(precede(tmp[0],exp[i]))
{
case -1:
op.push(exp[i]);
i++;
break;
case 0:
op.pop();
i++;
break;
case 1:
postexp=postexp+op.peek().toString();
op.pop();
break;
}
}
}
while(op.peek()!="=")
{
postexp=postexp+op.peek().toString();
op.pop();
}
postexpArr=postexp.toCharArray();
}
// 后缀表达式求值
public String compvalue(){
String a,b,c;
int d;
int temp;
Stack st=new Stack();
int i=0;
while(i<postexpArr.length)
{
switch(postexpArr[i])
{
//不管是整数还是分数,统一加"/1"转为分数来处理四则运算
case '+':
//这里不能转为int,因为栈取出来的数都是带小数点的,比如20.0,这种数在java里面是不能转int
a=st.pop().toString();
b=st.pop().toString();
c=Fraction.add(a+"/1", b+"/1");
st.push(c);
break;
case '-':
a=st.pop().toString();
b=st.pop().toString();
c=Fraction.sub(b+"/1", a+"/1");
st.push(c);
break;
case '×':
a=st.pop().toString();
b=st.pop().toString();
c=Fraction.multi(a+"/1", b+"/1");
st.push(c);
break;
case '÷':
a=st.pop().toString();
b=st.pop().toString();
c=Fraction.divide(b+"/1", a+"/1");
st.push(c);
break;
default:
d=0;
while(postexpArr[i]>='0' && postexpArr[i]<='9'){
d=10*d+postexpArr[i]-'0';
int tmp=i;
i=i+1;
if(i>=postexpArr.length){
break;
}
}
st.push(d);
break;
}
i++;
}
return st.peek().toString();
}
// 生成无括号表达式
public String createExp(int range){
String exp="";
//第一个随机种子用于确定除了括号的运算符,不能大于3个
Random r1=new Random();
int opNumber=r1.nextInt(3)+1;
String[] op=new String[opNumber];
Random r3=new Random();
for(int m=0;m<opNumber;m++){
switch(r3.nextInt(4))
{
case 0:op[m]="+";break;
case 1:op[m]="-";break;
case 2:op[m]="×";break;
case 3:op[m]="÷";break;
}
}
//第二个随机种子用于生成操作数
Random r2=new Random();
//操作数个数比运算符多1
int[] number = new int[opNumber+1];
for(int j=0;j<opNumber+1;j++){
number[j]=r2.nextInt(range);
}
int a=0;
int b=0;
for(int x=0;x<2*opNumber+1;x++){
if(x%2==0){
exp=exp+number[a];
a++;
}else{
exp=exp+op[b];
b++;
}
}
return exp;
}
// 给表达式添加括号
public String addBrackets(String exp){
//为生成的表达式字符串加上括号
//先获得原先字符串的操作符
String[] opArr=new String[(exp.length()-1)/2];
int d=0;
for(int c=0;c<exp.length();c++){
if(exp.charAt(c)=='+'||exp.charAt(c)=='-'
||exp.charAt(c)=='×'||exp.charAt(c)=='÷'){
opArr[d]=exp.substring(c,c+1);
d++;
}
}
//获取各个操作数
String[] nArr=exp.split("\\+|-|\\×|÷");
Random r4=new Random();
Random r5=new Random();
while(1>0){
int n1=r4.nextInt(nArr.length);
int n2=r5.nextInt(nArr.length);
if(n1<n2){
nArr[n1]="("+nArr[n1];
nArr[n2]=nArr[n2]+")";
break;
}else if(n1>n2){
nArr[n2]="("+nArr[n2];
nArr[n1]=nArr[n1]+")";
break;
}else{
continue;
}
}
exp="";
for(int x=0;x<opArr.length;x++){
exp=exp+nArr[x];
exp=exp+opArr[x];
}
exp=exp+nArr[nArr.length-1];
return exp;
}
//分数类
package arithmetic;
public class Fraction {
//分子
private int numerator;
//分母
private int denominator;
//存放正负的标志
private int mark;
public Fraction(String s){
if(s.indexOf('-')==-1){
this.mark=1;
}else{
this.mark=-1;
}
String[] arr=s.split("/");
int a=Math.abs(Integer.parseInt(arr[0]));
int b=Integer.parseInt(arr[1]);
this.numerator=a;
this.denominator=b;
}
//约分
public static String deal(String s){
String[] arr=s.split("/");
int a=Math.abs(Integer.parseInt(arr[0]));
int b=Integer.parseInt(arr[1]);
int gcd=gcd(a,b);
a=a/gcd;
b=b/gcd;
if(s.indexOf('-')==-1){
return a+"/"+b;
}else{
return "-"+a+"/"+b;
}
}
//求最大公约数
public static int gcd(int m,int n){
if (n == 0) {
return m;
}
else {
return gcd(n, m % n);
}
}
//加法
public static String add(String a,String b){
Fraction o1=new Fraction(a);
Fraction o2=new Fraction(b);
String result=(o1.mark*o1.numerator*o2.denominator+o2.mark*o2.numerator*o1.denominator)+"/"+(o1.denominator*o2.denominator);
return deal(result);
}
//减法
public static String sub(String a,String b){
Fraction o1=new Fraction(a);
Fraction o2=new Fraction(b);
String result=(o1.mark*o1.numerator*o2.denominator-o2.mark*o2.numerator*o1.denominator)+"/"+(o1.denominator*o2.denominator);
Fraction o3=new Fraction(deal(result));
return deal(result);
}
//乘法
public static String multi(String a,String b){
Fraction o1=new Fraction(a);
Fraction o2=new Fraction(b);
String result=(o1.mark*o1.numerator*o2.mark*o2.numerator)+"/"+(o1.denominator*o2.denominator);
Fraction o3=new Fraction(deal(result));
return deal(result);
}
//除法
public static String divide(String a,String b){
Fraction o1=new Fraction(a);
Fraction o2=new Fraction(b);
String result=o1.numerator*o2.denominator*o1.mark*o2.mark+"/"+o1.denominator*o2.numerator;
Fraction o3=new Fraction(deal(result));
return deal(result);
}
}
六、测试运行
七、psd个人过程
八、小结
在实验过程中只用到了数据结构中的栈来完成后缀表达式的转换和求值,没有用到二叉树导致没有完成查重功能,今后会继续学习二叉树的相关知识。重温了java中集合类的使用,有list和map。注意到了两个小细节,栈取出来的是对象类型,转为string之后数值型数据会带上小数点比如20.0,这时候用Integer.parseint()不会产生相应整数,所以只能将转为浮点型。还有一个就是打开文件输出流必须及时关闭,不然内容不会写入文件。
九、项目地址:https://gitee.com/wujinhan/test