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 软件工程 个人项目作业的更多相关文章

  1. BUAA 2020 软件工程 结对项目作业

    Author: 17373051 郭骏 3.28添加:4.计算模块接口的设计与实现过程部分,PairCore实现的细节 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) ...

  2. BUAA软件工程个人项目作业

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

  3. BUAA 2020 软件工程 个人博客作业

    BUAA 2020 软件工程 个人博客作业 Author: 17373051 郭骏 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 个人博客作业 ...

  4. BUAA 2020 软件工程 热身作业

    BUAA 2020 软件工程 热身作业 Author: 17373051 郭骏 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 第一次作业-热身! ...

  5. 【BUAA 软工个人项目作业】玩转平面几何

    BUAA 软件工程个人项目作业 项目 内容 课程:2020春季软件工程课程博客作业(罗杰,任健) 博客园班级链接 作业:BUAA软件工程个人项目作业 作业要求 课程目标 学习大规模软件开发的技巧与方法 ...

  6. BUAA 2020 软件工程 提问回顾与个人总结

    BUAA 2020 软件工程 提问回顾与个人总结 Author: 17373051 郭骏 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 提问回顾 ...

  7. BUAA软件工程结对项目作业

    BUAA软件工程结对项目 小组成员:16005001,17373192 1.教学班级和项目地址 项目 内容 这个作业属于哪个课程 博客园班级连接 这个作业的要求在哪里 结对项目作业 我在这个课程的目标 ...

  8. BUAA 2020 软件工程 软件分析案例作业

    Author: 17373051 郭骏 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 个人博客作业-软件分析案例 我在这个课程的目标是 学习软件 ...

  9. BUAA 软工 结对项目作业

    1.相关信息 Q A 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 结对项目作业 我在这个课程的目标是 系统地学习软件工程开发知识,掌握相关流程和技术,提升 ...

随机推荐

  1. 性能测试必备命令(2)- uptime

    性能测试必备的 Linux 命令系列,可以看下面链接的文章哦 https://www.cnblogs.com/poloyy/category/1819490.html 介绍 系统启动up了(运行了)多 ...

  2. Docker 面试宝典

    Docker 是什么? 是实现容器技术的一种工具 是一个开源的应用容器引擎 使用 C/S 架构模式,通过远程API 来管理 可以打包一个应用及依赖包到一个轻量级.可移植的容器中 容器是什么? 对应用软 ...

  3. 使用shell脚本实现everthing的功能

    我们知道,在 Windows 下,有一款非常实用的神器,叫作 Everything ,它可以在极短的时间里,搜索出来你所想要的文件/目录,如下图示: Linux 下也有一些类似于 everything ...

  4. es6语法中promise的使用方法

    Promise是一个构造函数,它有resolve,reject,race等静态方法;它的原型(prototype)上有then,catch方法,因此只要作为Promise的实例,都可以共享并调用Pro ...

  5. 升级到windows10之后的骚操作,安装debian,centos7,支持linux、docker、kubectl命令

    修改Windows10默认字体和图标很大 打开Hyper-V Windows10下载Docker Desktop https://www.docker.com/products/docker-desk ...

  6. PHP中的日期相关函数(三)

    之前我们已经介绍过了 PHP 的一些相关的日期操作对象,今天我们就来学习剩下的那些面向过程的使用方式.当然,如果是和 DateTime 类中相似的方法我们就不再进行介绍了.另外,Date() 和 ti ...

  7. php curl发送数据和文件

    function mycurl($file, $url, $aid) { // 如果文件名是中文名,将中文字符编码转换一下 $file=iconv("UTF-8","gb ...

  8. CI框架页面打开空白,无报错为解决方法新环境

    1.打开错误显示,可以在控制controllers的首页入口加入以下代码,查看错误 error_reporting(-1); ini_set('display_errors', 1); //插入显示所 ...

  9. 华为云计算IE面试笔记-桌面云用户登录连接流程及故障处理?

    1-10:桌面与系统验证成功                           http协议 11-19:桌面list(VM列表)获取,选择                http协议 20-30: ...

  10. MySQL修改root密码的多种方法, mysql 导出数据库(包含视图)

    方法1: 用SET PASSWORD命令 mysql -u root mysql> SET PASSWORD FOR 'root'@'localhost' = PASSWORD('newpass ...