软件工程个人作业01

题目:     

像二柱子那样,花二十分钟写一个能自动生成三十道小学四则运算题目的 “软件”,要求:除了整数以外,还要支持真分数的四则运算(需要验证结果的正确性)、题目避免重复、可定制出题的数量。(有能力者改编成网页版)

设计思想: 

  • 项目名称:CTXT
  • 包名称:cn.simo
  • 文件名称:
    1. Arithmetic.java
    2. ChangeToFenshuDemo.java
    3. CT.java
  • main方法在文件3中,文件1中的GetQueston_int()和GetQuestion_div()方法分别用来生成整数表达式和分数表达式;
  • 文件1中定义了arithmetic()方法来求解表达式字符串的结果(返回值double型),支持分数表达式;
  • 文件2中定义了toFenshu()方法来将小数转化成分数,在GetQuestion_div()中使用它可以生成小于1的随机分数;
  • 当已做题数到达5的倍数时就出一道分数题,否则出整数题;
  • 定义一个大小30的字符串数组盛放已做题目,生成题目时需要判断是否已经做过;
  • 在文件1中计算除法时,运算数是BigDecimal型的,使用divide方法运算时如果没有告诉计算机小数位精确到哪位的话,将结果分为小数部分和整数部分,小数部分可能因为超过int数值范围而报错,所以需要在81行位置指定精度2。

源代码:

Github源码:git@github.com:54Simo/Java-Tutorial.git

Arithmetic.java

package cn.simo;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/** 
 * 四则运算表达式计算 
 * @see 功能:可以用arithmetic返回一个double类型的结果,在calculate方法中由“刘立伟”设置了精度解决某些BUG。
 * @author penli 
 */  
public class Arithmetic {  
    public static double arithmetic(String exp){  
        String result = parseExp(exp).replaceAll("[\\[\\]]", "");  
        return Double.parseDouble(result);  
    }  
    /** 
     * 解析计算四则运算表达式,例:2+((3+4)*2-22)/2*3 
     * @param expression 
     * @return String
     */  
    public static String parseExp(String expression){  
        //String numberReg="^((?!0)\\d+(\\.\\d+(?<!0))?)|(0\\.\\d+(?<!0))$";  
        expression=expression.replaceAll("\\s+", "").replaceAll("^\\((.+)\\)$", "$1");  
        String checkExp="\\d";  
        String minExp="^((\\d+(\\.\\d+)?)|(\\[\\-\\d+(\\.\\d+)?\\]))[\\+\\-\\*\\/]((\\d+(\\.\\d+)?)|(\\[\\-\\d+(\\.\\d+)?\\]))$";  
        //最小表达式计算  
        if(expression.matches(minExp)){  
            String result=calculate(expression);  
              
            return Double.parseDouble(result)>=0?result:"["+result+"]";  
        }  
        //计算不带括号的四则运算  
        String noParentheses="^[^\\(\\)]+$";  
        String priorOperatorExp="(((\\d+(\\.\\d+)?)|(\\[\\-\\d+(\\.\\d+)?\\]))[\\*\\/]((\\d+(\\.\\d+)?)|(\\[\\-\\d+(\\.\\d+)?\\])))";  
        String operatorExp="(((\\d+(\\.\\d+)?)|(\\[\\-\\d+(\\.\\d+)?\\]))[\\+\\-]((\\d+(\\.\\d+)?)|(\\[\\-\\d+(\\.\\d+)?\\])))";  
        if(expression.matches(noParentheses)){  
            Pattern patt=Pattern.compile(priorOperatorExp);  
            Matcher mat=patt.matcher(expression);  
            if(mat.find()){  
                String tempMinExp=mat.group();  
                expression=expression.replaceFirst(priorOperatorExp, parseExp(tempMinExp));  
            }else{  
                patt=Pattern.compile(operatorExp);  
                mat=patt.matcher(expression);  
                  
                if(mat.find()){  
                    String tempMinExp=mat.group();  
                    expression=expression.replaceFirst(operatorExp, parseExp(tempMinExp));  
                }  
            }  
            return parseExp(expression);  
        }  
        //计算带括号的四则运算  
        String minParentheses="\\([^\\(\\)]+\\)";  
        Pattern patt=Pattern.compile(minParentheses);  
        Matcher mat=patt.matcher(expression);  
        if(mat.find()){  
            String tempMinExp=mat.group();  
            expression=expression.replaceFirst(minParentheses, parseExp(tempMinExp));  
        }  
        return parseExp(expression);  
    }  
    /** 
     * 计算最小单位四则运算表达式(两个数字) 
     * @param exp 
     * @return String
     */  
    public static String calculate(String exp){  
        exp=exp.replaceAll("[\\[\\]]", "");  
        String number[]=exp.replaceFirst("(\\d)[\\+\\-\\*\\/]", "$1,").split(",");  
        BigDecimal number1=new BigDecimal(number[0]);  
        BigDecimal number2=new BigDecimal(number[1]);  
        BigDecimal result=null;  
        /*
         * 设置精度,否则报错(计算机并不知道要保留几位,所以很干脆的报错)
         * By 刘立伟
         */
        MathContext mc = new MathContext(2, RoundingMode.HALF_DOWN);
        //精度为2,舍入模式为大于0.5进1,否则舍弃。 
        
        String operator=exp.replaceFirst("^.*\\d([\\+\\-\\*\\/]).+$", "$1");  
        if("+".equals(operator)){  
            result=number1.add(number2);  
        }else if("-".equals(operator)){  
            result=number1.subtract(number2);  
        }else if("*".equals(operator)){  
            result=number1.multiply(number2);  
        }else if("/".equals(operator)){  
            result=number1.divide(number2,mc);  
        }  
          
        return result!=null?result.toString():null;  
    }  
}  

ChangeToFenshuDemo.java

package cn.simo;  

/**
 * 小数转分数
 * @author Happy_Girl2012
 */
public class ChangeToFenshuDemo {  
	/**
	 * 取得公约数
	 * @param a
	 * @param b
	 * @return int
	 */
    public static int getGongYueShu(int a, int b) {//求两个数的最大公约数  
        int t = 0;  
        if(a < b){  
        t = a;  
        a = b;  
        b = t;  
        }  
        int c = a % b;  
        if(c == 0){  
            return b;  
        }else{  
            return getGongYueShu(b, c);  
        }  
    }  
    /**
     * 转化成分数
     * @param xiaoshu
     * @return String
     */
    public static String toFenshu(String xiaoshu) {   
  
        String[] array = new String[2];  
        array = xiaoshu.split("\\.");  
        int a = Integer.parseInt(array[0]);//获取整数部分  
        int b = Integer.parseInt(array[1]);//获取小数部分  
        int length = array[1].length();  
        int FenZi = (int) (a * Math.pow(10, length) + b);  
        int FenMu = (int) Math.pow(10, length);  
        int MaxYueShu = getGongYueShu(FenZi, FenMu);  
        return new String(FenZi / MaxYueShu + "/" + FenMu / MaxYueShu);  
    }  
  
}  

CT.java

package cn.simo;

import java.util.Scanner;
/**
 * 小学生出题系统(30道四则运算题)
 * @author www.cnsimo.cn
 * @since 2017-03-07 21:53:14
 */
public class CT {

	public static String str = "";//保存题目的字符串
    public static int num = 5;//每题中数的个数
    public static int num_i = 0;//题目中已有数的个数    
    public static int numberRange = 100;//运算中数的最大取值    
    public static double sum = 0;//记录结果
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println("注意:结果保留1位小数!");
		System.out.println("共30道题目:");
		Scanner in = new Scanner(System.in);
		double answer = 0;
		double result = 0;
		String[] question = new String[30];
		int questionNumber = 0;
		int answerTrue = 0;
		boolean flag;
		for(;;) {
			answer = 0; result = 0; flag = true; str="";
			if((questionNumber + 1)%5 != 0) {
				GetQuestion_int();
			} else {
				GetQuestion_div();
			}
			for(int j = questionNumber-1; j >= 0; j --) {
				if(question[j].equals(str)) {
					flag = false; break;
				}
			}
			if(!flag) continue;
			else {question[questionNumber] = new String(str); questionNumber++;}
			System.out.print("" + questionNumber + ". " + str+" = ");
			answer = in.nextDouble();
			if(!str.isEmpty()) {
				result = Arithmetic.arithmetic(str);
			}
			if(answer == result) {
				System.out.println("   ✔️");
				answerTrue++;
			} else {
				System.out.println("   ❌ " + "  正确答案:" + result);
			}
			
			if(questionNumber == 30) break;         // 满30个跳出
		}
		
		System.out.println("你的正确概率:" + answerTrue + "/30");
		in.close();
	}
	
    private static void GetQuestion_int() {
        //得到问题函数,在这里调用递归函数quesGrow()。
        str = "";
        sum = 0;
        num_i = num;//用前都清零
        quesGrow_int();
    }
    
    private static void GetQuestion_div() {
    	str = "";
        sum = 0;
        num_i = num;//用前都清零
        quesGrow_div();
    }

    private static void quesGrow_int() {
        //
        if( num_i > 1 ) {
            int j = num_i;//记录这是第几层调用。
            num_i--;
            quesGrow_int();//递归
            
            int w=1+(int)(Math.random()*numberRange);//随机生成一个数
            int t=1+(int)(Math.random()*100);//向左生成,还是向右生成,类似于树。
            int f=1+(int)(Math.random()*100);//运算符控制
            
            if(t>50)//新数往右加
            {
                if(f>50) {
                    sum = sum + w;
                    str = str + "+" + String.valueOf( w );
                }
                else {
                    sum = sum - w;
                    str = str + "-" + String.valueOf( w );        
                }
            }
            else//否则 新数往左加
            {
                if(f>50) {
                    sum = w + sum;
                    str = String.valueOf( w ) + "+" + str;    
                }
                else {
                    if( j < 3 ) {//3——摸索出的数,不用给自己套上括号。实际上就是j=2
                        sum = w - sum;
                        str = String.valueOf( w ) + "-" + str;
                    }
                    else {
                        sum = w - sum;
                        str = String.valueOf( w ) + "-" + "(" +str+ ")";
                        //向左添减法的时候加括号,打破顺序计算模式。
                    }
                }
            }
        }
        else if( num_i == 1 ) {
            //最后一层,也是输出的第一层
            int w=1+(int)(Math.random()*numberRange);
            sum = sum + w;
            str = str + String.valueOf( w );
        }
    }
    
    private static void quesGrow_div() {
    	if( num_i > 1 ) {
            int j = num_i;//记录这是第几层调用。
            num_i--;
            quesGrow_div();//递归
            
            double w=Math.random();//随机生成一个数
            int t=1+(int)(Math.random()*100);//向左生成,还是向右生成,类似于树。
            int f=1+(int)(Math.random()*100);//运算符控制
            
            if(t>50)//新数往右加
            {
                if(f>50) {
                    sum = sum + w;
                    str = str + "+" + ChangeToFenshuDemo.toFenshu((""+(w+0.01)).substring(0, 4));
                }
                else {
                    sum = sum - w;
                    str = str + "-" + ChangeToFenshuDemo.toFenshu((""+(w+0.01)).substring(0, 4));        
                }
            }
            else//否则 新数往左加
            {
                if(f>50) {
                    sum = w + sum;
                    str = ChangeToFenshuDemo.toFenshu((""+(w+0.01)).substring(0, 4)) + "+" + str;    
                }
                else {
                    if( j < 3 ) {//3——摸索出的数,不用给自己套上括号。实际上就是j=2
                        sum = w - sum;
                        str = ChangeToFenshuDemo.toFenshu((""+(w+0.01)).substring(0, 4)) + "-" + str;
                    }
                    else {
                        sum = w - sum;
                        str = ChangeToFenshuDemo.toFenshu((""+(w+0.01)).substring(0, 4)) + "-" + "(" +str+ ")";
                        //向左添减法的时候加括号,打破顺序计算模式。
                    }
                }
            }
        }
        else if( num_i == 1 ) {
            //最后一层,也是输出的第一层
            double w=Math.random();
            sum = sum + w;
            str = str + ChangeToFenshuDemo.toFenshu((""+(w+0.01)).substring(0, 4));
        }
	}
}

运行结果: 

总结: 

平时要善于积累常用的算法,以及自己写的觉得有用的代码;

写代码时,千万不要所有的东西都叠在一个方法里面,根据功能写相应的方法,以后用的时候可以直接调用;

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏ACM小冰成长之路

KWIC-C/C++实现

吐槽 最近我们 JavaJava 老师不知道为啥非要我用 C/C++C/C++ 来实现 KWICKWIC,但是因为没有上过课,不知道这个东西是干嘛的,所以想网上...

18910
来自专栏小詹同学

【记录帖】(No.003)从零打卡刷Leetcode

小詹一直觉得自己编程能力不强,想在网上刷题,又怕不能坚持。不知道有木有和小伙伴和小詹一样想找个人一起刷题呢?欢迎和小詹一起定期刷leetcode,每周一周五更新...

1342
来自专栏Crossin的编程教室

【每周一坑】神奇的九宫格

五一小长假大家应该玩的挺开心吧,还沉浸在假日的愉悦中么?请大家收收心,准备准备月底的端午节。 看看本周的题目吧,本周的题目由读者 @疯琴 提供,我们做了小小的改...

2657
来自专栏小詹同学

【记录帖】(No.001)从零打卡刷Leetcode

小詹一直觉得自己编程能力不强,想在网上刷题,又怕不能坚持。不知道有木有和小伙伴和小詹一样想找个人一起刷题呢?欢迎和小詹一起定期刷leetcode,每周一周五更新...

923
来自专栏程序员互动联盟

【答疑释惑】最小存储单元是什么?

学习编程,要从基础学起,bit和byte有很大区别,最主要别搞混。 ? bit是位,byte是字节。 有问题就问题,不要害羞,不要怕被笑话,最重要的搞清楚自己要...

3369
来自专栏编程

编写高质量代码的思考

前言 最近在看《代码大全》,可以说是一本软件开发的百科全书,特别厚,但是干货也很多。平时写代码,代码规范是一个最低的要求(很多老代码连最低要求都达不到),为什么...

2086
来自专栏Python攻城狮

面向对象编程-OOP1.面向对象编程介绍2.定义类3."魔法"方法4.self

面向对象(object-oriented ;简称: OO)至今还没有统一的概念 我这里把它定义为:按人们 认识客观世界的系统思维方式,采用基于对象(实体)的概念...

934
来自专栏take time, save time

初级程序员面试不靠谱指南(五)

四、递归的第一次亲密接触     我经常会想,如果给没有学过计算机或者数学的人说递归这个词他们脑中会怎样理解这个词的意思。递归这个概念在面试中出现的概率大于85...

3288
来自专栏数据结构与算法

Vijos / 题库 / 输油管道问题

背景 想念car的GF,car就出了道水题! 描述 某石油公司计划建造一条由东向西的主输油管道。该管道要穿过一个有n 口油井的油田。从每口油井都要有一条输油管道...

33011
来自专栏编程

Python利器之迭代器

各位小伙伴们 大家周四愉快 今天要和大家探讨一个 Python的特色功能 也是Python有别于其他变成语言的 强大利器 迭代器 迭代这一个词可能有的小伙伴不理...

1797

扫码关注云+社区