写在前面

项目 内容
所属课程 2020春季计算机学院软件工程(罗杰 任健) (北航)
作业要求 [个人项目作业](<https://edu.cnblogs.com/campus/buaa/BUAA_SE_2020_LJ/homework/10429)
课程目标 培养软件开发能力
本作业对实现目标的具体作用 锻炼个人开发项目的能力
教学班级 006
github项目地址 https://github.com/LiuZH-19/SE_IntersectProject

PSP表格记录

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划
· Estimate · 估计这个任务需要多少时间 5 10
Development 开发
· Analysis · 需求分析 (包括学习新技术) 180 220
· Design Spec · 生成设计文档 10 20
· Design Review · 设计复审 (和同事审核设计文档) 15 20
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 5 5
· Design · 具体设计 30 60
· Coding · 具体编码 120 160
· Code Review · 代码复审 20 20
· Test · 测试(自我测试,修改代码,提交修改) 120 120
Reporting 报告
· Test Report · 测试报告 30 30
· Size Measurement · 计算工作量 5 5
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 10 30
合计 550 700

解题思路描述

大体思路:

看了题目之后,想到的方法是:

  • 利用数学的方法,解出两直线L1,L2存在交点的条件和其存在的交点的公式。

  • 然后对输入中的任意两条直线,进行判断。若存在交点,则取出放入容器中(避免容器中元素的重复性)

  • 最后输出容器中交点的个数

    但考虑到需对输入中的直线两两求解,复杂度为\(\O(n^2)\)。我打算上网查一查,看看有没有更巧妙的算法。

    查询无果,与小伙伴们讨论了一下,也没有更好的算法。就打算按这个算法,仔细想想细节部分。

    为了避免直线的一些特殊情况的分析,我即将直线表示为一般式:Ax+By+C=0

    附加题,也是类似的思路:

    直线两两求解交点,再求解直线之前的交点情况,再求解两圆之间的交点情况。

    在求解交点前,先判断是满足具有交点的条件,避免不必要的计算

细节考量:

直线与直线的交点比较好算,所以自己手算出来了算式关系。

直线与圆的公式也是手算的。

圆与圆之前的交点的公式,解得我头大,也不确定自己算出来的对不对,所以参考了网上的算法。详见

求任意两圆的交点

设计实现过程

代码组织:

期初设想:

  • 类的设计:

    • Line 类

      • 直线用 Ax+By+C=0 表示 ,其中A,B,C为x1,y1,x2,y2的 函数
      • 属性为 A,B,C
      • 成员函数为 getA(), getB(), getC()
    • Circle类
      • 圆用\(\(x-X)^{2}+(y-Y)^{2}=R^2\)表示
      • 属性为 X, Y, R
      • 成员函数为 getX(), getY(),getR()
  • 用 vector 存放 所有的直线

  • 交点为Pair类型,用set存放所有的交点

  • 三个函数,分别计算两直线之间,直线与圆之间,两圆之间的交点情况。

    按照上述思路实现后,我发现Line 和 Circle类有点多余。教材中也说,只是封装数据的话,不用class用struct。所以我打算将直线和圆的参数用结构体来存。将原先计算交点情况的三个函数封装成一个Calculator类。又考虑到计算中的精度损失问题,构造了point 类,重写了operator <。具体情况如下:

    后来改进:

  • Calculator 类

    重载了三个成员函数

    class Calculator
    {
    public:
    Calculator();
    int haveIntersection(Line l1, Line l2, set<Point>& nodeSet);
    int haveIntersection(Circle c, Line l, set<Point>& nodeSet);
    int haveIntersection(Circle c1, Circle c2, set<Point>& nodeSet);
    };
  • Point类

    自定义了运算符

    bool Point::operator < (const Point& p)const {
    //return x==p.x?y<p.y:x<p.x;
    return dcmp(x - p.x) == 0 ? dcmp(y - p.y) < 0 : dcmp(x - p.x) < 0; } bool Point::operator ==(const Point& p)const {
    if (dcmp(x-p.x)==0&&dcmp(y-p.y)==0)
    return true;
    return false;
    }
  • Line 和Circle的架构体

  • main中的函数 countALLinsect()

    分别计算 直线之间,直线与圆之间,圆与圆之间的交点情况

单元测试设计

  • 测试Calculator类,具体包括以下几个方面:

    • 直线直线的交点情况

      • 平行
      • 三线交于一点
      • 直线平行于x轴
      • 直线 平行于y轴
      • 一般情况
    • 直线与圆的交点情况
      • 直线的特殊情况
      • 线圆关系
        • 相切
        • 相离
        • 相交
    • 圆与圆的交点关系
      • 相离
      • 相交
      • 外切
      • 内切
      • 内含(同心)
    • 复杂情况(两圆与一直线)
      • 有一交点重叠
      • 有两个交点重叠
      • 一般情况
  • 测试point类

    检测 重写的operator < 是否正确
  • 整体测试
    • 大量数据测试 ,检测时间是否符合题意
    • 检测 set 是否去重

测试均通过,且交点为8000000左右时,用时7s,也没有超时。

性能改进相关

图中可以看出,采用set容器后,构建红黑树占用了绝大部分CPU时间。在建树过程中,用到了我在Point类里面 重写的operator <,故其占用时间也较多。除去set的相关操作外,接下来去看了下 Calculator中的函数。

其中 CPU绝大部分的占用时间依然是 set的insert操作。所以在没能想到更优的算法下,性能改进工作可做的很少。我将一些常用的计算式先算出来,避免之后的重复计算。例如下图:

消除 Code Quality Analysis 中的所有警告

采用的是“Microsoft建议”的风格。

关键代码说明

  • 求解所有的交点情况:直线之前、直线与圆、两圆之间

    int countAllinsect(vector<Line> lVec, vector<Circle> cVec, set<Point> &nodeSet){
    Calculator* calc = new Calculator();
    size_t i, j;
    //计算两条直线间的交点
    for (i = 0; i < lVec.size(); i++) {
    for (j = i + 1; j < lVec.size(); j++) {
    calc->haveIntersection(lVec[i], lVec[j], nodeSet);
    }
    }
    //计算直线与圆之间的交点
    for (i = 0; i < cVec.size(); i++) {
    for (j = 0; j < lVec.size(); j++) {
    calc->haveIntersection(cVec[i], lVec[j], nodeSet);
    }
    }
    //计算两圆之间的交点
    for (i = 0; i < cVec.size(); i++) {
    for (j = i + 1; j < cVec.size(); j++) {
    calc->haveIntersection(cVec[i], cVec[j], nodeSet);
    }
    }
    return nodeSet.size();
    }

    方法很暴力,就是循环遍历。

困惑

本次项目涉及到浮点数运算的精度问题。由于公式中存在开方以及除法运算,导致最终算出来的点只是近似值。因此我重写了 比较函数 ,EPS最终取的是0.0000001。虽然这个EPS是经过我多番测试选出来的值,但我任然无法保证他的实用性。所以在判断点是否重合的时候,可能会存在误差。

下面是关于精确度问题的相关代码:

#define EPS  0.0000001

int dcmp(double x) {
if (fabs(x) < EPS) return 0;
return x < 0 ? -1 : 1;
} bool Point::operator < (const Point& p)const {
//return x==p.x?y<p.y:x<p.x;
return dcmp(x - p.x) == 0 ? dcmp(y - p.y) < 0 : dcmp(x - p.x) < 0; }

BUAA软件工程个人项目的更多相关文章

  1. BUAA软件工程结对项目作业

    BUAA软件工程结对项目 小组成员:16005001,17373192 1.教学班级和项目地址 项目 内容 这个作业属于哪个课程 博客园班级连接 这个作业的要求在哪里 结对项目作业 我在这个课程的目标 ...

  2. BUAA软件工程个人项目作业

    BUAA软件工程个人项目作业 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 个人项目作业 我在这个课程的目标是 学习软件开发的流程 这个作业在哪 ...

  3. BUAA 软件工程个人作业

    BUAA 软件工程 个人项目作业 Author: 17373015 乔玺华 教学班级 :005 项目地址:https://github.com/JordenQiao/SE_Homework_Perso ...

  4. BUAA 2020 软件工程 个人项目作业

    BUAA 2020 软件工程 个人项目作业 Author: 17373051 郭骏 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 个人项目作业 ...

  5. 【BUAA软件工程】第一次阅读作业

    BUAA软件工程 第一次阅读作业 项目 内容 这个作业属于哪个课程? 北航软工 这个作业的要求在哪里? 第一次个人作业 我在这个课程的目标是? 学习高效严谨的软件工程开发过程,建立团队意识 这个作业在 ...

  6. BUAA软件工程:软件案例分析

    BUAA软件工程:软件案例分析 Author:17373015 乔玺华 项目 内容 这个作业属于哪个课程 2020计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 软件案例分析博客作业 我在这个 ...

  7. 2021S软件工程——结对项目第一阶段

    # 2021S软件工程--结对项目第一阶段 2021春季软件工程(罗杰 任健) 项目地址 1020 1169 --- ## 1 结对感受 总体来说,结对编程与之前的个人编程感觉有很大的不同.有如下几个 ...

  8. 2021S软件工程——结对项目第三阶段

    2021S软件工程--结对项目第三阶段 2021春季软件工程(罗杰 任健) 项目地址 1020 1169 1 实践反思 1.1 问题分析 两人习惯不一致 没有具体制定时间节点 写完代码才开始" ...

  9. BUAA 2020 软件工程 结对项目作业

    Author: 17373051 郭骏 3.28添加:4.计算模块接口的设计与实现过程部分,PairCore实现的细节 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) ...

随机推荐

  1. java例题_12 奖金问题(暴力破解)

    1 /*12 [程序 12 计算奖金] 2 题目:企业发放的奖金根据利润提成. 3 利润I低于或等于 10 万元时,奖金可提 10%: 4 利润高于 10 万元,低于 20 万元时,低于 10 万元的 ...

  2. 图文详解Java对象内存布局

    作为一名Java程序员,我们在日常工作中使用这款面向对象的编程语言时,做的最频繁的操作大概就是去创建一个个的对象了.对象的创建方式虽然有很多,可以通过new.反射.clone.反序列化等不同方式来创建 ...

  3. 20182217_刘洪宇 后门原理与实践 EXP2

    1.后门概念 后门就是不经过正常认证流程而访问系统的通道. 哪里有后门呢? 编译器留后门 操作系统留后门 最常见的当然还是应用程序中留后门 还有就是潜伏于操作系统中或伪装为特定应用的专用后门程序. - ...

  4. 《环形队列》游戏高《TPS》模式下减少cpu线程切换

    序言 什么高TPS?QPS,其实很多人都知道,还有人说大数据,大流量这些关键词夜以继日的出现在我们眼前: 针对高TPS,QPS这些词汇还有一个次可能比较陌生那就是CCU,tps,qps接受满天飞,CC ...

  5. Java 时间日期系列

    Java Calendar,Date,DateFormat,TimeZone,Locale等时间相关内容的认知和使用(1) Calendar Java Calendar,Date,DateFormat ...

  6. Hadoop完整搭建过程(二):伪分布模式

    1 伪分布模式 伪分布模式是运行在单个节点以及多个Java进程上的模式.相比起本地模式,需要进行更多配置文件的设置以及ssh.YARN相关设置. 2 Hadoop配置文件 修改Hadoop安装目录下的 ...

  7. Magicodes.IE.ASPNETCore之多样化接口使用

    1.安装包 Install-Package Magicodes.IE.AspNetCore 2.开始配置 在Startup.cs的Configure()方法中,在UseRouting()中间件之后,注 ...

  8. C语言-字符串函数的实现(二)之strcpy

    C语言中的字符串函数有如下这些 获取字符串长度 strlen 长度不受限制的字符串函数 strcpy strcat strcmp 长度受限制的字符串函数 strncpy strncat strncmp ...

  9. Mybatis3源码笔记(二)SqlSession

    1. 核心层次 2. SqlSession 先从顶层的SqlSession接口开始说起.SqlSession是MyBatis提供的面向用户的API,表示和数据库的会话对象,用于完成对数据库的一系列CR ...

  10. istio1.2.2 安装及使用示例

    前言 本文介绍istio的安装及使用 dashboard,grafana,prometheus,kiali,jaeger的配置示例.演示通过istio的ingressgateway统一访问入口 Ist ...