這篇博客是軟件工程基礎(羅傑、任建)的第三次課程做業(我的項目做業)git
項目 | 內容 |
---|---|
這個做業屬於哪一個課程 | 軟件工程基礎(羅傑,任建) |
這個做業的要求在哪裏 | 做業要求的連接 |
我在這個課程的目標是 | 提高對軟件工程的宏觀和微觀的全面認識,並加以實踐 |
做業在哪些方面幫我實現目標 | 親身實踐我的項目開發的完整流程 |
個人教學班級 | 006 |
個人GitHub項目地址 | https://github.com/SnowOnVolcano/IntersectProject.git |
在開始實現程序以前,在下述 PSP 表格記錄下你估計將在程序的各個模塊的開發上耗費的時間。(0.5')github
在你實現完程序以後,在下述 PSP 表格記錄下你在程序的各個模塊上實際花費的時間。(0.5')算法
PSP2.1 | Personal Software Process Stages | 預估耗時(分鐘) | 實際耗時(分鐘) |
---|---|---|---|
Planning | 計劃 | 15 | 20 |
· Estimate | · 估計這個任務須要多少時間 | 15 | 20 |
Development | 開發 | 330 | 470 |
· Analysis | · 需求分析 (包括學習新技術) | 60 | 80 |
· Design Spec | · 生成設計文檔 | 30 | 20 |
· Design Review | · 設計複審 (和同事審覈設計文檔) | 0 | 0 |
· Coding Standard | · 代碼規範 (爲目前的開發制定合適的規範) | 10 | 10 |
· Design | · 具體設計 | 40 | 60 |
· Coding | · 具體編碼 | 100 | 120 |
· Test | · 測試(自我測試,修改代碼,提交修改 | 90 | 180 |
Reporting | 報告 | 75 | 110 |
· Test Report | · 測試報告 | 45 | 60 |
· Size Measurement | · 計算工做量 | 10 | 10 |
· Postmortem & Process Improvement Plan | · 過後總結, 並提出過程改進計劃 | 20 | 40 |
Total | 合計 | 420 | 600 |
解題思路描述。即剛開始拿到題目後,如何思考,如何找資料的過程。(3')函數
讀完問題的第一思路,是利用高中學到的 通常方程法 進行交點的求解:工具
這樣的解法有幾個好處:性能
D
是否爲 0,直接判斷兩直線是否平行;set
用來存放已獲得的交點,直接在存入時進行除重,最終結果輸出 set
的大小便可;固然,其缺點也很明顯:單元測試
N
條直線的樣本,須要進行 \(C_N^2\) 次計算,時間複雜度爲 \(O(n^2)\);set<(x,y)>
進行的方式進行存儲,則須要進行浮點運算,這既會帶來精度的損失,也會加長運行時間。若是不採用直接計算的方法,如何得出交點個數呢?我想到的另外一個方法是,作減法。學習
若是任意的兩條直線都相交且任意的三條直線不交於同一點,那麼對於具備 N
條直線的樣本,交點的個數爲 \(C_N^2\),對通常狀況就有,測試
可是,仔細想一想,這種方法彷佛並不比思路簡單,由於我沒有想到好的算法去計算同一交點的直線數目……優化
思路三
想不到好的算法,我最後只能決定使用直接計算的方法,可是我想其實思路一的方法還能夠簡單地優化一下細節,減少精度損失,有兩個方法,
set
的排序函數進行重載,設置必定的精度範圍,這樣能夠必定程度地減少精度損失,可是沒法從根本上避免。我最終選擇了後者,以平衡精度和時間複雜度。
設計實現過程。設計包括代碼如何組織,好比會有幾個類,幾個函數,他們之間關係如何,關鍵函數是否須要畫出流程圖?單元測試是怎麼設計的?(4')
Point
:表示點 \((x,y)\),其中 Point.x
、Point.y
分別表示點的縱橫座標;Line
:表示直線 \(ax+by+c=0\),其中 Line.a
、Line.b
、Line.c
分別對應直線的三個參數;Point
和 Line
的構造函數,計算兩直線交點的函數 calLineLineIst(...)
,主函數;
各函數關係以下,
我主要進行了三個方面的單元測試:
如下是個人一些單元測試的測試點的圖形示意:
記錄在改進程序性能上所花費的時間,描述你改進的思路,並展現一張性能分析圖(由 VS 2019 的性能分析工具自動生成),並展現你程序中消耗最大的函數。(3')
代碼說明。展現出項目關鍵代碼,並解釋思路與註釋說明。(3')
// calculate the intersections of two lines static void calLineLineIst(Line& line1, Line& line2) { int D; D = line1.a * line2.b - line2.a * line1.b; switch (D) { case 0: // parallel break; default: // line1: a1*x+b1*x+c1=0, line2: a2*x+b2*x+c2=0 // ==> x=(b1*c2-b2*c1)/(a1*b2-a2*b1), // y=(a2*c1-a1*c2)/(a1*b2-a2*b1) // let D=a1*b2-a2*b1 // ==> x=(b1*c2-b2*c1)/D, y=(a2*c1-a1*c2)/D Point point = { (line1.b * line2.c - line2.b * line1.c) / (float)D, (line2.a * line1.c - line1.a * line2.c) / (float)D }; points.insert(point); break; } }
集合的排序和精度的肯定
bool operator == (const Point& other) const { return fabs(x - other.x) < 0.00000001 && fabs(y - other.y) < 0.00000001; } bool operator < (const Point& other) const { if (x != other.x) { return x < other.x; } else { return y < other.y; } }
// calculate the intersections of line and Circle static void calLineCircleIst(Line& line, Circle& circle) { int intercept; // intercept=r^2-d^2=r^2-(ax+by+c)^2/(a^2+b^2) intercept = (int)(pow(circle.r, 2) - pow(line.a * circle.x + line.b * circle.y + line.c, 2) / (pow(line.a, 2) + pow(line.b, 2))); // not intersect if (intercept < 0) { return; } // tLine is perpendicular to line Line tLine = { line.b, -line.a, line.a * circle.y - line.b * circle.x }; int D; D = tLine.a * line.b - line.a * tLine.b; // tPoint is the intersection of line and tLine Point tPoint = { (tLine.b * line.c - line.b * tLine.c) / (float)D, (line.a * tLine.c - tLine.a* + line.c) / (float)D }; switch (intercept) { case 0: // line is tangent to circle points.insert(tPoint); break; default:// line passes through circle float vecX; float vecY; float offset; // (vecX, vecY) is a unit vector vecX = (float)(line.b / sqrt(pow(line.a, 2) + pow(line.b, 2))); vecY = (float)(-line.a / sqrt(pow(line.a, 2) + pow(line.b, 2))); // Offset is half of the intercept offset = (float)sqrt(intercept / (pow(line.a, 2) + pow(line.b, 2))); // intersection = tPoint +/- vec*offset Point ist1 = { tPoint.x + vecX * offset, tPoint.y + vecY * offset }; Point ist2 = { tPoint.x - vecX * offset, tPoint.y - vecY * offset }; points.insert(ist1); points.insert(ist2); break; } }
// calculate intersections of two circles static void calCircleCircleIst(Circle& circle1, Circle& circle2) { int radiusSum; int radiusDiff; int centerDis; radiusSum = (int)pow(circle1.r + circle2.r, 2); radiusDiff = (int)pow(circle1.r - circle2.r, 2); centerDis = (int)(pow(circle1.x - circle2.x, 2) + pow(circle1.y - circle2.y, 2)); // not intersect if (centerDis > radiusSum || centerDis < radiusDiff) { return; } // line passes both two intersections of circles Line line = { circle1.d - circle2.d, circle1.e - circle2.e, circle1.f - circle2.f }; // the intersections of two circles are also the intersections of line and circle calLineCircleIst(line, circle1); }