个人项目作业博客

1. 在文章开头给出教学班级和可克隆的 Github 项目地址(例子如下)。(1')

项目 内容
北航2020软工 班级博客
作业要求 具体要求
项目GitHub地址 个人项目
教学班级 005(周三上午三四节)

2. 在开始实现程序之前,在下述 PSP 表格记录下你估计将在程序的各个模块的开发上耗费的时间。(0.5')

见Part 7.

3. 解题思路描述。即刚开始拿到题目后,如何思考,如何找资料的过程。(3')

  1. 首先读题后,发现:这个问题,一定程度上是一个数学问题,所以思路是按照数学来解题。

  2. 先不考虑附加题:

    • 因为有重合点的情况,一组直线的所有交点,如果不计算并且存下来进行对比,最终的结果是无法保证的,所以先考虑两条直线的交点计算,再考虑存储结构。

    • 直线与直线的交点求法有多种,方便电脑计算的有哪些? ------> 调研

      • 方法一:

        通过点\((x_1, y_1),\;\;(x_2, y_2)\)求得直线\(l1\)的斜率和截距\(k_1, \;b_1\),另一条直线同理

      \[\left\{ \begin{array}{rcl}
      l_1 : y = k_1*x + b_1\\
      l_2 : y = k_2*x + b_2
      \end{array}\right.
      \]

      ​ 联立求解得

      \[\left\{ \begin{array}{rcl}
      &x_0 = \frac{b_2-b_1}{k_1-k_2}&\\
      &y_0 = k_1*x_0+b_1& \;\;(or\;y_0 =k_2*x_0+b_2)
      \end{array}\right.
      \]

      \[\left\{ \begin{array}{rcl}
      l_1 : a_1x+b_1y+c_1=0\\
      l_2 : a_2x+b_2y+c_2=0
      \end{array}\right.
      \;\;\;\;\;\;\;\ \;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;(1)
      \]

      ​ 把每条直线的两个点带入解析中得:

      \[\left\{ \begin{array} \\
      a_1 = y_2 - y_1\\
      b_1 = x_1 - x_2\\
      c_1 = x_2y_1-x_1y_2
      \end{array}\right.
      \]

      ​ 直线\(l_2\)类似,可以求得未知数\(a_2,b_2,c_2\);之后由(1)得:

      \[D =
      \left|\begin{array}{ccc}
      a_1 & b_1\\
      a_2 & b_2
      \end{array}\right|
      =a_1b_2-a_2b_1\quad\quad(D == 0 则平行否则有交点)\\
      x_0 = (b_1c_2-b_2c_1)/D\\
      y_0 = (a_2c_1-a_1c_2)/D
      \]

      ​ 这种方法的好处在于不用特判斜率不存在的情况,只需要根据D判断是否有交点进行计算即可

    • 这时考虑交点的存储结构(使用c++),每当算出一个新的交点时,都需要判断是否已经有这个点,选择三种存储结构

      • set:不重复的元素集合,但是在insert的时候有大量的排序操作
      • hashMap:通过hash值来存储,每次计算hash值来确定是否已经存在
      • vector:无脑push_back,最后扫描去重

      经过实测,最终的效果显示,第三种方式的效果更好,set和hashMap因为比较次数太频繁和计算hash值太频繁导致时间消耗很大,难以完成任务。(仅代表个人程序结果,不排除其他因素影响)

  3. 附加题的思路基于上述,考虑直线和圆,圆和圆的交点计算:

    • 直线和圆:

      • 首先判断直线和圆是否相交,通过计算圆心到直线的距离与半径比较

        向量\(\overrightarrow{AB}\)与向量 \(\overrightarrow{AC}\) 的外积的模长代表以AB、AC为边的平行四边形的面积S,利用面积公式 \(S=|\overrightarrow{AB}|*d\),可以得到 \(d = S / |\overrightarrow{AB}|\);

      • 若相交则计算交点坐标:

        1. 求垂足D的坐标:见上图,利用内积公式\(\overrightarrow{AB}·\overrightarrow{AC}=|\overrightarrow{AB}||\overrightarrow{AC}|cos\theta=|\overrightarrow{AB}|· AD\),所以可以得到AD的长度,则向量\(\overrightarrow{AD}=\frac{AD}{|\overrightarrow{AB}|}·\overrightarrow{AB}\),又因为知道了A点坐标,所以D点坐标就可以得到
        2. 根据向量\(\overrightarrow{AB}\)求得AB方向的单位向量\(\overrightarrow{e}\),然后,利用直角三角形算出GD的长度为base;
        3. 此时有了D点坐标,GD长度,和直线AB上的单位向量,可以得到G和H点的坐标:
          • \(G = D - \overrightarrow{e} * base\)
          • \(H = D + \overrightarrow{e} * base\)
    • 圆和圆的交点求法有公式法,但是从一篇博客中找到了更好的解法,就是仿照上面按照公式计算,在此不赘述。博客地址

4. 设计实现过程。设计包括代码如何组织,比如会有几个类,几个函数,他们之间关系如何,关键函数是否需要画出流程图?单元测试是怎么设计的?(4')

基本要求:

  1. 类:

    • Point:点,因为要判断是否是同一个点,需要实现判断函数
    • Line:数据实体,保存输入文件中的信息,并且用于计算
    • Circle:类似于Line(附加题需求)
  2. 函数:
    • getCross():计算两条直线的交点,将结果直接保存在全局变量Result中,具体过程为第三部分的方法二
    • getAllIntersec():把输入文件读取的Lines信息传入,计算所有的交点,简单的循环计算。

整体逻辑如下图:

可以看到,Line和Point其实并没有交集,所以这里的Line本质上可以直接删除,只需要记录对应的数据传入getAllIntersec函数即可,不过为了方便附加题的实现,这里没有那么做。

附加题:

  1. 计算交点分为三种情况:

    • 直线与直线:见上图
    • 直线与圆:循环遍历直线和每一个圆,调用函数求交点;
    • 圆与圆:循环遍历圆和圆,调用函数求交点;
  2. 读取数据后,把直线存在lines中,圆存放在circles中,然后执行上面的三种计算,交点全部存放在全局结果Result中,最后读取Result的大小即为结果。

其中直线和圆求交点的代码按照Part 3的附加题思路,具体代码见Part 6.

圆和圆求交点的代码按照博客方法计算,具体思路见Part 6.

(注:因为是按照公式来计算,所以再次不进行详细的赘述,放到Part6)

单元测试:

主要对三个子函数进行测试:

  • 直线和支线求交点

    1. 一条直线无斜率,一条有
    2. 一条直线平行于x轴,一条正常
    3. 一条直线无斜率,一条平行于x轴
    4. 两条直线都有斜率(包含了2,3)
  • 直线和圆
    1. 相交(过圆心与不过圆心两种)
    2. 相切,一个交点
    3. 不相交
  • 圆和圆
    1. 相交,外交;
    2. 相交,内交
    3. 相切,外切
    4. 相切,内切(内切又分为两种,c1内切c2,c2内切c1
    5. 分离,外离
    6. 分离,内离
  • 以及测试了一些小函数的功能,求外积和内积

最终的测试结果如下:

上面的测试时基本的功能测试,为了进一步测试,通过python生成随机数据来进行验证,和同学对拍来验证结果。

通过对拍,找出以下bug:

  • 直线与圆相切计算,需要控制精度,否则会因为计算产生的误差导致判断失误
  • 圆和圆的内切需要分两种情况
  • 以及不同的精度控制范围导致结果的不同,最后测试出自己程序的稳定范围在\(1^{-11} \thicksim 1^{-10}\),这个精度对于直线和直线的计算,已经足够,因为数据范围位\((-100000, 100000)\),最多出现二次项,也就是\(1^{10}\)的级别,对应于精度\(1^{-10}\),而直线和圆,圆和圆的计算,引入的计算误差使得精度到了\(1^{-12}\)时程序结果就不稳定了。

代码分支覆盖率如下图:

5. 记录在改进程序性能上所花费的时间,描述你改进的思路,并展示一张性能分析图(由 VS 2019 的性能分析工具自动生成),并展示你程序中消耗最大的函数。(3')

从写完最初的版本到现在几乎一直处于改进状态,大概是10个小时吧,期间找了很多资料,也和同学谈论了很多。

  1. 思考能否不计算出交点坐标就能到最后的结果,目前找不到相关说明,自己也没有头绪;

  2. 改善固有程序:

    • 一开始使用set来存计算出来的Point,这样好处是可以自动去重,问题是数据变大后set.insert()是一个占据很大热点的函数,耗时太多,超时;
    • 考虑hashmap,但是一个同学使用的hashmap,他的程序效果同样很慢,自己初步尝试后放弃;
    • 既然这样,干脆选择最简单的vector,最后进行去重,一开始无脑push_back计算出来的交点,最后操作一次,因为满足交点个数小于MAX=5000000,只要在超过MAX时就去重,这样最后在去重一次,就不会出现无脑push的导致重复点过多的问题。

    包装vector.push_back()函数,添加后,若超过MAX,就排序去重,然后添加,最后全部计算完成后,再次排序去重,得到结果。性能截图如下,主要的热点就是排序去重的部分,占据76%左右,而set占据80%左右,好处是vector操作较为轻量级。总体时间节省不少。

  1. 热点函数展示

6. 代码说明。展示出项目关键代码,并解释思路与注释说明。(3')

  • 直线和直线求交点:

    采用前述的方法二计算,按照公式逻辑计算即可,只需要特判两直线是否平行即可。

    //l1: a1*x + b1*y + c1 = 0
    //l2: a2*x + b2*y + c2 = 0
    //向量法求解:
    //D判断是否平行
    //x0 = (b1 * c2 - b2 * c1) / D
    //y0 = (a2 * c1 - a1 * c2) / D
    bool getCross(Line l1, Line l2, Point* res)
    {
    //求l1的a1,b1,c1
    double a1 = l1.getPy() - l1.getQy();
    double b1 = l1.getQx() - l1.getPx();
    double c1 = l1.getPx() * l1.getQy() - l1.getQx() * l1.getPy();
    //求l2的a2,b2,c2
    double a2 = l2.getPy() - l2.getQy();
    double b2 = l2.getQx() - l2.getPx();
    double c2 = l2.getPx() * l2.getQy() - l2.getQx() * l2.getPy(); double D = a1 * b2 - a2 * b1;
    //平行则退出,没有交点
    if (D == 0) {
    return false;
    }
    res->setPoint((b1 * c2 - b2 * c1) / D, (a2 * c1 - a1 * c2) / D);
    return true;
    }
  • 直线和圆求交点:

    1. 先判断是否有交点
    2. 然后利用向量法求解
    bool getCircleLineCross(Circle c, Line l)
    {
    Point ceter = c.getCeter();
    double R = c.getR();
    //求圆心到直线的距离
    double juli = getDistance(l, ceter);
    //判断是否相交,或者相切,还是不相交
    if (juli > R + PRECISION) {
    return false;
    } //求垂足的坐标
    Vector segment = l.getQ() - l.getP();
    double ratio = dot(ceter - l.getP(), segment) / segment.norm();
    Point foot = l.getP() + segment * ratio; //特判,如果相切,则交点就是垂足坐标
    if (abs(juli - R) < PRECISION) {
    addPoint(foot);
    return true;
    } //直线AB的单位向量,与AB同向
    Vector e = segment / segment.module();
    //base = 直线与圆相交的弦的一半. 利用勾股定理
    double base = sqrt(R * R - (ceter - foot).norm());
    //向量加减得到两个点的坐标
    Point p1 = foot - e * base;
    Point p2 = foot + e * base;
    addPoint(p1);
    addPoint(p2);
    return true;
    }
  • 圆和圆求交点:

    按照公式步骤求解,具体逻辑就是计算公式,不赘述。

    bool getCircleCross(Point c1, double r1, Point c2, double r2)
    {
    double x1 = sqrt((c1 - c2).norm());
    double b1 = abs(r1 - r2);
    double b2 = abs(r1 + r2);
    //判断相离,内离和外离
    if (x1 < b1 || x1 > b2) {
    return false;
    }
    //外切
    else if (x1 == b2) {
    Vector e = (c2 - c1) / (c2 - c1).module();
    addPoint(c1 + e * r1);
    return true;
    }
    //内切
    else if (x1 == b1) {
    Vector e = (c2 - c1) / (c2 - c1).module();
    if (r1 < r2) {
    addPoint(c1 - e * r1);
    }
    else {
    addPoint(c1 + e * r1);
    }
    }
    //相交
    else {
    Vector AB = (c2 - c1);
    double l = AB.module();
    Vector e = AB / l;
    double AE = (r1 * r1 - r2 * r2 + l * l) / (2 * l);
    Point E = c1 + AB * AE / l;
    double CE = sqrt(r1 * r1 - AE * AE);
    //两圆心横坐标相同
    if (c1.getX() == c2.getX()) {
    Point left(E.getX() - CE, E.getY());
    Point right(E.getX() + CE, E.getY());
    addPoint(left);
    addPoint(right);
    }
    //两个圆心纵坐标相同
    else if (c1.getY() == c2.getY()) {
    Point up(E.getX(), E.getY() - CE);
    Point down(E.getX(), E.getY() + CE);
    addPoint(up);
    addPoint(down);
    }
    //一般情况
    else {
    double k1 = (c2.getY() - c1.getY()) / (c2.getX() - c1.getX());
    double k2 = -1 / k1;
    double EF = sqrt(CE * CE / (1 + k2 * k2));
    double cx = E.getX() - EF;
    double cy = E.getY() + k2 * (cx - E.getX());
    double dx = E.getX() + EF;
    double dy = E.getY() + k2 * (dx - E.getX());
    Point tmp(cx, cy);
    addPoint(tmp);
    tmp.setPoint(dx, dy);
    addPoint(tmp);
    }
    }
    return true;
    }

7. 在你实现完程序之后,在下述 PSP 表格记录下你在程序的各个模块上实际花费的时间。(0.5')

PSP2.1 Personal Software Process Stage 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 60 60
· Estimate · 估计这个任务需要的时间
Development 开发 480 720
· Analysis · 需求分析 30 60
· Design Spec · 生成设计文档 30 30
· Design Review · 设计复审 30 15
· Coding Standard · 代码规范 30 15
· Design · 具体设计 60 60
· Coding · 具体编码 120 180
· Code Review · 代码复审 60 240
· Test · 测试 120 120
Reporting 报告 60 120
· Test Report · 测试报告 30 60
· Size Measurement · 计算工作量 15 30
· Postmorten & Process Improvement Plan · 事后总结,并提出过程改进计算 15 30
合计 600 900+

附录

Code Quality Analysis

【BUAA_2020_软工】个人作业的更多相关文章

  1. 软工团队作业--Scrum冲刺集合贴

    软工团队作业--Scrum冲刺集合贴 团队 团队名称:广东靓仔六强选手 团队成员: 黄清山 黄梓浩 钟俊豪 周立 邓富荣 郑焕 博客链接 Scrum 冲刺 第一篇 Scrum 冲刺 第二篇 Scrum ...

  2. 2021软工-调研作业-Notion

    2021软工-调研作业-Notion 项目 内容 这个作业属于哪个课程 2021春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 案例分析作业要求 我在这个课程的目标是 学习软件开发的工业化 ...

  3. 2020BUAA软工热身作业

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

  4. 软工个人作业4——Alpha阶段个人总结

    一.个人总结 1.在alpha 结束之后, 每位同学写一篇个人博客, 总结自己的alpha 过程: 经过本次alpha阶段的冲刺,首先学到了很多,收获了很多,同时也蛮辛苦的.其实我觉得作为组员我有很认 ...

  5. 2020BUAA 软工-结对作业

    结对作业 项目 内容 北航2020软工 班级博客 作业要求 具体要求 1.在文章开头给出教学班级和可克隆的 GitHub 项目地址(例子如下).(1') 教学班级 005 项目地址 GitHub 2. ...

  6. Fzu软工第一次作业-准备篇

    (0)一些想说的话 我叫陈浩,是16级计算机一个很普通的学生.我是补选选软工实践这门课的.选软工实践这门课我想了很久.因为学长学姐告诉我这是一门很锻炼人又很辛苦的课程,需要大量的时间和精力.然而如今的 ...

  7. BUAA软工第一次作业-热身

    第一次作业-热身 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) (北京航空航天大学 - 计算机学院) 这个作业的要求在哪里 第一次作业-热身作业(阅读) 我在这个课程 ...

  8. 停下来,回头看 ——记2020BUAA软工第一次作业-热身!

    description: 'Mar 1st, 2020 - Mar 3rd, 2020' 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任建) 这个作业的要求在哪里 第一次作 ...

  9. 2018软工实践作业八之UML设计

    1. 团队信息 队名:小白吃队 成员: 后敬甲 031602409 卢泽明 031602328 蔡文斌 031602301 葛亮 031602617 刘浩 031602423 黄泽 031602317 ...

随机推荐

  1. c++ 反汇编 继承

    单继承,父类中没有虚函数 单继承,父类存在虚函数,子类重写虚函数 单继承,父类存在虚函数,子类不新定义虚函数 单继承,父类存在虚函数,子类新定义虚函数 单继承,父类不存在虚函数,子类定义虚函数 多继承 ...

  2. Android Studio中Switch控件有关 textOn 和 textOff 用法

    •属性 textOn:控件打开时显示的文字 textOff:控件关闭时显示的文字 showText:设置是否显示开关上的文字(API 21及以上) •用法 <?xml version=" ...

  3. 第14 章 : Kubernetes Service讲解

    Kubernetes Service 本文将主要分享以下四方面的内容: 为什么需要 K8s service: K8s service 用例解读: K8s service 操作演示: K8s servi ...

  4. Rancher 安装和使用-实践

    Rancher 安装和使用 Rancher是一个完整的,开源的平台,用于在生产环境中部署和管理容器.它包括Kubernetes,Mesos和Docker Swarm的商业支持发行版,使得在任何基础架构 ...

  5. Ansible 教程

    [注]本文译自:https://www.edureka.co/blog/ansible-tutorial/   在阅读本文之前,你应该已经知道,Ansible 构成了 DevOps 认证的关键部分,它 ...

  6. (二)基于商品属性的相似商品推荐算法——Flink SQL实时计算实现商品的隐式评分

    系列随笔: (总览)基于商品属性的相似商品推荐算法 (一)基于商品属性的相似商品推荐算法--整体框架及处理流程 (二)基于商品属性的相似商品推荐算法--Flink SQL实时计算实现商品的隐式评分 ( ...

  7. matlab结构体、数组和单元数组类型的创建

    matlab结构体.数组和单元数组类型的创建 @ 目录 matlab结构体.数组和单元数组类型的创建 matlab结构体类型 数组类型 单元数组类型 matlab结构体类型 通过字段赋值创建结构体 创 ...

  8. Day13_67_interrupt() 方法

    interrupt() 方法 中断线程 * interrupt()方法的简单理解 - interrupt() 方法只是改变线程的阻塞状态而已,让一个正在阻塞状态的线程,恢复执行.但是它不会中断一个正在 ...

  9. Manachar's Algorithm

    1.模板 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int MAX=21000020; 4 char s[MAX], ...

  10. 浅谈Java的反射机制和作用

    浅谈Java的反射机制和作用 作者:Java大师 欢迎转载,转载请注明出处 很多刚学Java反射的同学可能对反射技术一头雾水,为什么要学习反射,学习反射有什么作用,不用反射,通过new也能创建用户对象 ...