BUAA 2020 软件工程 个人项目作业
BUAA 2020 软件工程 个人项目作业
Author: 17373051 郭骏
| 项目 | 内容 |
|---|---|
| 这个作业属于哪个课程 | 2020春季计算机学院软件工程(罗杰 任健) |
| 这个作业的要求在哪里 | 个人项目作业 |
| 我在这个课程的目标是 | 学习软件工程的开发知识,培养工程化开发能力 |
| 这个作业在哪个具体方面帮助我实现目标 | 通过实操掌握PSP开发基础 |
1.前言
给定 N 条直线,询问平面中有多少个点在至少 2 条给定的直线上。题目输入保证答案只有有限个。
2.PSP表格
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 5 | 5 |
| · Estimate | · 估计这个任务需要多少时间 | 5 | 5 |
| Development | 开发 | 250 | 490 |
| · Analysis | · 需求分析 (包括学习新技术) | 20 | 40 |
| · Design Spec | · 生成设计文档 | 20 | 20 |
| · Design Review | · 设计复审 (和同事审核设计文档) | 5 | 5 |
| · Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 5 | 5 |
| · Design | · 具体设计 | 60 | 60 |
| · Coding | · 具体编码 | 60 | 60 |
| · Code Review | · 代码复审 | 20 | 60 |
| · Test | · 测试(自我测试,修改代码,提交修改) | 60 | 240 |
| Reporting | 报告 | 70 | 70 |
| · Test Report | · 测试报告 | 30 | 30 |
| · Size Measurement | · 计算工作量 | 10 | 10 |
| · Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 30 | 30 |
| 合计 | 325 | 565 |
3.解题思路(没写程序前)
暴力求解
对于这个题,最直接的求解方式是用暴力求解法。
- 1.对于每一条直线给出的两点,求出直线的一般式。
- 2.对这\(n\)条直线,每两条直线求一次交点,共需要求\(\frac{n(n-1)}{2}\)次交点。
- 3.对这\(\frac{n(n-1)}{2}\)个交点进行去重,暴力去重法需要每两个点比较一次。
这样做显然时间开销非常大,且时间复杂度最大的环节在第三步,会达到\(O(n^4)\)级别。
改进去重
对于判断两个点是否为同一个,我们可以通过改进数据结构的方式来进行。
我们使用哈希表来存储交点的坐标。每计算出一个坐标,就将它存入哈希表中。哈希表通过哈希函数直接计算寻址,通过判断寻址处有没有重复元素来判断是否重复。理想情况下,每次寻址均没有出现哈希冲突,则为\(m\)个点去重的时间复杂度为\(O(m)\)。
交点的个数对于平面上所有点来说是稀疏的,在哈希函数设计较好的情况下,可以很大程度上减少哈希冲突的发生,即使是最坏的情况下,也和暴力对比法开销相近。
改进求交点个数
在最坏情况下,我们确实需要对没两条直线都求一次交点。但是由简单的数学知识可知,当两条直线平行时,他们必然没有交点。所以,我们可以将互相平行的直线找出来并入同一组,在最后只需要对组间直线求交点即可。
假设\(n\)条直线中,有\(a_1\)条直线互相平行,又有\(a_2\)条直线互相平行……又有\(a_k\)条直线互相平行,且\(a_1+a_2+\cdots+a_k=n\),每两组直线之间不平行,则这些直线的交点个数最多为\(\sum^{1\leq i,j\leq k}_{i\not=j}a_ia_j\)。显然,当所有的\(a_i\)并不全都为1的时候,交点个数是小于\(\frac{n(n-1)}{2}\)的。只要交点个数能够在去重之前变少,去重时的开销也必然变少。
下面我们来考虑如何判断直线平行。我们记录直线的斜率(平行于y轴特判),用斜率作为键建立哈希表,和上文同样的道理,为\(n\)条直线判断是否平行在理想情况下只需要\(O(n)\)的复杂度。
性能测试中,直线条数最多500000条,理论上最多可以产生1.25×10^11个交点,但交点个数限制在5000000以内,所以此类优化能够取得很大的效果,很难逼近最坏情况。
附加题:暴力求解
附加题依然采用暴力求解+哈希存储的方式解决。
- 对于直线和圆,将直线方程带入圆中可以解出坐标。可以提前判断直线和圆的距离来判断两者有无交点,如果有交点,再进行判别式的计算,可以节省一定的开销。
- 对于圆和圆,首先判断两圆的位置关系是否有相交,然后再将两者方程作差得到一条直线,这条直线过且只过他们的交点,可以将问题转化为圆和直线的问题,调用前面的函数即可解决。
4.设计实现过程
本次题目的工程量不算很大,也需要我们在一周内完成,所以我虽然使用的是C++语言,但不会特别强调到面向对象的特性。对于对象内的属性,也没有设置private去保护。这主要是为了增加程序性能。
代码整体上分为四个类:
class Point:点类。存储点的坐标等相关信息。class Line:直线类。存储直线的斜率、纵轴截距。有计算和直线交点的方法。class Circle:圆类。附加题专用,存储圆的圆心坐标和半径。有计算和直线、圆交点的方法。class Individual:程序的主类。包含输入分析方法、数据存储方法、计算交点方法。核心在于unordered_set保存点的坐标。
关于垂直于y轴的直线:
定义极大常数INF特判,当斜率为INF时,截距值默认等于横轴截距。
因为所有输入均为整数,且数字有范围,所以斜率的最大值不可能超过200000。
这里设INF=500000。
单元测试的设计中,包含以下内容:
- 题目中所给的简单样例
- 边界条件,比如斜率不存在,以及交点的距离非常近
- 复杂情况,主要是多条直线交于多个点的手造数据。
在实现过程中,我发现,double类型的数据容易丢失精度,在去重的时候容易引起误判,且能够造出相关数据来导致误差。所以在实现的过程中,我控制前面所有的计算均使用long long类型来进行,在整个步骤中只进行一次除法,这样能够将double精度带来的的误差降至最低,经测验,能够通过我手造的特殊数据测试。
5.性能分析
以下是性能分析图。

从图中可以看出,算法耗费时间最久的方法是calc方法,这个方法中,最耗时的函数又是insert函数。
显然,程序主要的时间都花在哈希函数的比较去重上。但是此块,我并没有想到非常好的优化方法,只能尽可能在其他地方寻找优化。

比如图中63、66、67行,将end()方法只调用一次,以静态存储的方式记录迭代器末尾的位置,而不是写在for循环的条件中,经验证,可以一定程度上降低CPU占用,所以我采用了这种写法。
6.代码说明
此处附上代码质量分析图、单元测试通过图、代码覆盖率图。



代码覆盖率并没有达到100%。原因是我在程序中写了一些冗余的函数。这些函数在执行过程中没有被调用,但是我并没有删掉他们,因为这些函数是最开始就写好的,以备不时之需而使用,如果需求有所增加,可以直接使用,但目前无法进行测试,也不会对当前程序运行带来影响。
另外没有覆盖到的内容还有对命令行参数的处理。我写了略微复杂的if逻辑结构,占用了main函数较大的篇幅,所以main.cpp看上去覆盖率较低。
关键代码如下所示。
// 求两条直线的交点
// 直线方程为kx-dy+b=0
Point Line::getIntersect(Line& geo) {
// 没有实现两条直线平行时的特判
// 判断直线斜率是否存在
if (d == 0) {
return Point(-b, k, -geo.k * b + geo.b, geo.d * k);
}
if (geo.d == 0) {
return Point(-geo.b, geo.k, -k * geo.b + b, geo.k * d);
}
//这里计算出点坐标的分子(xu, yu)和分母(down)
long long xu = geo.b * d - b * geo.d;
long long yu = k * geo.b - geo.k * b;
long long down = k * geo.d - geo.k * d;
//在最后一步,建立点类时才做除法
return Point(xu, down, yu, down);
}
// 求两个圆的交点
vector<Point> Circle::getIntersect(Circle& geo) {
// 计算圆心间的距离,和半径作比较,确定两个圆有交点
long long dl = (a - geo.a) * (a - geo.a) + (b - geo.b) * (b - geo.b);
if (dl <= (r + geo.r) * (r + geo.r) && dl >= (r - geo.r) * (r - geo.r)) {
// 计算出圆方程相减得到的直线方程
long long xc = 2 * (geo.a - a);
long long yc = -2 * (geo.b - b);
long long c = a * a + b * b - r * r
- geo.a * geo.a - geo.b * geo.b + geo.r * geo.r;
Line tmp(xc, yc, c);
// 创建临时直线,调用求交点方法
return tmp.getIntersect(*this);
}
// 没有交点,返回空的向量
return vector<Point>();
}
BUAA 2020 软件工程 个人项目作业的更多相关文章
- BUAA 2020 软件工程 结对项目作业
Author: 17373051 郭骏 3.28添加:4.计算模块接口的设计与实现过程部分,PairCore实现的细节 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) ...
- BUAA软件工程个人项目作业
BUAA软件工程个人项目作业 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 个人项目作业 我在这个课程的目标是 学习软件开发的流程 这个作业在哪 ...
- BUAA 2020 软件工程 个人博客作业
BUAA 2020 软件工程 个人博客作业 Author: 17373051 郭骏 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 个人博客作业 ...
- BUAA 2020 软件工程 热身作业
BUAA 2020 软件工程 热身作业 Author: 17373051 郭骏 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 第一次作业-热身! ...
- 【BUAA 软工个人项目作业】玩转平面几何
BUAA 软件工程个人项目作业 项目 内容 课程:2020春季软件工程课程博客作业(罗杰,任健) 博客园班级链接 作业:BUAA软件工程个人项目作业 作业要求 课程目标 学习大规模软件开发的技巧与方法 ...
- BUAA 2020 软件工程 提问回顾与个人总结
BUAA 2020 软件工程 提问回顾与个人总结 Author: 17373051 郭骏 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 提问回顾 ...
- BUAA软件工程结对项目作业
BUAA软件工程结对项目 小组成员:16005001,17373192 1.教学班级和项目地址 项目 内容 这个作业属于哪个课程 博客园班级连接 这个作业的要求在哪里 结对项目作业 我在这个课程的目标 ...
- BUAA 2020 软件工程 软件分析案例作业
Author: 17373051 郭骏 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 个人博客作业-软件分析案例 我在这个课程的目标是 学习软件 ...
- BUAA 软工 结对项目作业
1.相关信息 Q A 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 结对项目作业 我在这个课程的目标是 系统地学习软件工程开发知识,掌握相关流程和技术,提升 ...
随机推荐
- Java-Bean Validation后端校验总结
Validation Information resource: SpringBoot Docs: 2.8.9. @ConfigurationProperties Validation url: ht ...
- 浅谈KMP模式匹配算法
普通的模式匹配算法(BF算法) 子串的定位操作通常称为模式匹配算法 假设有一个需求,需要我们从串"a b a b c a b c a c b a b"中,寻找内容为"a ...
- 源码编译安装 LNMP及论坛
一.安装Nginx服务 systemctl stop firewalld systemctl disable firewalld setenforce 0 1.安装依赖包 yum -y install ...
- RocketMQ详解(四)核心设计原理
专题目录 RocketMQ详解(一)原理概览 RocketMQ详解(二)安装使用详解 RocketMQ详解(三)启动运行原理 RocketMQ详解(四)核心设计原理 RocketMQ详解(五)总结提高 ...
- 使用Eclipse的基本配置
因本人 IntelliJ IDEA 正版授权前些日子已到期,最近开始使用 Eclipse .体验开发了一阵子,觉得除了在界面美观与前端编辑的操作上 Eclipse 与 IDEA 差距还比较大以外,其他 ...
- 动态规划精讲(一)A单串
单串 单串 dp[i] 线性动态规划最简单的一类问题,输入是一个串,状态一般定义为 dp[i] := 考虑[0..i]上,原问题的解,其中 i 位置的处理,根据不同的问题,主要有两种方式: 第一种是 ...
- PTA面向对象程序设计6-3 面积计算器(函数重载)
实现一个面积计算器,它能够计算矩形或长方体的面积. 函数接口定义: int area(int x, int y); int area(int x, int y, int z); 第一个函数计算长方形的 ...
- nginx proxy_next_upstream 与openresty balancer.set_more_tries的使用
背景 我们这边网关服务使用的 kong,前段时间上线一个服务,这个服务它报错了,产生了502的错误码,追查这个报错的时候发现了网关服务的两个可疑的地方,第一个疑点是我们在Kong上配置的 Retrie ...
- Docker系列(12)- 部署Tomcat
#官方的使用:我们之前的启动都是后台,停止容器后,容器还是可以看到#docker run -it --rm,一般用来测试,用完就会删除容器,镜像还在[root@localhost ~]# docker ...
- linux 安装配置 jdk1.8
一.查看Linux系统是否有自带的jdk: 1.输入:java -version 2.输入:rpm -qa | grep java 检测jdk的安装包,(注意:rpm命令符没有时记得下载一个输入:ap ...