项目 内容
课程:北航2020春软件工程 博客园班级博客
作业:完成一个平面图形求交点的程序,体验PSP的过程 个人项目作业
我在这个课程的目标是 体验软件开发的全流程
这个作业在哪个具体方面帮助我实现目标 体会PSP的过程
教学班级 006
项目地址 https://github.com/lzhmarkk/lineCross

时间规划

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

需求分析

本次作业需要做一个计算平面直角坐标系中直线于直线之间交点的程序。

那么就浮现出以下几个问题:

  • 如何表示点:

    封装一个类,包含\(x\)和\(y\)两个属性

  • 如何去表示直线:

    \(y=kx+b\)似乎面对平行于y轴的直线有点无力,采用\(ax+by+c=0\)的形式

    封装一个类,包含\(a\),\(b\),\(c\)三个属性

  • 如何去表示圆:

    \((x-m)^2+(y-n)^2=r^2\)

    封装一个类,包含\(m\),\(n\),\(r\)三个属性

  • 直线和直线的交点如何求:

    直线联立解方程,给出预先解好的结果,直接代入求解

    \(x=\frac{b_1*c_2-b_2*c_1}{a_1*b_2-a_2*b_1}\),\(y=\frac{a_2*c_1-a_1*c_2}{a_1*b_2-a_2*b_1}\)

  • 直线与圆的交点如何求:

    • 若直线不平行于y轴:

      联立解方程,可以得到\(ix^2+jx+k=0\),其中\(i=a^2+b^2\),\(j=2ac+2nab-2mb^2\),\(k=(m^2+n^2-r^2)b^2+c^2-2nbc\)

      再考虑解\(ix^2+jx+k=0\)这个方程得到交点的x值,回代直线方程得到交点坐标

    • 若直线平行于y轴:

      判断半径与圆心到直线的距离关系,若半径更大,说明有交点,交点纵坐标可通过已知的横坐标代入圆方程解出;若半径更小,说明无交点

  • 圆和圆的交点如何求:

    两圆方程相减,得到公共弦方程或者公共切点的公切线方程或者两圆重心所在直线。再用该直线与其中任一圆相交,可得到交点。

  • 特殊情况有哪些

    • 点重合

      根据样例3,这种情况只算一个点

    • 直线平行但不重合

      意识到不可以直接带入上述公式,需要作出判断

    • 直线重合

      有无数解,但是题目规定题目输入保证答案只有有限个,故暂不考虑,编码时可以考虑使用assert

    • 圆重合

      有无数解,但是题目规定题目输入保证答案只有有限个,故暂不考虑,编码时可以考虑使用assert

  • 其他问题

    • 没用过C++

      预计花费一小时学习和摸索语法和特殊用法

    • 没用过Visual Studio,而且linux系统上没有VS

      预计花费两个小时熟悉IDE的编辑、调试和项目管理,以及性能测试等功能

设计

我设计了以下几个类和一个函数:

//Graph.h
class Dot {};//用于表示一个点 class Graph {};//接口,便于扩展圆 class Line : public Graph {};//用于表示一条线,采用ax+by+c=0表示 class Circle : public Graph {};//用于表示一个圆,采用(x-m)^2+(y-n)^2=r^2来表示 /* 用于计算图像和图像之间的交点
params: Graph *a, Graph *b
return: 图a和图b的交点列表
*/
vector<Dot> solve(Graph *a, Graph *b);
class Container {};//用于存储交点,并除去重合的点

其中solve函数逻辑如下:

测试

  • 测试Dot类,其中包括了以下几个方面:

    • 构造函数

    • equals()方法测试,尤其是double类型存在着极小的误差

      使用const double eps = 1e-6作修正,若\(|a-b|<eps\)则视为\(a==b\)

  • 测试Line类,其中包括以下几个方面:

    • 两种构造函数的测试(通过点构造和直接通过abc的值构造)
    • 斜率(如果存在的话)值的计算和截距(如果存在的话)值的计算
  • 测试Circle类,其中包括以下几个方面:

    • 构造函数的测试
  • 测试solve方法,其中输入为以下几种情况:

    • 线与线

      • 平行
      • 重合
      • 不平行
    • 圆圆
      • 相离
      • 相交
      • 内切
      • 外切
      • 内含
      • 同心
      • 重合
    • 线圆
      • 线垂直于x轴
      • 线垂直于y轴
      • 线不垂直于x或y轴
  • 测试Container模块,主要是对相同(两者各自坐标差在eps范围之内的)的点能否做到不重

对每一种情况,我都设计一组数据对其进行测试。

性能优化

刚开始我的Container是采用vector来实现,每次得到一个点的时候都需要检查之前处理过的所有点以检查重复性,同时担心double类型的误差,我均要利用eps来减小误差,导致其时间非常长

后来将Container改写成了set的结构,使得其效率得到极大的提升

以下是VS给出的性能分析图

图中可以看出,构建RB树的时间占用最多,其中最大一部分就是std::less<Dot>::operator()也就是比较两个结点之间的时间

再后来我取消了esp的考虑,因为发现利用RB树来构建set的话,其中有以下几个原因:

  • 比较两个结点所占的时间超过了总程序运行时间的80%,取消eps效率提升接近一倍
  • 出现\(10^{-7}\)以下误差的概率比较低,在直线相交的情况下几乎不可能出现

代码风格

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

关键代码

由于源代码相对较长,这里给出伪代码

void solve(Container *con, Graph* g1, Graph* g2){
if (aIsLine && bIsLine) {
//if both is line
if (!parrallel){
double x = (b1 * c2 - b2 * c1) / (a1 * b2 - a2 * b1);
double y = (a2 * c1 - a1 * c2) / (a1 * b2 - a2 * b1);
con->add(Dot(x, y));
}
} else if (aIsLine && !bIsLine) {
//if g1 is line and g2 is circle
if (g1 is vertical) {
double distance = pow(_r, 2) - pow(x - _m, 2);
if (distance >= 0) {
calculateY();//垂径定理求y
con->add(Dot(x1, y1));
con->add(Dot(x2, y2));
}
} else {
euqationX = equation(g1, g2);//联立直线和圆,消去y
solveEquation(euqationX);//解关于x的方程
con->add(Dot(x1, y1));
con->add(Dot(x2, y2));
}
} else if (!aIsLine && bIsLine) {
//if g1 is circle and g2 is line
return solve(con, g2, g1);
} else {
//if both are circle
Line* l = g2 - g1;
return solve(con, l, g2);
}
}

因为采用了非常非常暴力的方法来联立求解线和圆、圆和圆的交点,所以在计算上相对缓慢而且可能会造成double类型的误差的累积。

但是笔者也承认其中存在着bug,那就是在处理圆的时候double的精度问题几乎没有修正,最终结果基本只有小数点后的前6位是可信的,那么在判断点和点是否重合的时候,必然会造成误差和错误。

PSP初体验:求交点的更多相关文章

  1. 总结/PSP初体验—排球计分程序1.0

    要做一个排球计分程序,墨迹了很长时间才做出个的东西,过程很不爽: 功能:这个软件有两个页面,可以实现窗体A的部分变化控制窗体B的部分变化.A是操作人员使用看到的,B是投放给观众的,完全由A操控: 学到 ...

  2. 【阿里云产品公测】消息队列服务MQS java SDK 机器人应用初体验

    [阿里云产品公测]消息队列服务MQS java SDK 机器人应用初体验 作者:阿里云用户啊里新人   初体验 之 测评环境 由于MQS支持外网访问,因此我在本地做了一些简单测试(可能有些业余),之后 ...

  3. (数据科学学习手札35)tensorflow初体验

    一.简介 TensorFlow时谷歌于2015年11月宣布在Github上开源的第二代分布式机器学习系统,目前仍处于快速开发迭代中,有大量的新功能新特性在陆续研发中: TensorFlow既是一个实现 ...

  4. Node.js 网页瘸腿爬虫初体验

    延续上一篇,想把自己博客的文档标题利用Node.js的request全提取出来,于是有了下面的初哥爬虫,水平有限,这只爬虫目前还有点瘸腿,请看官你指正了. // 内置http模块,提供了http服务器 ...

  5. SRM1154--Topcoder初体验

    SRM 711 DIV2 <br > 在frank_c1的帮助下,辣鸡Xiejiadong也开始做Topcoder辣...... <br > 这算是一次Topcoder的初体验 ...

  6. Handlebars的基本用法 Handlebars.js使用介绍 http://handlebarsjs.com/ Handlebars.js 模板引擎 javascript/jquery模板引擎——Handlebars初体验 handlebars.js 入门(1) 作为一名前端的你,必须掌握的模板引擎:Handlebars 前端数据模板handlebars与jquery整

    Handlebars的基本用法 使用Handlebars,你可以轻松创建语义化模板,Mustache模板和Handlebars是兼容的,所以你可以将Mustache导入Handlebars以使用 Ha ...

  7. Python 3.8.0 正式版发布,新特性初体验 全面介绍

    Python 3.8.0 正式版发布,新特性初体验 北京时间 10 月 15 日,Python 官方发布了 3.8.0 正式版,该版本较 3.7 版本再次带来了多个非常实用的新特性. 赋值表达式 PE ...

  8. 深度学习之TensorFlow安装与初体验

    深度学习之TensorFlow安装与初体验 学习前 搞懂一些关系和概念 首先,搞清楚一个关系:深度学习的前身是人工神经网络,深度学习只是人工智能的一种,深层次的神经网络结构就是深度学习的模型,浅层次的 ...

  9. pytorch入门2.1构建回归模型初体验(模型构建)

    pytorch入门2.x构建回归模型系列: pytorch入门2.0构建回归模型初体验(数据生成) pytorch入门2.1构建回归模型初体验(模型构建) pytorch入门2.2构建回归模型初体验( ...

随机推荐

  1. C# 输出一个字符串的前缀、后缀和它的子串(信息内容安全 实验一)

    一.什么是前后缀 字符串的前缀:符号串左部的任意子串(或者说是字符串的任意首部) 字符串的后缀:符号串右部的任意子串(或者说是字符串的任意尾部) 举例:比如 101110 它的前缀就是空串.1.10. ...

  2. java例题_15 有小到大排序

    1 /*15 [程序 15 排序] 2 题目:输入三个整数 x,y,z,请把这三个数由小到大输出. 3 程序分析:我们想办法把最小的数放到 x 上,先将 x 与 y 进行比较,如果 x>y 则将 ...

  3. WebGPU[4] 纹理三角形

    代码见:https://github.com/onsummer/my-dev-notes/tree/master/webgpu-Notes/04-texture-triangle 原创,发布日 202 ...

  4. 基于Hive进行数仓建设的资源元数据信息统计:Spark篇

    在数据仓库建设中,元数据管理是非常重要的环节之一.根据Kimball的数据仓库理论,可以将元数据分为这三类: 技术元数据,如表的存储结构结构.文件的路径 业务元数据,如血缘关系.业务的归属 过程元数据 ...

  5. [Fundamental of Power Electronics]-PART I-4.开关实现-0 序

    4 开关实现 在前面的章节中我们已经看到,可以使用晶体管,二极管来作为Buck,Boost和其他一些DC-DC变换器的开关元件.也许有人会想为什么会这样,以及通常如何实现半导体的开关.这些都是值得被提 ...

  6. OO第三单元作业——魔教规格

    OO第三单元作业--魔教规格 JML的理论基础和相关工具   JML(Java Modeling Language,Java建模语言),在Java代码种增加了一些符号,这些符号用来标志一个方法是干什么 ...

  7. OO第一单元作业——魔幻求导

    简介 本单元作业分为三次 第一次作业:需要完成的任务为简单多项式导函数的求解. 第二次作业:需要完成的任务为包含简单幂函数和简单正余弦函数的导函数的求解. 第三次作业:需要完成的任务为包含简单幂函数和 ...

  8. hadoop 简单安装部署

    hadoop第一课:虚拟机搭建和安装hadoop及启动 hadoop第二课:hdfs集群集中管理和hadoop文件操作 hadoop第三课:java开发hdfs hadoop第四课:Yarn和Map/ ...

  9. 了解什么是redis的雪崩和穿透?redis崩溃之后会怎么样?系统该如何应对这种情况?如何处理redis的穿透?

    缓存雪崩发生的现象 缓存雪崩的事前事中事后的解决方案 事前:redis高可用,主从+哨兵,redis cluster,避免全盘崩溃 事中:本地ehcache缓存 + hystrix限流&降级, ...

  10. 自学转行JAVA,没有项目经历怎么找工作?

    应届生或者是刚参加工作的转行人员都有这样一个疑惑,刚学出来没有工作经验,但是企业又要求你必须要有工作经验,但是刚毕业找不到工作就不可能有工作经验,感觉陷入一个死循环.其实这种情况那些企业是不可能不知道 ...