BUAA軟工__我的項目做業

項目 內容
這個做業屬於哪一個課程 2020春季計算機學院軟件工程(羅傑 任健) (北京航空航天大學 - 計算機學院)
這個做業的要求在哪裏 我的項目做業
個人教學班級 005
這個項目的GitHub地址 https://github.com/LastWhisper1/IntersectionCounter

PSP項目表格

PSP2.1 Personal Software Process Stages 預估耗時(分鐘) 實際耗時(分鐘)
Planning 計劃
· Estimate · 估計這個任務須要多少時間 10 5
Development 開發
· Analysis · 需求分析 (包括學習新技術) 240 180
· Design Spec · 生成設計文檔 90 60
· Design Review · 設計複審 (和同事審覈設計文檔) 30 30
· Coding Standard · 代碼規範 (爲目前的開發制定合適的規範) 10 10
· Design · 具體設計 60 90
· Coding · 具體編碼 90 90
· Code Review · 代碼複審 20 20
· Test · 測試(自我測試,修改代碼,提交修改) 90 180
Reporting 報告
· Test Report · 測試報告 30 30
· Size Measurement · 計算工做量 30 30
· Postmortem & Process Improvement Plan · 過後總結, 並提出過程改進計劃 30 30
合計 730 755

從實際結果來看,整個項目的實現時間仍是太長了。我碼代碼的能力果真仍是使人捉急,雖然前期有過很複雜的想法,但在具體編碼時並無獲得實現,對最後的程序性能也不太滿意。雖然不熟悉C++能夠做爲一部分緣由,但總的來講在效率上仍是有很大的提高空間,從此仍是要增強本身的編碼能力,避免有想法而實現不了的狀況。c++

解題思路描述

爲了不各類偏差對結果形成的影響,解題中避免了使用浮點數,具體的實現方式上,使用瞭如下三個類:git

1. 直線類Line

直線採用最萬能的標準式 $$ Ax+By+C=0 $$ 來保存,經過給定的兩點計算出三個參數便可,而且無需化簡。github

這個類中還實現了檢驗兩條直線是否平行的函數,因爲不考慮重合,只需對兩條直線 $$ \left{\begin{matrix} & A_{1}x + B_{1}y + C_{1}=0\ & A_{2}x + B_{2}y + C_{2}=0 \end{matrix}\right. $$ 驗證是否成立 $$ A_{1}B_{2}=A_{2}B_{1} $$ 便可。框架

2. 交點類Intersection

題目要求已經確保直線不會重合,經過函數驗證兩條直線不平行後,方程 $$ \left{\begin{matrix} & A_{1}x + B_{1}y + C_{1}=0\ & A_{2}x + B_{2}y + C_{2}=0 \end{matrix}\right. $$ 的惟一解是 $$ \left{\begin{matrix} x = \frac{C{{1}}B{{2}}-C_{2}B_{1}}{B{{1}}A{{2}}-B_{2}A_{1}}\ y = \frac{A{{1}}C{{2}}-A_{2}C_{1}}{B{{1}}A{{2}}-B_{2}A_{1}} \end{matrix}\right. $$函數

在保存時,分別保存$x, y$的分子和分母(都是整數),這樣就避免了浮點數致使的精度問題。若是接下去考慮把圓加進來的話,能夠再額外保存根式下的分子和分母。但程序中沒有實現圓相關的部分,故再也不贅述。性能

3. 交點數統計類Counter

每讀入一條直線便與每一條現有直線比較,若兩條直線不平行,則計算交點值並放入set容器中,最後將新讀入的直線放入vector容器中。這樣作的話,若是使用unordered_set,在平均狀況下的時間複雜度爲$O(n^{2})$,若使用set則爲$O(n^{2}logn)$。單元測試

在set與unordered_set的選擇中,發現程序在輸入規模 $N = 10000$ 時,使用set會致使超過60秒時間限制,但使用unorder_set會致使bad_alloc異常。通過權衡,既然想不到更好的方法提高性能,不如使用set儘量保證準確性,也能避免使用unorder_set後因hash函數致使的bug。學習

4. 優化上的一些沒實現的想法

程序中,存儲和計算交點是必不可少的工做量。對每一個交點,若是在計算中,發現直線經過某個交點,那麼該直線和經過這個交點的全部直線,都不可能再有其餘交點,這樣就能夠省去和一些直線計算交點的步驟。考慮到性能測試中交點個數$h$遠小於$N(N-1)/2$,暗示多線共點的狀況較多,那麼這種方法能起到比較大的優化做用。測試

針對平行線的優化可能收效不大,舉個例子,若一條直線與一組$m$條平行線中的某一條相交,能夠知道該直線與該組平行線共產生了$m$個交點,但因爲存在多線共點的狀況,仍須要計算每一個交點的位置。優化

設計實現過程

如上一段所說,我實現了Line,Intersection,Counter三個類,並採用了上面所說的容器。在命令行參數的識別上,採用比較普通的while循環來實現參數的讀取和處理。下面主要講一下使用的測試方法與測試樣例的構建。

在構建的單元測試樣例中,我作了基本的功能測試和邊界測試。在基本的功能測試中,我儘量測試了每一個函數的功能是否正確,包括直線標準式是否正確、交點計算是否正確(也就是Line和Intersection的構造函數),同時構建了幾個比較基礎的樣例測試。在邊界測試中,考慮了諸如邊界點肯定直線、兩條直線的交點很是接近的狀況進行樣例構建,測試結果並無問題。下面這張圖描述了其中一個測試樣例的構建:

在性能方面的壓力測試中,我另寫了一個cpp文件(代碼不在git倉庫中),使用隨機生成的方法構建大量樣例。雖然這樣生成的直線並不能保證不重合(實際上機率很是小),但在我本身的程序邏輯中,兩條直線重合被視爲平行,不會引起崩潰性的bug,故在性能測試中能夠接受。實際上,隨機生成的直線幾乎不存在平行或重合的狀況,得出的交點數基本都是$N(N-1)/2$。

程序性能改進

程序寫好之後,實際上並無作大的框架上的改動,但在細節處理上仍是改過不少地方。例如,判斷兩條直線是否平行的函數和Counter類中插入新直線的函數,因爲函數體較爲簡短,故改爲了inline函數提升性能。另外,程序中還嘗試使用double代替以前介紹的分數表示方法來存儲交點,實際顯示性能提高不是那麼明顯。以下是在使用set容器存儲交點集合,$N = 5000$時性能測試的截圖:

從截圖中能夠看出,程序運行時間中至關一部分都在set的RB樹構建上(固然以前測試的時候也對其餘時間佔比較大的地方進行了優化)。

若把set替換成unordered_set的話,性能會有比較明顯的提高(經測試,在相同$N$值下,平都可減小$1/3$執行時間),但因爲沒辦法很好解決掉內存分配異常的問題,加上hash函數可能存在錯誤,故仍是採用set容器儘量保證程序正確性。

關鍵代碼說明

首先展現交點的構造函數,這裏的A1, B2等使用了宏定義,對應了兩條直線標準式的相應參數,具體含義以前的部分已經說起:

Intersection::Intersection(Line* line1, Line* line2) {
	int xnume = C1 * B2 - C2 * B1;
	int xdeno = B1 * A2 - B2 * A1;
	int ynume = A1 * C2 - A2 * C1;
	int ydeno = xdeno;
}

代碼中的xnume對應交點x座標的分子,xdeno對應x的分母,y座標依此類推。經過分子分母分別存儲的方法,避免了浮點數可能帶來的精度問題。

下面是整個程序的核心函數CountIntersections:

int Counter::CountIntersections() {
	for (size_t i = 0; i < lineSet->size(); i++) {
		Line* line1 = lineSet->at(i);
		for (size_t j = 0; j < i; j++) {
			Line* line2 = lineSet->at(j);
			if (!line1->isParallel(line2)) {
				Intersection intsec(line1, line2);
				intersectionSet->insert(intsec);
			}
		}
	}
	return intersectionSet->size();
}

代碼的邏輯在上文中已經介紹過了,這段函數中的isParallel()爲平行線的斷定函數,並寫成了內聯函數以提升性能。若想要繼續提升這個函數的性能,除了將set改爲unordered_set以外,只能從循環條件出發,略去一些沒必要要的交點計算。

Code Quality Analysis截圖

還請老師助教提出批評意見。

3/10追記:在和結對搭檔對拍程序的時候,發現了比較嚴重的bug,初步判斷是Intersection類中運算符重載錯誤引發的,但短期內難以找出消除bug的方法。在以前和搭檔對double精度的討論中,同伴的觀點是double精度足夠完成實驗需求,因此在最後一次提交時,利用程序中預留的宏定義開關將Intersection類改成double實現,直接存儲交點x, y座標的double值(在博客代碼的基礎上作除法便可獲得)。

相關文章
相關標籤/搜索