• 软件工程


    软件工程 - 个人项目作业

    项目 内容
    这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健)
    这个作业的要求在哪里 个人项目作业
    我在这个课程的目标是 学习软件工程的开发知识,培养工程化开发能力
    这个作业在哪个具体方面帮助我实现目标 通过实操掌握PSP开发基础

    1 概览

    教学班级:006

    项目地址https://github.com/sinoyou/Software_Intersection


    2 PSP2.1 分析

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

    3 解题思路

    题目描述

    • 题目需求为,给定若干直线(以两个点确定一条直线的方式给出,点的坐标必须为整数),求其交点的数量。
    • 考虑性能情况下:直线条数0 <= N <= 500000
    • 交点个数0 <= h <= 5000000
    • 运行时长60s

    数学模型

    两个不同的点决定一条直线,给定点((x_1, y_1))((x_2, y_2)),可以得出一条直线的形式为(ax + by + c = 0)(其中,a和b不能够同时为0):

    • (a = y_1 - y_2)
    • (b = x_2 - x_1)
    • (c = x_1y_2 - x_2y_1)

    对于两条直线(a_1x+b_1y+c_1=0)(a_2x+b_2y+c_2=0),根据运算,可以得到交点的表达式为:

    [x= frac{b_1 imes c_2 - b_2 imes c_1}{a_1 imes b_2 - a_2 imes b_1} ]

    [y= frac{a_2 imes c_1 - a_1 imes c_2}{a_1 imes b_2 - a_2 imes b_1} ]

    程序实现

    计算机以离散数据形式处理内容,虽然给定的点坐标为整数,但其交点不一定为整数。因此,本问题中需要考虑小数问题,在分析了数学模型的公式形式后,权衡C++库中的默认double、float类尚存在一定的精度误差,因此我决定设计自定义的分数类Rational用于数据的记录,并自定义分数支持的四则运算符和比较操作,以支持该问题所需的操作。


    4 模型设计

    基于面向对象的思想,主要设计了有理数、点和线三个和平面几何有关的类,他们之间的联系见下图:


    5 代码分析与性能分析

    首先,使用Visual Studio自带的代码分析工具对所编写的代码进行了检查,得到没有issue发现的状态如下图所示:

    而后,使用了VS的性能分析工具,将所参与的直线数量调整至最大输入量N=500000,进行性能分析运行,运行60秒后中断分析结果,定位到目前的消耗最大的函数是Line 类的静态方法get_intersection(),再具体来说,是在生成交点时,自定义的Rational分数类之间的四则运算操作开销很大,具体来说有一下两个方面:

    • 其一:是new operation的频次过多,造成过大的性能开销。关于这方面我在短时间内还未找到妥善处理的方法。
    • 第二:是标准空间中所定义的abs, max, min函数性能开销很大,在这些函数进行人工inline重写后,函数的性能开销明显下降,如下图所示。
    • 第三:GCD(求最大公因数)用于化简分数,也是一个性能开销大的函数,在改写其递归结构变为while循环类型后,性能开销存在些许下降,但并不明显。


    6 代码说明

    代码结构

    对于实现的代码,可以按以下文件进行组织:

    ├─header
    │      AlgorithmRunner.h
    │      Line.h
    │      Point.h
    │      Rational.h
    │      utils.h
    │      
    └─source
        │  AlgorithmRunner.cpp
        │  Intersect.cpp
        │  utils.cpp
        └─geometry
                Line.cpp
                Point.cpp
                Rational.cpp
    

    项目基于面向对象的方式编写,在.h头文件中定义类的成员与方法,在每个.cpp文件中定义了类方法的具体实现。

    下面我以编写的有理数(分数)和二维坐标点为例子,进行具体的代码说明:

    • 成员与构造:有理数类中有两个成员,用于分别表示(frac{分子}{分母})的两个数字,两个数字属于不可更改的数据,在进行类初始化时即使用GCD(最大公因数)进行化简并保存。
    • 运算:有理数类支持了加减乘除四种四则运算的方法,以满足在求解直线交点时所需要的所有运算。
    • 重载方法:为了支持有理数能够使用STL容器,在此重载了部分操作符和hash函数,对于std::set类,需要重载小于操作符<,对于std::unordered_set类,需要重载hash函数和==操作符。
    // 有理数类的实现
    class Rational
    {
    private:
    	int64_t numerator;
    	int64_t dominator;
    public:
    	Rational(int64_t value);
    	Rational(int64_t num, int64_t dom);
    
    	static Rational* add(Rational* a, Rational* b);
    	static Rational* sub(Rational* a, Rational* b);
    	static Rational* multiply(Rational* a, Rational* b);
    	static Rational* div(Rational* divee, Rational* diver);
    
    	bool operator==(const Rational& r);
    	bool operator<(const Rational& r);
    	size_t hash();
    };
    

    对于二维坐标点类,其用于表示用于确定直线的点和记录交点:

    • 成员与构造函数:包括两个有理数用于表示x和y坐标。为了后续的扩展性,增加了线条的列表,表示该点存在于的线条对象中。
    • 方法:Point类中所定义的主要方法是用于获取其内部成员变量的方法和其与Line类关系的维护。
    • 重载方法:重载了==和hash函数,以支持部分STL容器。
    class Point
    {
    private:
    	Rational* x;
    	Rational* y;
    	std::list<Line*> on_lines;
    
    public:
    	Point(Rational* x, Rational* y);
    	Point(int64_t x, int64_t y);
        
    	void register_line(Line* l);
    	std::list<Line*>* get_lines();
    	Rational* get_x() const;
    	Rational* get_y() const;
    
    	bool operator==(const Point& p);
    	size_t hash();
    };
    

    代码单元测试

    由于类方法封装得比较好,因此单元测试比较方便,以每个类为具体如下:

    (补充)测试策略:

    方法种类 测试逻辑
    Rational(有理数) 生成方法 分数化简(分子分母分别为负零正)
    比较方法(==,<,hash) @负零正三类数字中大小比较;未化简分数比较;@hash方法的正确性;hash碰撞;@指针和对象比较区分。
    运算方法 负零正数之间涉及四则运算;运算的交换律;零至少作为一个操作数;零作为结果。
    Point(二维点) 比较方法(<, ==) 设计跨越四个象限的正方形四个点,自身、两两比较。
    查询方法 查询正常情况;查询空值情况。
    Line(直线) 生成方法 投入相同坐标点的异常;两点排序正常检验。
    交点求解 功能验证(相交、平行);变量边界溢出运算检验。

    关于性能和挑战性任务的一些思考(todo)


  • 相关阅读:
    NSWindow,一些有的沒的
    IT单身男士必看【找女友单身程序员】
    Base 64 Encoding 编码
    如何成为一名优秀的C程序员
    iOS学习笔记—ObjectiveC 委托、非正式协议、正式协议
    程序员学习能力提升三要素
    工程师如何不被PM欺负
    最常被程序员们谎称读过的计算机书籍
    一些重要的算法
    asp调用.Net 托管代码
  • 原文地址:https://www.cnblogs.com/sinoyou/p/12457883.html
Copyright © 2020-2023  润新知