写在前面

项目 内容
所属课程 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. WPF 反射加载Geometry几何图形数据图标

    相信大家在阅读WPF相关GitHub开源项目源码时都会看见一串串这种数据 这种Geometry数据就是几何图形数据 为什么要用Geometry数据做图标? 有一种做法是使用ttf字体文件代替,不过使用 ...

  2. HarmonyOS三方件开发指南(15)-LoadingView功能介绍

    目录: 1. LoadingView组件功能介绍2. Lottie使用方法3. Lottie开发实现4.<HarmonyOS三方件开发指南>系列文章合集 1. LoadingView组件功 ...

  3. Java例题_27 100以内的素数

    1 /*27 [程序 27 求素数] 2 题目:求 100 之内的素数 3 */ 4 5 /*分析 6 * 素数:是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数. 7 * 同第二题: ...

  4. 单链表c语言实现的形式

    包括初始化,创建,查询,长度,删除,清空,销毁等操作 代码如下: #include<stdio.h> #include<stdlib.h> //定义单链表的数据类型 typed ...

  5. Istio 网络弹性 实践 之 故障注入 和 调用重试

    网络弹性介绍 网络弹性也称为运维弹性,是指网络在遇到灾难事件时快速恢复和继续运行的能力.灾难事件的范畴很广泛,比如长时间停电.网络设备故障.恶意入侵等. 重试(attempts) Istio 重试机制 ...

  6. 有了CMDB,为什么还需要应用配置管理?

    有了CMDB,为什么还需要应用配置管理? 你不妨先停下来,思考一下这个问题. 我抛出的观点是: CMDB是面向资源的管理,应用配置是面向应用的管理. 请注意,这里是面向"资源",不 ...

  7. oo第四单元作业总结暨课程总结

    oo第四单元作业总结暨课程总结 一.本单元作业架构设计 本单元需要构建一个UML解析器,通过对输入的UML类图/顺序图/状态图的相关信息进行解析以供查询,其中课程组已提供输入整体架构及输入解析部分,仅 ...

  8. 「Spring Boot 2.4 新特性」启动耗时详细监控

    背景 Spring Boot 项目随着项目开发过程中引入中间件数量的增加,启动耗时 逐渐增加. 笔者在 <Spring Boot 2.4.0 正式 GA,全面拥抱云原生>文章评论下发现了 ...

  9. Spring Boot demo系列(一):Hello World

    2021.2.24 更新 1 新建工程 打开IDEA选择新建工程并选择Spring Initializer: 可以在Project JDK处选择JDK版本,下一步是选择包名,语言,构建工具以及打包工具 ...

  10. aws eks ebs StorageClass PersistentVolume PersistentVolumeClaim

    aws EBS 提供存储资源 Amazon EBS CSI 驱动程序的安装,请参考https://docs.aws.amazon.com/zh_cn/eks/latest/userguide/ebs- ...