CSDCounter

2020软工个人项目作业

北航软工个人项目作业

项目 内容
这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健)
这个作业的要求在哪里 个人项目作业
我在这个课程的目标是 学习软件工程相关知识,提高自己团队项目的开发能力
教学班级 005
项目地址 IntersectProject

PSP表格

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

解题思路描述

总体思路

本次作业重点在于求解交点个数。通过查阅网上资料,若题目给定任意三条直线不能相交于一点,则可使用动态规划求解。但我们的题目并没有这一条件,那么为了去除所有的重复点,我们就不可避免的需要求出所有交点。最简单的想法是暴力求解,对于输入的集合对象,两两求出交点后用set存储所有交点,最后set中的元素个数即为所有交点数量。对这一想法,可以做出简单的改进,对于一个新加入的几何对象,只需要求出它和之前已经加入进来的几何对象的交点,再把这些交点放入set,相比于最暴力的两两求解,可以减少一半的运算量,本次作业采用这种思路。

  • 关于精度问题,由两直线交点坐标公式可知,一个点其实可以用三个整数来表示,这样就不需要担心double带来的精度损失,但是引入圆之后,圆与直线的交点坐标并不满足这种形式,故最终还是采用double进行点的表示。

  • 关于可能的改进,事实上,我们每次进行一次交点计算都可以得到一些有用的信息,若充分利用这些信息可以一定程度简化计算。我们知道,若两直线平行,则他们不可能有交点,故可以设计一个以斜率为key的map存储直线,对于新来的斜率为k的直线,无需与map中key为k的直线比较,可以节省一点时间。进一步的,若多条直线交于一点,且它们的交点在新来的直线上,那么新直线也无需与这些直线进行交点计算,因为它们已经不可能产生新的交点。遗憾的是,由于时间原因,这次我并未完成这些优化。

下面讨论各种几何对象的交点求解方式:

直线与直线:直接根据直线交点坐标公式求解,需要注意的是对于平行以及斜率不存在情况的判定

直线与圆:直接联立方程组求解坐标形式未免过于复杂,查阅资料后发现可以借助几何的方法求解

圆与圆:将圆方程化为一般式后,两方程相减即得过两圆交点的直线的方程,从而问题转化为了直线与圆的交点。在求圆与圆的交点之前,需先判断两圆位置关系,可根据以下规则判断:

  • d>R+r:两圆外离;两圆的圆心距离之和大于两圆的半径之和

  • d=R+r:两圆外切;两圆的圆心距离之和等于两圆的半径之和

  • d=R-r:两圆内切;两圆的圆心距离之和等于两圆的半径之差

  • d<R-r:两圆内含;两圆的圆心距离之和小于两圆的半径之差

  • d<R+r:两园相交;两圆的圆心距离之和小于两圆的半径之和

设计实现过程

总体思路: 程序执行思路为:新读入一个几何对象,判断其类型,之后分别与现有的直线与圆判断位置关系并进行交点运算,将新得出的交点至于set中,结束后将该几何对象也置于线集合或圆集合中。本次涉及的几何对象为直线与圆,故设计两个类分别进行表示。

  • 存储结构:
vector<Line> linevec;
vector<Circle> circlevec;
set<Point> pointset;
  • line与circle类
class Line {
public:
	// use Ax+By+C=0 to describe a line
	double A, B, C;
	Line(double x1, double y1, double x2, double y2);
	Line(double a, double b, double c);
	Point calintpoint1(Line line1);
};

class Circle {
public:
	double x0, y0, r0;//describe a circle
	Circle(double x0, double y0, double r0);
};
  • main函数
	int num = 0;
	while (num < argc) {
		if ((string)argv[num] == "-i") {
			infile.open(argv[num + 1]);	//accept input
		}
		else if ((string)argv[num] == "-o") {
			outfile.open(argv[num + 1]);
		}
		num++;
	}
	int n = 0;
	infile >> n;
	double x1, y1, x2, y2;
	double x0, y0, r0;
	string op;
	int i = 0;
	for (i = 0; i < n; i++) {
		infile >> op;
		if (op == "L") {
			infile >> x1 >> y1 >> x2 >> y2;
			Line line1(x1, y1, x2, y2);
			intersectforline(line1);
		}
		if (op == "C") {
			infile >> x0 >> y0 >> r0;
			Circle circle1(x0, y0, r0);
			intersectforcircle(circle1);
		}
	}

单元测试:

因为本次作业的重点在于求直线交点,故主要对求解两直线交点的方法进行测试,结果均符合预期。如下代码是一个测试样例。

TEST_METHOD(TestMethod10)
		{
			Line line1(0, 1, 1, 5);
			Line line2(0, 3, 1, 10);
			pair<double, double> point;
			point.first = -(double)(2)/3;
			point.second = -(double)(5)/3;
			Assert::AreEqual(line1.calintpoint1(line2).x, point.first);
			Assert::AreEqual(line1.calintpoint1(line2).y, point.second);
		}

性能分析

起初,我的直线和点都是通过set进行存储,从图中可以看出,用set容器存储结点时,因为set内部的有序性,导致插入结点时维护红黑树所要付出的代价太大。为了适当减少这部分影响,我选择改用vector容器来完成对于直线和圆的存储,但由于交点的不可重复性,用vector需要去重,当数据量很大时vector应该也要付出相当的代价,故对于交点我依然使用set进行存储。

可以看到,插入交点依然是我程序目前性能的主要瓶颈,但由于设计原因,我目前还没有找到优化的办法。

关键代码说明

  • 求两直线的交点

Point Line::calintpoint1(Line line1) {//line and line
	double tmp1 = B * line1.C - line1.B * C;
	double tmp2 = A * line1.B - line1.A * B;
	double tmp3 = line1.A * C - A * line1.C;
	double x = tmp1 / tmp2;
	double y = tmp3 / tmp2;//直线交点坐标公式
	Point point(x, y);
	return point;
}

  • 求直线与圆的交点
void calintpoint2(Line line, Circle circle, double dis) {//line and circle
	Line line2(line.B, -line.A, line.A * circle.y0 - line.B * circle.x0);//过圆心的垂线
	Point point = line.calintpoint1(line2);//垂足
	pair<double, double> e;//定义直线的单位向量
	double gougu = sqrt(circle.r0 * circle.r0 - dis * dis);
	e.first = (double)line.B / sqrt(line.A * line.A + line.B * line.B);
	e.second = -(double)line.A / sqrt(line.A * line.A + line.B * line.B);//求直线的单位向量
	Point point1(point.x + e.first * gougu, point.y + e.second * gougu);
	pointset.insert(point1);

	if (dis == circle.r0)//若相切则只有一个交点
		return;

	Point point2(point.x - e.first * gougu, point.y - e.second * gougu);
	pointset.insert(point2);
}
  • 直线与现有几何对象求交点过程
void intersectforline(Line line1) {
	//line and line
	vector<Line>::iterator iter1;
	for (iter1 = linevec.begin(); iter1 != linevec.end(); ++iter1)
	{
		if (line1.A * (*iter1).B - (*iter1).A * line1.B == 0) { //parallel
			;
		}
		else {
			Point point = line1.calintpoint1(*iter1);
			pointset.insert(point);
		}
	}
	//line and circle
	vector<Circle>::iterator iter2;
	for (iter2 = circlevec.begin(); iter2 != circlevec.end(); ++iter2)
	{
		double dis = getdistance(line1, *iter2);
		if (dis > (*iter2).r0) {//直线与圆相离
			continue;
		}
		else {
			calintpoint2(line1, (*iter2), dis);
		}
	}
	linevec.push_back(line1);
}
  • 圆与现有几何对象求交点过程
void intersectforcircle(Circle circle1) {
	//line and circle
	vector<Line>::iterator iter1;
	for (iter1 = linevec.begin(); iter1 != linevec.end(); ++iter1)
	{
		double dis = getdistance(*iter1, circle1);
		if (dis > circle1.r0) {//直线与圆相离
			continue;
		}
		else {
			calintpoint2(*iter1, circle1, dis);
		}
	}
	//circle and circle
	vector<Circle>::iterator iter2;
	for (iter2 = circlevec.begin(); iter2 != circlevec.end(); ++iter2)
	{
		double d = sqrt((circle1.x0 - (*iter2).x0) * (circle1.x0 - (*iter2).x0) + (circle1.y0 - (*iter2).y0) * (circle1.y0 - (*iter2).y0));
		if (d == 0 || d < fabs(circle1.r0 - (*iter2).r0) || d >(circle1.r0 + (*iter2).r0)) {
			continue;
		}//判断两圆是否有交点

		//求出过两圆交点的直线
		Line line1(2 * ((*iter2).x0 - circle1.x0), 2 * ((*iter2).y0 - circle1.y0), circle1.x0 * circle1.x0 + circle1.y0 * circle1.y0 - 
			(*iter2).x0 * (*iter2).x0 - (*iter2).y0 * (*iter2).y0 + (*iter2).r0 * (*iter2).r0 - circle1.r0 * circle1.r0);
		double dis = getdistance(line1, circle1);
		calintpoint2(line1, circle1, dis);//求出两圆交点
	}
	circlevec.push_back(circle1);
}

相关截图

  • 无警告

posted on 2020-03-10 14:23  CSDCounter  阅读(159)  评论(2编辑  收藏  举报

导航