个人项目

这个作业属于哪个课程 2020春北航计算机学院软件工程(罗杰 任健)
这个作业的要求在哪里 个人项目作业
我在这个课程的目标是 增强软件开发能力,增强沟通表达能力
这个作业在哪个具体方面帮助我实现目标 完成个人项目,接触软件开发流程
教学班级   006
github地址 https://github.com/Therp-GY/IntersectProject

PSP表格

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

实际耗时远远大于预估时间。1. 对语言不熟练,导致简单代码也花费了一些时间 2.第一次按照规范流程完成项目,在一个个环节上也不够熟练,问题颇多。

解题思路

直线表示方法:ax + by + c = 0

圆表示方式:(x-a)^2 + (y-b)^2 = r^2

直线与直线之间求交点直接联立方程求解,然而圆与直线、圆与圆方程求解实现并不简单,于是关于圆交点的求解用几何方法,只需要引入向量类(实际上就是交点类),进行向量加减法即可完成求解。

圆与直线交点

圆与圆交点

关于精度:我使用double类型,因此需要引入eps精度。

关于重复点:由于时间原因,我使用较为暴力的方法。建立一个交点容器和一个几何对象容器,在每次输入新的几何对象后,求它和几何对象容器中所有几何对象的交点,并将这些交点插入到交点容器中,若容器中已经存在这个交点则不插入。

设计实现过程

my_math.h头文件中定义了三个类,点类、线类、圆类

 点类

class Point {
    double x_;
    double y_;
public:
    Point(double x, double y);
    Point();
    double get_x()const;
    double get_y()const;
    double distance(const Point& point)const;
    bool operator==(const Point& point) const;
    bool operator<(const Point& point) const;  //  重载用于set容器
    Point operator+(const Point& point) const;
    Point operator-(const Point& point) const;
    Point operator*(const double& d) const;
    Point operator/(const double& d) const;
    friend void operator<<(std::ostream &os,  Point &point);
};

typedef Point Vector;   //  点类就是向量类

线类

class Line {
    double a;
    double b;
    double c;
    //    ax + by + c = 0
public:
    Line(const Point& p1, const  Point& p2);
    Line(const double& a_, const double& b_, const double& c_);
    Line(const double& a_, const double& b_, const Point &point);
    Point find_intersection(const Line &line);    //  线和线的交点
    double get_a()const;
    double get_b()const;
    double get_c()const;
    Vector get_directionVector()const;  //  单位向量
    bool operator==(const Line& line); 
    friend void operator<<(std::ostream& os, Line& line);
};

圆类

class Circle {
    Point o;
    double r;
public:
    Circle(const Point& point, const double r_);
    Circle();
    int find_intersection(const Line& line , Point *p);  //  圆和线的交点
    int find_intersection(const Circle& circle, Point* p);  //  圆和圆的交点
    double get_r()const;
    Point get_o()const;

};

 

关键函数比较离散,并不复杂。线线、圆线都是独立求的,圆圆会转化为圆线相交,逻辑比较简单。

单元测试

对线线测试相交、平行、重合(即报错)情况。通过测试

 

对线圆测试相交、相切、相离情况。通过测试

 

对圆圆测试内切、外切、相交、无交点情况。通过测试

 

并使用GeoGebra进行测试多种几何对象相交情况(未写入单元测试)

 

 

 同时每一次版本改变就做回归测试。

性能改进

性能改进上花了约120min,由于对c++一些数据结构并没有清晰的概念,导致在改进上速度很慢。

我最开始选择存储点的容器未map,但是一开始对insert函数的误操作,在insert上又加find函数,导致在查找点时连续进行两次查找,耗时巨大。

在将find去除后,性能探查器得到如下结果。测试数据为5000组。

 

 

 

性能消耗最大的函数即find_intersection,因为要遍历所有已有点并完成插入工作。

void find_intersection_l_l(Line &l,vector<Line> &l_list, set<Point>&p_set) {
    for (unsigned int i = 0; i < l_list.size(); i++) {
        Point p;
        Point nopoint(INFINITY, INFINITY);
        p = l_list[i].find_intersection(l);
        if (p == nopoint)continue;
        p_set.insert(p);
    }
}

 

 

map效果十分不理想,由此我又使用set存储点,只存储唯一键值。

以下是用set进行5000组测试。

 

 

 可以看出速度变快了许多,性能有很大改进。

但由于都是有序容器,因此我想尝试无序容器。因此我采用过unordered_set,因为无序查找速度更快。 但并没有很大的效果改进,甚至时间更长了,或许是我的hash函数并没有很好的分配索引,hash查找比起有序查找在性能上没有显著进步。

代码说明

 

线线求交点,直接联立直线方程求解。                                                                        

Point Line::find_intersection(const Line& line)
{
    if (*this == line) {
        std::cout << "两条线重合, 无数个交点" << std::endl;
        /*abort();*/
    }
    else {
        if (b * line.a - a * line.b == 0) {
            return Point();
        }
        else {
            double x;
            double y;
            y = (a * line.c - c * line.a) / (b * line.a - a * line.b);
            if (a != 0) {
                x = (- c - b * y) / a;
            }
            else {
                x = (-line.c - line.b * y) / line.a;
            }
            return Point(x, y);
        }
    }
}        

 

 

 

圆线求交点,算出圆心到直线的距离,再用勾股定理算出弦长并转化为向量,然后求解点。

 

int Circle::find_intersection(const Line& line, Point* p)
{
    int n;    //    交点个数
    Vector line_vector = line.get_directionVector();    //    line 的单位向量
    Line line_h(-line.get_b(), line.get_a(), o); // 和 line 垂直且过圆心的垂线 line_h
    Point i1 = line_h.find_intersection(line);    // l_h 和 line 的交点 i1
    double d = i1.distance(o);    //    i1 到 圆心 o 的 距离
    double l = sqrt(pow(r, 2) - pow(d, 2));    //  sqrt(r^2 - d^2)
    Vector v = line_vector* l;
    *p = i1 + line_vector * l;
    *(p+1) = i1 - line_vector * l;

    if (d > r) {
        n = 0;
        return n;
    }
    else if (abs(d - r) <= eps) n = 1;
    else n = 2;
    return n;
}

 

 

                                  

圆圆求交点,用几何做法比较麻烦,要分别判断是不相交、外切、内切、相交,然后依靠向量求解。

 

int Circle::find_intersection(const Circle& circle, Point* p)
{
    if (o == circle.o && (r - circle.r) < eps) {
        std::cout << "两圆重合,无数个交点" << std::endl;
        /*abort();*/
        return 0;
    }

    //    交点个数
    int n;

    //    分大小圆
    Circle big;
    Circle small;
    if (r >= circle.r) {
        big = *this;
        small = circle;
    }
    else {
        big = circle;
        small = *this;
    }

    //    判断交点个数
    double d = big.o.distance(small.o);
    if (d > (big.r + small.r) || d < big.r - small.r) {
        n = 0;
        return n;
    }
    else if (abs(d - (big.r + small.r)) <= eps) {    //    小圆外切
        n = 1;
        Vector v1 = small.o - big.o;    //    大圆圆心射向小圆圆心的向量
        v1 = v1 * (big.r / (big.r + small.r));
        *p = big.o + v1;
        return n;
    }
    else if (abs(d - (big.r - small.r)) <= eps) {    //    小圆内切
        n = 1;
        Vector v1 = small.o - big.o;    //    大圆圆心射向小圆圆心的向量
        v1 = v1 / big.o.distance(small.o) * big.r;
        *p = big.o + v1;
        return n;
    }
    else  {    // 2个交点  d ,r1,r2形成三角形 
        n = 2;
        Vector v1 = small.o - big.o;
        v1 = v1 / big.o.distance(small.o);    //    单位向量
        double x = (pow(big.r, 2) - pow(small.r, 2) + pow(d, 2)) / (2 * d);
        v1 = v1 *  x;
        Point cross = big.o + v1;
        Line l(v1.get_x(), v1.get_y(), cross);
        big.find_intersection(l, p);
        return n;
    }
    return 0;
}

 

 

 

是否有运行警告

 

posted @ 2020-03-10 01:17  Therp-GY  阅读(171)  评论(2编辑  收藏  举报