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. Docker(42)- 镜像原理之联合文件系统

    前言 学习狂神老师的 Docker 系列课程,并总结 镜像是什么 镜像是一种轻量级.可执行的独立软件保,用来打包软件运行环境和基于运行环境开发的软件 他包含运行某个软件所需的所有内容,包括代码.运行时 ...

  2. Intel® QAT加速卡之性能简介

    Intel QuickAssist Adapter 8950 设备简介 支持英特尔QuickAssist技术的英特尔QuickAssist适配器提供加密加速和压缩加速服务. 1. Key featur ...

  3. ClientValidationFunction

    CustomValidator.ClientValidationFunction 属性 获取或设置用于验证的自定义客户端脚本函数的名称. 命名空间:   System.Web.UI.WebContro ...

  4. Vue Abp vNext用户登录(Cookie)

    因为Abp vNext没找到Vue的模板,网上也没找到相关vNext的例子,只能自己试着写写,asp.net core abp vue都是刚学不久,所以很粗糙也可能有错误的地方,如果您看到请指正,谢谢 ...

  5. 修改Typora的代码以支持文件夹和文件混合排序

    用Markdown文件写笔记,用文件夹做分类,整个笔记文档项目构成了一个树形结构.笔记文章之间.文章与分类之间经常有特定的先后顺序,于是就在文件名前面加上数字前缀来控制排序.但是,Windows的文件 ...

  6. JVM-调优-命令

    目录 jps 命令格式 option参数 示例 jstat 命令格式 参数 option 参数总览 option 参数详解 -class -compiler -gc -gccapacity -gcut ...

  7. Docker安装mysql镜像并进行主从配置

    Docker安装mysql镜像并进行主从配置 1.下载需要的mysql版本镜像 docker pull mysql:5.6 2.启动mysql服务实例(基本启动) #启动主mysql docker r ...

  8. 【转载】linux 工作队列上睡眠的认识--不要在默认共享队列上睡眠

    最近项目组做xen底层,我已经被完爆无数遍了,关键在于对内核.驱动这块不熟悉,导致分析xen代码非常吃力.于是准备细细的将 几本 linux 书籍慢慢啃啃. 正好看到LINUX内核设计与实现,对于内核 ...

  9. 自己实现一个Controller——精简型

    写在最前 controller-manager作为K8S master的其中一个组件,负责众多controller的启动和终止,这些controller负责监控着k8s中各种资源,执行调谐,使他们的实 ...

  10. Python项目生成requirements.txt文件之pipreqs的使用

    生成requirements.txt时使用pip freeze > requirements.txt会将环境下所有的安装包都进行生成,再进行安装的时候会全部安装很多没有的包.耗时耗力其实是不可取 ...