第一次大作业总结

前言

到目前为止已经完成了三次PTA作业,因为以前学习过JS等面向对象语言,所以对JAVA的学习还算轻松。第一次作业主要是学习一些基本的JAVA输入输出格式,回顾过去的一些编程算法和函数,而第二次第三次便是正式对JAVA的面向过程进行了深入理解与探究,通过对代码功能以及联系分类,使各个部分井然有序的。题量我认为较大,可能是由于太久没有去写大量算法题导致比较懒,但是难度尚可。

设计与分析

第一次PTA练习都是一些基础回顾,为后续练习打下基础,但也没有太多深究必要

重点探究第二次和第三次的部分

7-2 串口字符解析

RS232是串口常用的通信协议,在异步通信模式下,串口可以一次发送5~8位数据,收发双方之间没有数据发送时线路维持高电平,相当于接收方持续收到数据“1”(称为空闲位),发送方有数据发送时,会在有效数据(5~8位,具体位数由通信双方提前设置)前加上1位起始位“0”,在有效数据之后加上1位可选的奇偶校验位和1位结束位“1”。请编写程序,模拟串口接收处理程序,注:假定有效数据是8位,奇偶校验位采用奇校验。

输入格式:

由0、1组成的二进制数据流。例如:11110111010111111001001101111111011111111101111

输出格式:

过滤掉空闲、起始、结束以及奇偶校验位之后的数据,数据之前加上序号和英文冒号。
如有多个数据,每个数据单独一行显示。
若数据不足11位或者输入数据全1没有起始位,则输出"null data",
若某个数据的结束符不为1,则输出“validate error”。
若某个数据奇偶校验错误,则输出“parity check error”。
若数据结束符和奇偶校验均不合格,输出“validate error”。

题目分析:题目也是一道较为基础的算法使用题,回顾了字符串截取和拼合,整体难度不大,重点是判断输出的准确性

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String arr = scanner.nextLine();
        int num = 0;
        if(arr.length()<11) {
            System.out.print("null data");
            return;
        }
        for (int i = 0; i < arr.length(); i++) {
            if (arr.charAt(i) == '0') {
                int flag = 0;
                if(i+10>arr.length()){
                    return;
                }
                System.out.print(++num+":");
                for (int j = i + 1; j < i + 9; j++) {
                    if (arr.charAt(j) == '0') flag++;
                }
                if (arr.charAt(i + 10) != '1') {
                    System.out.println("validate error");
                    i = i + 10;
                    continue;
                }
                if ((flag % 2 == 0 && arr.charAt(i + 9) != '1') || (flag % 2 == 1 && arr.charAt(i + 9) != '0')) {
                    System.out.println("parity check error");
                    i = i + 10;
                    continue;
                }
                for (int j = i + 1; j < i + 9; j++) {
                    System.out.print(arr.charAt(j));
                }
                System.out.println();
                i = i + 10;
            }
        }
        if (num == 0) System.out.println("null data");
    }
}

 

  SourceMonitor的生成报表

 

上述代码主要还是面向过程,并没有使用复杂的类分析,所以重点对下面进行分析

 

7-1 点线形系列1-计算两点之间的距离

 输入连个点的坐标,计算两点之间的距离

输入格式:

4个double类型的实数,两个点的x,y坐标,依次是x1、y1、x2、y2,两个点的坐标之间以空格分隔,每个点的x,y坐标以英文“,”分隔。例如:0,0 1,1或0.1,-0.3 +3.5,15.6。
若输入格式非法,输出"Wrong Format"。
若输入格式合法但坐标点的数量超过两个,输出“wrong number of points”。

输出格式:

计算所得的两点之间的距离

题目分析:验证输入合法性,但题目整体简单,也是为后面题目做还准备,也初步去实现分类思想

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String arr = scanner.nextLine();
        Judgment judgment = new Judgment(arr);
        judgment.isBeyondNum(2);
        Point[] pointArr = judgment.pointArr;
        System.out.println(pointArr[0].pointDistance(pointArr[1]));
    }
}

  创建两个用法类,也为了后续题目使用

判定类

class Judgment {
    public String arr = "";
    public int value = 0;
    public Point[] pointArr = new Point[6];

    Judgment(String str) {
        String regex = "(\\d:)?([+|-]?(0|[1-9]\\d*)(\\.\\d+)?,[+|-]?(0|[1-9]\\d*)(\\.\\d+)? )+[+|-]?(0|[1-9]\\d*)(\\.\\d+)?,[+|-]?(0|[1-9]\\d*)(\\.\\d+)?";
        if (!str.matches(regex)) {
            System.out.println("Wrong Format");
            System.exit(0);
        } else {
            if (str.matches("(\\d:).*")) {
                this.value = Integer.parseInt(str.split(":")[0]);
                this.arr = str.split(":")[1];
            } else this.arr = str;
            int i = 0;
            for (String point : arr.split(" ")) {
                this.pointArr[i] = new Point();
                this.pointArr[i].x = Double.parseDouble(point.split(",")[0]);
                this.pointArr[i++].y = Double.parseDouble(point.split(",")[1]);
            }
        }
    }

    public void isBeyondNum(int n) {
        if (arr.split(" ").length != n) {
            System.out.println("wrong number of points");
            System.exit(0);
        }
    }
}

 

Point类

class Point {
    public double x = 0;
    public double y = 0;

    public double pointDistance(Point n1) {
        return Math.sqrt(Math.pow(this.x - n1.x, 2) + Math.pow(this.y - n1.y, 2));
    }
}

7-2 点线形系列2-线的计算

用户输入一组选项和数据,进行与直线有关的计算。选项包括:
1:输入两点坐标,计算斜率,若线条垂直于X轴,输出"Slope does not exist"。
2:输入三个点坐标,输出第一个点与另外两点连线的垂直距离。
3:输入三个点坐标,判断三个点是否在一条线上,输出true或者false。
4:输入四个点坐标,判断前两个点所构成的直线与后两点构成的直线是否平行,输出true或者false.
5:输入四个点坐标,计算输出前两个点所构成的直线与后两点构成的直线的交点坐标,x、y坐标之间以英文分隔",",并输出交叉点是否在两条线段之内(不含四个端点)的判断结果(true/false),判断结果与坐标之间以一个英文空格分隔。若两条线平行,没有交叉点,则输出"is parallel lines,have no intersection point"。

输入格式:

基本格式:选项+":"+坐标x+","+坐标y+" "+坐标x+","+坐标y。
例如:1:0,0 1,1
如果不符合基本格式,输出"Wrong Format"。
如果符合基本格式,但输入点的数量不符合要求,输出"wrong number of points"。
不论哪个选项,如果格式、点数量都符合要求,但构成任一条线的两个点坐标重合,输出"points coincide",

输出格式:

见题目描述。

题目分析:这道题目是在上一道题目的基础加强难度,从设计角度上,我们可以继续使用上一道题的判断类和Point类,并创建Line类,Line类应该由两个Point类对象构成,可以通过传入两个Point对象对Line类继续实例化,并在构造器中计算出一些直线相关信息,如直线函数ax+by+c的各个系数以及线段长度,方便后续的方法实现。在构造器也要对直线是否成立进行判断,如果传入两个点共点,便要返回错误信息,并结束应用。当上述实现后,这道题目便变得清晰起来,计算斜率、点线距离、公线判断等,只要通过传入信息和直线相关函数构建即可。

Line类

 

class Line {
    public Point[] pointArr = new Point[2];
    public double length = 0;
    public double a, b, c;

    public Line(Point n1, Point n2) {
        if (n1.x == n2.x && n1.y == n2.y) {
            System.out.println("points coincide");
            System.exit(0);
        }
        pointArr[0] = n1;
        pointArr[1] = n2;
        this.length = n1.pointDistance(n2);
        this.a = n1.y - n2.y;
        this.b = n2.x - n1.x;
        this.c = n1.x * n2.y - n2.x * n1.y;
    }

    public double slope() {
        if (this.pointArr[0].x == this.pointArr[1].x) return 999;
        else return -a / b;
    }

    public double VerticalDistance(Point n1) {
        return Math.abs(a * n1.x + b * n1.y + c )/ Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2));
    }

    public boolean isCollinear(Point n1) {
        return a * n1.x + b * n1.y + c == 0;
    }

    public boolean isParallel(Line l1) {
        return this.slope() == l1.slope();
    }

    public Point Intersection(Line l1) {
        Point n = new Point();
        double det = l1.a * this.b - this.a * l1.b;
        n.x = (this.c * l1.b - l1.c * this.b) / det;
        n.y = (this.a * l1.c - l1.a * this.c) / det;
        return n;
    }

    public boolean isInside(Point n1) {
        if (Math.abs(n1.pointDistance(pointArr[0]) + n1.pointDistance(pointArr[1]) - this.length) < 0.01 && n1.pointDistance(pointArr[0])!=0 && n1.pointDistance(pointArr[1])!=0) return true;
        else return false;
    }
}

 

 Main

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String arr = scanner.nextLine();
        Judgment judgment = new Judgment(arr);
        Point[] pointArr = judgment.pointArr;
        Line[] line = new Line[2];
        switch (judgment.value) {
            case 1:
                judgment.isBeyondNum(2);
                line[0] = new Line(pointArr[0], pointArr[1]);
                if (line[0].slope() == 999) System.out.println("Slope does not exist");
                else System.out.println(line[0].slope());
                break;
            case 2:
                judgment.isBeyondNum(3);
                line[0] = new Line(pointArr[1], pointArr[2]);
                System.out.println(line[0].VerticalDistance(pointArr[0]));
                break;
            case 3:
                judgment.isBeyondNum(3);
                line[0] = new Line(pointArr[1], pointArr[2]);
                System.out.println(line[0].isCollinear(pointArr[0]));
                break;
            case 4:
                judgment.isBeyondNum(4);
                line[0] = new Line(pointArr[0], pointArr[1]);
                line[1] = new Line(pointArr[2], pointArr[3]);
                System.out.println(line[0].isParallel(line[1]));
                break;
            case 5:
                judgment.isBeyondNum(4);
                line[0] = new Line(pointArr[0], pointArr[1]);
                line[1] = new Line(pointArr[2], pointArr[3]);
                if (line[0].isParallel(line[1]))
                    System.out.println("is parallel lines,have no intersection point");
                else {
                    Point n = line[0].Intersection(line[1]);
                    System.out.println(n.x + "," + n.y + " " + (line[0].isInside(n) || line[1].isInside(n)));
                }break;
            default:
                System.out.println("Wrong Format");
        }
    }
}

 

7-3 点线形系列3-三角形的计算

用户输入一组选项和数据,进行与三角形有关的计算。选项包括:
1:输入三个点坐标,判断是否是等腰三角形、等边三角形,判断结果输出true/false,两个结果之间以一个英文空格符分隔。
2:输入三个点坐标,输出周长、面积、重心坐标,三个参数之间以一个英文空格分隔,坐标之间以英文","分隔。
3:输入三个点坐标,输出是钝角、直角还是锐角三角形,依次输出三个判断结果(true/false),以一个英文空格分隔,
4:输入五个点坐标,输出前两个点所在的直线与三个点所构成的三角形相交的交点数量,如果交点有两个,则按面积大小依次输出三角形被直线分割成两部分的面积。若直线与三角形一条线重合,输出"The point is on the edge of the triangle"
5:输入四个点坐标,输出第一个是否在后三个点所构成的三角形的内部(输出in the triangle/outof triangle)。
必须使用射线法,原理:由第一个点往任一方向做一射线,射线与三角形的边的交点(不含点本身)数量如果为1,则在三角形内部。如果交点有两个或0个,则在三角形之外。若点在三角形的某条边上,输出"on the triangle"

输入格式:

基本格式:选项+":"+坐标x+","+坐标y+" "+坐标x+","+坐标y。点的x、y坐标之间以英文","分隔,点与点之间以一个英文空格分隔。

输出格式:

基本输出格式见每种选项的描述。
异常情况输出:
如果不符合基本格式,输出"Wrong Format"。
如果符合基本格式,但输入点的数量不符合要求,输出"wrong number of points"。
如果输入的三个点无法构成三角形,输出"data error"。
注意:输出的数据若小数点后超过6位,只保留小数点后6位,多余部分采用四舍五入规则进到最低位。小数点后若不足6位,按原始位数显示,不必补齐。例如:1/3的结果按格式输出为 0.333333,1.0按格式输出为1.0

选项4中所输入线的两个点坐标重合,输出"points coincide",

GeometricObject类

abstract class GeometricObject {
    private java.util.Date dateCreated;

    protected GeometricObject() {
        dateCreated = new java.util.Date();
    }

    public abstract double getArea();
    public abstract double getPerimeter();
}

 

Triangle类

 

class Triangle extends GeometricObject{
    private double len[] = new double[3];
    private Point[] pointArr = new Point[3];
    private Line[] lineArr = new Line[3];

    public Triangle(Point n1, Point n2, Point n3) {
        pointArr[0] = n1;
        pointArr[1] = n2;
        pointArr[2] = n3;
        len[0] = n1.pointDistance(n2);
        len[1] = n2.pointDistance(n3);
        len[2] = n3.pointDistance(n1);
        if (len[0] >= len[1] + len[2] || len[1] >= len[0] + len[2] || len[2] >= len[1] + len[0]) {
            System.out.println("data error");
            System.exit(0);
        }
        lineArr[0] = new Line(n1, n2);
        lineArr[1] = new Line(n2, n3);
        lineArr[2] = new Line(n3, n1);
    }

    public boolean Isosceles() {
        return len[0] == len[1] || len[0] == len[2] || len[1] == len[2];
    }

    public boolean Equilateral() {
        return len[0] == len[1] && len[1] == len[2];
    }

    public double getPerimeter() {
        return len[0] + len[1] + len[2];
    }

    public double getArea() {
        double Perimeter = getPerimeter();
        return Math.sqrt(Perimeter / 2 * (Perimeter / 2 - len[0]) * (Perimeter / 2 - len[1]) * (Perimeter / 2 - len[2]));
    }

    public Point focus() {
        double x = (pointArr[0].x + pointArr[1].x + pointArr[2].x) / 3;
        double y = (pointArr[0].y + pointArr[1].y + pointArr[2].y) / 3;
        return new Point(x, y);
    }

    public int triangularType() {
        for (int i = 1; i < 3; i++) {
            if (len[i] > len[0]) {
                double temp = len[0];
                len[0] = len[i];
                len[i] = temp;
            }
        }
        double num = Math.pow(len[1], 2) + Math.pow(len[2], 2) - Math.pow(len[0], 2);
        if (num > 0.000001) return 1;
        else if (num < -0.000001) return -1;
        else return 0;
    }

    public int intersect(Line l) {
        double[] temp = new double[3];
        for (int i = 0; i < 3; i++) {
            temp[i] = l.VerticalDistance(pointArr[(i + 1) % 3]) * l.VerticalDistance(pointArr[(i + 2) % 3]);
            if (l.isCollinear(pointArr[i])) {
                if (temp[i] == 0) return -1;
                else if (temp[i] < 0) return 2;
                else return 1;
            }
        }
        if (temp[0] * temp[1] > 0 && temp[0] * temp[2] > 0) return 0;
        else return 2;
    }

    public double[] partialArea(Line l) {
        double[] partArea = new double[2];
        Point[] p = new Point[2];
        int n = 0;
        double Height = 0;
        for (int i = 0; i < 3; i++) {
            if (l.VerticalDistance(pointArr[i]) * l.VerticalDistance(pointArr[(i + 1) % 3]) < 0) {
                p[n++] = l.Intersection(lineArr[i]);
                if (l.isCollinear(pointArr[(i + 2) % 3])) {
                    p[1] = pointArr[(i + 2) % 3];
                    Height = Math.abs(l.VerticalDistance(pointArr[i]));
                    break;
                }
                continue;
            }
            Height = Math.abs(l.VerticalDistance(pointArr[(i + 2) % 3]));
        }
        double botLength = p[0].pointDistance(p[1]);
        partArea[0] = Math.min((Height * botLength) / 2, this.getArea() - (Height * botLength) / 2);
        partArea[1] = Math.max((Height * botLength) / 2, this.getArea() - (Height * botLength) / 2);
        return partArea;
    }

    public boolean isOnTriangle(Point n1) {
        for (int i = 0; i < 3; i++) {
            if (pointArr[i].pointDistance(n1) == 0) return true;
            if (lineArr[i].isInside(n1)) return true;
        }
        return false;
    }

    public boolean isOut(Point n1) {
        Line line = new Line(n1, pointArr[0]);
        if (this.intersect(line) == 2) {
            Point n = line.Intersection(lineArr[1]);
            return (new Line(pointArr[0], n)).isInside(n1);
        } else return false;
    }
}

 

 

 

 像之前题目一样,继续使用之前的创造的Line类和Point类,由于写这道题时,我也正在对后面PTA5的题目,所以使用了继承抽象父类来统筹各种多边形,可能目前显示不出作业,甚至有点累赘,但后续有很大的便捷作用。其次对输出有格式要求,所以我向判断修改添加了一个输出的方法。

import java.math.RoundingMode;
import java.text.DecimalFormat;

class Judgment {
    private String arr = "";
    public int value = 0;
    public Point[] pointArr = new Point[10];

    public Judgment(String str) {
        String regex = "(\\d:)?([+|-]?(0|[1-9]\\d*)(\\.\\d+)?,[+|-]?(0|[1-9]\\d*)(\\.\\d+)? )+[+|-]?(0|[1-9]\\d*)(\\.\\d+)?,[+|-]?(0|[1-9]\\d*)(\\.\\d+)?";
        if (!str.matches(regex)) {
            System.out.println("Wrong Format");
            System.exit(0);
        } else {
            if (str.matches("(\\d:).*")) {
                this.value = Integer.parseInt(str.split(":")[0]);
                this.arr = str.split(":")[1];
            } else this.arr = str;
            int i = 0;
            for (String point : arr.split(" ")) {
                this.pointArr[i++] = new Point(Double.parseDouble(point.split(",")[0]),Double.parseDouble(point.split(",")[1]));
            }
        }
    }

    public void isBeyondNum(int n) {
        if (arr.split(" ").length != n) {
            System.out.println("wrong number of points");
            System.exit(0);
        }
    }

    public String PrintJudge(double x) {
        DecimalFormat nf = new DecimalFormat("0.0#####");
        nf.setRoundingMode(RoundingMode.HALF_UP);
        return nf.format(x);
    }
}

 

  

对7-1到7-3图像类分析

PowerDesigner的类图

 SourceMonitor的生成报表

 

 上述可以看出三道题是层层递进的关系,由易到难,也逐渐体现了面向对象分类思想的重要性和好处。

采坑心得

在第一次进行输入判定的时候,由于正则表达式的使用并不够清楚,导致出现了很多次判定缺失,但也正应如此让我得以更加细致的去了解正则表达式,因为是刚开始的学习的阶段,能不断试错找到自己的不熟练的地方是好的事情。还有在进行线分类时,第一次都没有良好的代码规划,导致线类在第三题的复用价值很低,失去了代码的灵活性,导致不得不重新去构建类方法。

改进建议

可以看SourceMonitor的生成报表得出整体的最大复杂度严重偏高,这是可以注意的问题,在设计的设计的时候应该主动去思考更加精简的代码,灵活充分运用算法,避免使用嵌套的for循环。在类的设计依然纯在缺陷,给后面难度继续的算法设计题造成麻烦,可以对类的改造更加精确。

总结

这是第一次去尝试独立完成JAVA面向过程的算法设计,虽然其中很多地方完成的不尽人意,但终究是完成了,通过本次的练习不仅学习了最基本的JAVA输入输出的格式,也去了解了面向对象的设计逻辑并以此实操了,除此之外还学习了很多封装的数学函数,可以说这是一次非常好的起步。尽管总是抱怨题目多题目难,但也正因如此独立完成它时才会收获更大成就感,也激发继续深入了解JAVA的兴趣。

posted @ 2022-10-01 12:28  帅气仔厘子  阅读(81)  评论(0)    收藏  举报