写在前面

项目 内容
所属课程 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. Tkinter系列教程01—引言和安装Tk—Python GUI编程

    目录 Tkinter教程系列01--引言和安装Tk 引言 什么是Tkinter 安装 Tk 为 Windows 安装 Tk 验证是否安装正确 为 GNU/Linux 安装 Tk 使用 Linux 的包 ...

  2. C# .NET Socket 简单实用框架,socket组件封装

    参考资料 https://www.cnblogs.com/coldairarrow/p/7501645.html 根据.NET Socket 简单实用框架进行了改造,这个代码对socket通信封装还是 ...

  3. Python基础(十四):while循环与break、continue关键字

    前面我们已经学习了for循环,今天再带着大家讲述一下while循环.那么for循环和while循环,到底有什么区别呢? Python中for循环和while循环本质上是没有区别的,但是在实际应用上,针 ...

  4. Dynamic CRM登陆界面的客制化(持续更新)

    Dynamic CRM的登陆页面比较西化,不是很适合中国人使用.目前先把注销跳转的问题解决了. 服务端使用下面命令,将文件导出来 Export-AdfsWebTheme –Name default – ...

  5. [Skill]从零掌握正则表达式

    前言 无论你是出于什么原因需要掌握正则表达式(诸如爬虫.文本检索.后端服务开发或Linux脚本),如果之前从没接触过正则表达式(比如我)很容易在如山般的公式中迷失,以至于你在项目写的正则表达式很可能会 ...

  6. 《Effective C++》部分内容学习笔记整理

    简介 此笔记为<Effective C++>中部分内容的学习笔记. 目录 文档:<Effective C++>

  7. k8s helm 安装etcd

    待续 helm install etcd bitnami/etcd \ --set statefulset.replicaCount=3 \ --set persistence.enabled=tru ...

  8. PAT归纳总结——关于二叉树的一些总结

    今天是6月26日到下个月的这个时候已经考过试了,为了让自己考一个更高的分数,所以我打算把PAT的相关题型做一个总结.目前想到的方法就是将相关的题型整理到一起然后,针对这种题型整理出一些方法. 二叉树的 ...

  9. flex 弹性盒模型的一些例子;

    1.垂直居中 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UT ...

  10. 关于Spring Data JPA 多表查询 返回自定义Vo的问题记录

    这两天开了一个新项目,使用SpringBoot+SpringData,  刚做了一个小功能,都是一张表的操作没什么问题,今天设计到了两张表联查,两张表各取了几个字段,组合成了一个vo, 当我用原生sq ...