zoukankan      html  css  js  c++  java
  • [2020 BUAA 软件工程]个人项目作业

    [2020 BUAA 软件工程]个人项目作业

    项目 内容
    这个作业属于哪个课程 2020春北航计算机学院软件工程(罗杰 任健)
    这个作业的要求在哪里 个人项目作业
    我在这个课程的目标是 系统学习软件工程相关知识,培养自己的软件开发能力与团队协作能力,接受一定的实战锻炼
    这个作业在哪个具体方面帮助我实现目标 练习个人项目开发

    项目地址

    • 教学班级:006

    • 项目地址:https://github.com/blackbird52/Intersection-Calculation.git

      • 目录结构:
      Intersection-Calculation/
      ├── bin
      │   └── intersect.exe
      ├── README.md
      ├── src
      │   ├── main.cpp
      │   ├── counter.cpp
      │   ├── line.cpp
      │   ├── intersection.h
      │   ├── counter.h
      │   └── line.h
      └── test
          ├── pch.h
          ├── pch.cpp
          └── UnitTest.cpp
      

    解题思路

    • 第 2 行至第 N + 1 行:每行描述一条直线。具体格式如下:
      • 直线:L ,表示通过点 (x1, y1) 和 (x2, y2) 的直线。输入保证给定两点不重合。

    所有直线参数均为整数,范围为 (-100000, 100000)。

    直线表示方法

    根据题目所给定的信息,一条直线由线上两点表示。

    为方便表示所有类型的直线,同时便于判断平行和求交点,采用直线的一般式方程来描述:

    [Ax+By+C=0 ]

    这里,(A, B, C) 三个量的计算方法为:

    • (A=y_2-y_1)
    • (B=x_1-x_2)
    • (C=x_2*y_1-x_1*y_2)

    在这种情况下,两直线平行时:

    [A_1B_2=A_2B_1 ]

    两直线交点坐标:

    [(frac{B_1C_2-B_2C_1}{A_1B_2-A_2B_1}, frac{A_2C_1-A_1C_2}{A_1B_2-A_2B_1}) ]

    交点表示方法

    根据上述的交点坐标公式,可以直接求得坐标的具体值。

    但为了避免浮点数的精度问题,在中间过程中可以不直接求得具体值。

    我一开始选择的方法是将余数分别存储,但在实现中我发现这样其实存在多余存储与计算:

    • 需要存储六个量:
      • (x) 坐标的余数、商、除数
      • (y) 坐标的余数、商、除数
    • 而坐标公式中实际只有三个量:
      • (B_1C_2-B_2C_1)
      • (A_2C_1-A_1C_2)
      • (A_1B_2-A_2B_1)

    所以直接存储这三个量既免除了不必要计算,同时也节省了空间。

    存储方法

    选择使用集合 (set) 存储直线和交点,让集合自动去除重复元素。

    判断方法

    目前没有想到好的判断方法,所使用的就是暴力枚举,每枚举完一个将这条直线从集合中去除。

    复杂度

    因为是暴力枚举,复杂度仅能达到 (O(n^2))


    设计实现过程

    本设计包含一个结构体和两个类。

    交点 struct Intersection

    由于交点仅需存储三个量,操作比较简单,不需要用类实现。

    struct Intersection {
    	int x_numerator;
    	int y_numerator;
    	int denominator;
    };
    

    同时为了使用 (set) 存储交点,需要重载 (<) 运算符。

    bool operator <(const Intersection& s) const {
        return (this->x_numerator * s.denominator < s.x_numerator * this->denominator)
            || (this->x_numerator * s.denominator == s.x_numerator * this->denominator
                && this->y_numerator * s.denominator < s.y_numerator * this->denominator);
    }
    

    这里使用的比较方法是分数的交叉相乘。

    直线 class Line

    直线用一般式表示,需要存储 (A, B, C) 三个量。

    class Line {
    private:
    	int A_;
    	int B_;
    	int C_;
    public:
    	Line(int x1, int y1, int x2, int y2);
    	bool IsParallel(Line* line);
    	struct Intersection* CalculateIntersection(Line* line);
    };
    

    构造函数传入两个坐标,计算出 (A, B, C)

    Line::Line(int x1, int y1, int x2, int y2) {
    	this->A_ = y2 - y1;
    	this->B_ = x1 - x2;
    	this->C_ = x2 * y1 - x1 * y2;
    }
    

    两直线平行也直接使用公式 (A_1B_2=A_2B_1) 判断。

    bool Line::IsParallel(Line* line) {
    	return this->A_ * line->B_ == line->A_ * this->B_;
    }
    

    交点计算同样套用公式 ((frac{B_1C_2-B_2C_1}{A_1B_2-A_2B_1}, frac{A_2C_1-A_1C_2}{A_1B_2-A_2B_1})),存储三个中间量。

    struct Intersection* Line::CalculateIntersection(Line* line) {
    	int x_numerator = this->B_ * line->C_ - line->B_ * this->C_;
    	int y_numerator = line->A_ * this->C_ - this->A_ * line->C_;
    	int denominator = this->A_ * line->B_ - line->A_ * this->B_;
    
    	struct Intersection* intersection = new Intersection(x_numerator, y_numerator, denominator);
    	return intersection;
    }
    

    计数器 class counter

    计数器包含直线集合 (line\_set) 和交点集合 (intersection\_set)

    class Counter {
    private:
    	int line_count_;
    	set<Line*>* line_set_;
    	set<struct Intersection>* intersection_set;
    
    public:
    	Counter(ifstream& input);
    	size_t CalculateIntersection();
    };
    

    将输入文件传入计数器的构造函数,完成直线集合 (line\_set) 的初始化。

    Counter::Counter(ifstream& input) {
    	this->line_set_ = new set<Line*>;
    	this->intersection_set = new set<struct Intersection>;
    	
    	input >> this->line_count_;
    
    	string type;
    	int x1, y1, x2, y2;
    	for (int i = 0; i < this->line_count_; i++) {
    		input >> type ;
    
    		if (type == "L") {
    			input >> x1 >> y1 >> x2 >> y2;
    			Line* line = new Line(x1, y1, x2, y2);
    			this->line_set_->insert(line);
    		} else if (type == "O") {
    			// TO DO
    		}
    	}
    }
    

    使用 (Counter->CalculateIntersection()),在去除平行线后计算交点并存入交点集合 (intersection\_set),自动去重,最后集合大小即为交点个数。

    size_t Counter::CalculateIntersection() {
    	set<Line*>::iterator iter_1 = this->line_set_->begin();
    
    	while (iter_1!= this->line_set_->end()) {
    
    		set<Line*>::iterator iter_2 = this->line_set_->begin();
    		while (iter_2 != this->line_set_->end()) {
    			if (!(*iter_1)->IsParallel(*iter_2)) {
    				this->intersection_set->insert(*(*iter_1)->CalculateIntersection(*iter_2));
    			}
    			iter_2++;
    		}
    
    		iter_1 = this->line_set_->erase(iter_1);
    	}
    
    	return intersection_set->size();
    }
    

    改进程序性能

    这是在数据量为 (5,000) 时的性能分析图,可以看出,程序中消耗最大的函数是 Counter::CalculateIntersection()

    size_t Counter::CalculateIntersection() {
        set<Line*>::iterator iter_1 = this->line_set_->begin();
    
        while (iter_1!= this->line_set_->end()) {
    
            set<Line*>::iterator iter_2 = this->line_set_->begin();
            while (iter_2 != this->line_set_->end()) {
                if (!(*iter_1)->IsParallel(*iter_2)) {
                    this->intersection_set->insert(*(*iter_1)->CalculateIntersection(*iter_2));
                }
                iter_2++;
            }
    
            iter_1 = this->line_set_->erase(iter_1);
        }
    
        return intersection_set->size();
    }
    

    该函数由于包含 (O(n^2)) 的循环求交点和 (set) 更新操作,无法简化。

    我曾尝试将 (set) 更换为 (unordered\_set, hasp\_map),但是并没有明显改善。


    测试

    消除 Code Quality Analysis 中的所有警告

    分支覆盖率


    在覆盖率达到 99% 后,分析代码我发现未覆盖的语句是 (struct Intersection) 中对 (<) 的重载中,若两个 (Intersection) 相等,返回 (fsalse) 这一判断。

    但是我比较奇怪的是为什么未覆盖语句是这个 (else),在我发现已经有相等样例后也无法覆盖,于是我尝试把 (else) 删除,发现居然就 100% 了,我并没有理解这一机制。

    单元测试

    分别测试交点计算、平行判断、交点计数。

    Line::IsParallel(Line* line)

    TEST_METHOD(Line_IsParallel_1) {
        Line* line1 = new Line(1, 1, 2, 2);
        Line* line2 = new Line(3, 3, 4, 4);
        Assert::IsTrue(line1->IsParallel(line2));
    }
    
    TEST_METHOD(Line_IsParallel_2) {
        Line* line1 = new Line(1, 2, 3, 4);
        Line* line2 = new Line(2, 3, 8, 5);
        Assert::IsFalse(line1->IsParallel(line2));
    }
    

    Line::CalculateIntersection(Line* line)

    TEST_METHOD(Line_CalculateIntersection) {
        Line* line1 = new Line(0, 0, 3, 3);
        Line* line2 = new Line(0, 2, 2, 0);
        Intersection* i = line1->CalculateIntersection(line2);
        Assert::AreEqual(1.0, (double)i->x_numerator / (double)i->denominator);
        Assert::AreEqual(1.0, (double)i->y_numerator / (double)i->denominator);
    }
    

    Counter::CalculateIntersection()

    TEST_METHOD(Counter_CalculateIntersection_1) {
        ifstream input;
        input.open("test1.txt");
        Counter* counter = new Counter(input);
        input.close();
        Assert::AreEqual(0, (int)counter->CalculateIntersection());
    }
    
    TEST_METHOD(Counter_CalculateIntersection_2) {
        ifstream input;
        input.open("test2.txt");
        Counter* counter = new Counter(input);
        input.close();
        Assert::AreEqual(3, (int)counter->CalculateIntersection());
    }
    
    TEST_METHOD(Counter_CalculateIntersection_3) {
        ifstream input;
        input.open("test3.txt");
        Counter* counter = new Counter(input);
        input.close();
        Assert::AreEqual(1, (int)counter->CalculateIntersection());
    }
    
    TEST_METHOD(Counter_CalculateIntersection_4) {
        ifstream input;
        input.open("test4.txt");
        Counter* counter = new Counter(input);
        input.close();
        Assert::AreEqual(23, (int)counter->CalculateIntersection());
    }
    

    PSP 2.1

    PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
    Planning 计划
    · Estimate · 估计这个任务需要多少时间 10 10
    Development 开发
    · Analysis · 需求分析 (包括学习新技术) 30 60
    · Design Spec · 生成设计文档 30 30
    · Design Review · 设计复审 (和同事审核设计文档) 0 0
    · Coding Standard · 代码规范 (为目前的开发制定合适的规范) 10 10
    · Design · 具体设计 30 60
    · Coding · 具体编码 60 90
    · Code Review · 代码复审 60 60
    · Test · 测试(自我测试,修改代码,提交修改) 120 120
    Reporting 报告
    · Test Report · 测试报告 20 20
    · Size Measurement · 计算工作量 10 10
    · Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 30 40
    合计 400 510
  • 相关阅读:
    身体很累(0,25)
    近期安排
    石子合并问题圆形版 HRBUST 1819区间dp+环形+四边形优化
    Common Subsequence HDU 1159 最长公共子序列Longest Common Subsequence,LCS
    二维树状数组 1:单点修改,区间查询 LibreOJ 133
    c++位运算
    石子合并问题直线版 HRBUST 1818 简单区间动规
    最少拦截系统 HDU 1257 LIS最长递增子序列
    Longest Common Subsequence Again HDU 2253 LCS+位压缩
    石子合并 HYSBZ 3229 区间dp,最优二叉树问题,加西亚瓦克斯算法(GarsiaWachs)
  • 原文地址:https://www.cnblogs.com/tuoniao/p/12446277.html
Copyright © 2011-2022 走看看