博客三
項目 | 內容 |
---|---|
這個作業屬於哪個課程 | 2020春季計算機學院軟件工程(羅傑 任健) |
這個作業的要求在哪里 | 第一次項目作業 |
我在這個課程的目標 | 不求變強,只求做好,成為一顆有用的螺絲釘。 |
這個作業在哪個具體方面幫助我實現目標 | 使用單元測試以及性能分析工具 |
參考資料 | |
教學班級 | 005 |
項目地址 | 鏈接 |
PSP
PSP2.1 | Personal Software Process Stages | 預估耗時(分鍾) | 實際耗時(分鍾) |
---|---|---|---|
Planning | 計划 | 5 | 5 |
· Estimate | · 估計這個任務需要多少時間 | 5 | 0 |
Development | 開發 | 345 | 690 |
· Analysis | · 需求分析 (包括學習新技術) | 0 | 180 |
· Design Spec | · 生成設計文檔 | 0 | 0 |
· Design Review | · 設計復審 (和同事審核設計文檔) | 0 | 0 |
· Coding Standard | · 代碼規范 (為目前的開發制定合適的規范) | 0 | 0 |
· Design | · 具體設計 | 15 | 60 |
· Coding | · 具體編碼 | 30 | 30 |
· Code Review | · 代碼復審 | 120 | 120 |
· Test | · 測試(自我測試,修改代碼,提交修改) | 180 | 300 |
Reporting | 報告 | 60 | 45 |
· Test Report | · 測試報告 | 25 | 10 |
· Size Measurement | · 計算工作量 | 15 | 15 |
· Postmortem & Process Improvement Plan | · 事后總結, 並提出過程改進計划 | 20 | 20 |
合計 | 450 | 775 |
思路描述
考慮最暴力的方法,兩兩直線求交點,那么時間復雜度是$O(n^2)$,這個時間復雜度肯定是不可以接受的。
那么有更好的辦法嗎?除了如果不存在平行和三線共點,那么交點數應該是$n(n-1)/2$。如果新增的一條線與$x$條直線平行,那么會少$x$個交點;如果新增的一條直線與l另外$x(x>2)$條直線共點,那么會少$x$個交點。欲求得平行條件不難,時間復雜度為$O(1)$;欲求共點條件還是會用上遍歷,這里的時間開銷又回到了$O(n^2)$。
所以,我放棄了,規規矩矩地使用暴力方法,選擇了兩點式求直線交點,再去百度上找輪子。
設計實現過程
大體的思路是先計算,再把計算結果保存在集合中,由於集合不存在重復的元素,所以對於本次作業來說是一個比較好的數據結構,需要重寫比較函數。
相比於求取斜率的截距式,兩點式更加安全,但是計算效率會更低(截距式2個參數,兩點式4個參數)。
首先就是構造Dot類和重寫比較函數。然后構造一個 set<Dots, DotsCmp>
類。
計算交點的函數的介紹放在關鍵代碼部分。
再將這些函數封裝起來。
Dots類:
class Dots {
public:
double DotX;
double DotY;
Dots() {
DotX = 0;
DotY = 0;
}
Dots(double X, double Y) {
DotX = X;
DotY = Y;
}
};
重寫比較函數:
class DotsCmp {
public:
bool operator()(const Dots& dotA, const Dots& dotB) const {
if (dotA.DotX == dotB.DotX) {
return (dotA.DotY < dotB.DotY);
}
else {
return (dotA.DotX < dotB.DotX);
}
}
};
計算結果的類:
class CalDots {
private:
DotsSet _set;//交點集合
public:
CalDots();
Dots CalDot(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3);//計算兩直線交點
int DotsSize();//返回總交點數
};
單元測試1——CalDot()函數的正確性
TEST_METHOD(TestCalDot)
{
CalDots*cald = new CalDots();
int x0 = 0;
int y0 = 0;
int x1 = 1;
int y1 = 1;
int x2 = 0;
int y2 = 0;
int x3 = 0;
int y3 = 0;
double dotx = 0;
double doty = 0;
Assert::AreEqual(dotx, cald->CalDot(x0, y0, x1, y1, x2, y2, x3, y3).DotX);
Assert::AreEqual(doty, cald->CalDot(x0, y0, x1, y1, x2, y2, x3, y3).DotY);
}
單元測試2——時間
TEST_METHOD(TestTime)
{
srand(time(NULL));
long t = clock();
CalDots* cald = new CalDots();
int Dot[5000][4];
int i, j;
for (i = 0; i < 5000; i++) {
Dot[i][0] = rand() % 1000;
Dot[i][1] = rand() % 1000;
Dot[i][2] = rand() % 1000;
Dot[i][3] = rand() % 1000;
}
for (i = 0; i < 5000; i++) {
for (j = i + 1; j < 4999; j++) {
cald->CalDot(Dot[i][0], Dot[i][1], Dot[i][2], Dot[i][3], Dot[j][0], Dot[j][1], Dot[j][2], Dot[j][3]);
}
}
Assert::IsTrue(clock() - t < 60000);
}
實測5000條直線耗時54秒,效率很低,大部分用在set排序上。
時間記錄及改進思路
耗時最大的函數是CalDot()函數(關鍵代碼部分),即計算兩直線交點的函數;而其中耗時最多的是將計算得到的點插入set集合中。這一工作是內置函數完成的,暫時想不到改進策略。可以改善的是簡化計算步驟,將兩點式轉換為截距式,但截距式存在k不存在的情況,不僅會增加程序的錯誤處理,而且對速度提升不大。
關鍵代碼
Dots CalDots::CalDot(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) {
double k1, k2;
if (x0 != x1) {
k1 = (y0 - y1) / (x0 - x1);//if x0 == x1 k1 do not exist
if (x2 != x3) {
k2 = (y2 - y3) / (x2 - x3);
if (k1 != k2) {
DotX = (k1 * x0 - k2 * x2 + y2 - y0) / (k1 - k2);
DotY = y0 + (DotX - x0) * k1;
_set.insert(Dots(DotX, DotY));
return Dots(DotX, DotY);
}
//else paralle
}
else {
DotX = x2;
DotY = y0 + ((double)x2 - (double)x0) * k1;
_set.insert(Dots(DotX, DotY));
return Dots(DotX, DotY);
}
}
else {
if (x2 != x3) {
k2 = (y2 - y3) / (x2 - x3);//k2 exists
DotX = x0;
DotY = y2 + ((double)x0 - (double)x2) * k2;
_set.insert(Dots(DotX, DotY));
return Dots(DotX, DotY);
}
//else paralle
}
return Dots(DotX, DotY);
}