写在前面

项目 内容
所属课程 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. 数据库SQL查询作业

    --设有三个关系 --S(S#,SNAME,AGE,SEX) --SC(S#,C#,GRADE) --C(C#,CNAME,TEACHER) --(1)检索LIU老师所授课程的课程号.课程名 sele ...

  2. 数数字(JAVA语言)

    package 第三章习题; /*  * 把前n(n<=10000)个整数顺次写在一起:  * 89101112...  * 数一数0-9各出现多少次  * (输出10个整数,分别是09出现的次 ...

  3. 《逆向工程核心原理》——通过调试方式hook Api

    1.附加目标进程, 2.CREATE_PROCESS_DEBUG_EVENT附加事件中将目标api处设置为0xcc(INT 3断点) 3.EXCEPTION_DEBUG_EVENT异常事件中,首先判断 ...

  4. Maven项目中resources配置总结

    目录 背景 第一部分 基本配置介绍 第二部分 具体配置和注意事项 第三部分 读取resources资源 参考文献及资料 背景 通常Maven项目的文件目录结构如下: # Maven项目的标准目录结构 ...

  5. 冒泡算法(BubbleSort)

    /*冒泡排序原理 比较相邻的元素.如果前一个元素比后一个元素大,就交换这两个元素的位置. 对每一对相邻元素做同样的工作,从开始第一对元素到结尾的最后一对元素.最终最后位置的元素就是最大值.实现步骤 1 ...

  6. 深度学习---1cycle策略:实践中的学习率设定应该是先增再降

    深度学习---1cycle策略:实践中的学习率设定应该是先增再降 本文转载自机器之心Pro,以作为该段时间的学习记录 深度模型中的学习率及其相关参数是最重要也是最难控制的超参数,本文将介绍 Lesli ...

  7. SpringCloudAlibaba—微服务概念及SpringCloudAlibaba介绍

    目录 1.1 系统架构演变 1.1.1 单体应用架构 1.1.2垂直应用架构 1.1.3 分布式架构 1.1.4 SOA架构 1.1.5 微服务架构 1.2 微服务架构介绍 1.2.1 微服务架构的常 ...

  8. oo第二单元——多线程魔鬼电梯

    在初步认识了面向对象思想后,立刻进入了多线程的学习,本单元的难点主要是锁的理解,需要保证线程安全的同时防止死锁的发生,也要尽可能缩小锁的范围,提高性能.这一单元以电梯为载体,让我们从生活出发,从电梯运 ...

  9. springboot项目整合mybatis

    记录创建springboot项目并配置mybatis中间件: 资源准备及版本说明 编程工具:IDEA JDK版本:1.8 Maven版本:Apache Maven 3.6.3 springboot版本 ...

  10. 解决SQLPLUS无法使用上下箭头

    1 问题描述 SQLPLUS中使用上下箭头无法获取历史命令,如下图所示: 按上下箭头会显示^[[A/^[[B. 2 解决方案 需要安装rlwrap,可以的话可以用包管理器安装,笔者环境CentOS,这 ...