zoukankan      html  css  js  c++  java
  • 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^{1leq i,jleq k}_{i ot=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>();
    }
    
  • 相关阅读:
    电脑提示无法装入/加载SolidWorks DLL文件:sldshellutils如何解决
    vmware vpshere 安装完的必备工作
    建立AD域,修改密码后不重启生效命令
    VMware vSphere 6 Enterprise Plus 注册码
    VMware-viclient-all
    域控中将组策略应用到安全组
    Windows server 2003域控迁移到2012
    SecureCRT 基本设置
    linux之"server" directive is not allowed here in
    wordpress(二)wordpress环境迁移
  • 原文地址:https://www.cnblogs.com/sharinka0715/p/12452065.html
Copyright © 2011-2022 走看看