2021 BUAAOO 第三單元總結

2022-09-09 07:24:11 字數 3957 閱讀 4836

由於本單元的作業已經給出了jml,並且其整體上的架構已經比較完善了,因此總體上採用了jml的架構,只是在一些具體實現上進行了修改,例如將jml中的所有陣列都改為使用容器(具體見「三、容器選擇」模組)。而對不同型別的方法,採取了不同的實現方式,具體如下:

方法型別*****==

代表方法

實現策略

直接查詢或修改類中非容器的屬性

person中的getid,setmoney,group中的getid,message中的gettype

直接按照jml中的描述進行實現

簡單的查詢或修改乙個容器屬性的方法

peron中的quetyvalue,islinked

這些方法的jml都是利用for迴圈對陣列進行操作,因此直接套用jml會使得複雜度提高。解決方法是選用hashmap,這樣可以直接利用hashmap自帶的方法進行查詢或修改

需要遍歷單個容器進行計算的方法

group中的getvaluesum,getagemean,getagevar,network中的iscircle

jml中只有在呼叫對應方法時才會使用for迴圈進行求和,這樣如果多次呼叫該方法會使得執行時間很長。分析可以知道,實際上每次呼叫對應方法時很多都是重複的計算,因此可以在類中設定快取屬性。具體例子是在group中設定valuesum屬性記錄group中所有關係的value的和,每當呼叫addperson等方法改變容器的時候,就在其中維護valuesum的值。這樣在呼叫getvaluesum的時候就可以直接返回valuesum的值,從而降低了複雜度。再例如network中的iscircle方法,這個方法用來求兩個人之間是否有聯絡(通過有直接聯絡的人可以聯絡到一起)。通常情況下會採用深度優先或者廣度優先演算法,複雜度為o(n^2)。如果多次呼叫這個方法就會使得執行時間過長。分析可知只要確定兩人是否在同乙個連通分量就可以判斷,因此可以設定屬性表示連通分量,然後在addperons和addrelation中維護連通分量,在iscircle中直接呼叫對應結果就可以了。

多個容器屬**互的方法

network中的sendindirectmessage

對於jml描述中複雜度為o(n)的實現,直接按照jml實現即可。對於一些極其複雜的操作則考慮進行改進,例子是network中sendindirectmessage方法求最短路徑的操作。通常方式是直接使用迪傑斯特拉演算法進行求解,但該方法複雜度為o(n^2),直接使用會導致複雜度較大,因而可以使用堆優化的迪傑斯特拉演算法降低複雜度。考慮到使用迪傑斯特拉演算法需要的資料管理比較複雜,因此專門設計了乙個迪傑斯特拉類來處理相關問題。

我在這次實驗中主要使用了三種容器:hashmap,hashset,arraylist。這三種容器的特點如下:

下面具體分析我在這次作業中使用的容器。

為了提高效率,優先使用hashmap和hashset。具體例子為:

上述使用hashmap的情況都有乙個共同點:儲存在hashmasp中的物件都有乙個唯一的id進行識別,因此可以使用hashmap儲存。

使用hashmap可以顯著提高查詢和修改相應物件的速度,從而提高效率。同時由於hashmap中有著豐富的基本方法(例如containkey),這樣在實現某些jml方法的時候可以直接呼叫,也降低了程式設計難度。

我在person中使用了arratlist儲存了message,原因是person裡面的getreceivedmessages方法需要獲得前四個被接受的資訊,因此只能用arraylist儲存。

效能問題主要出現在network類中的iscircle方法和queryblocksum方法。iscircle方法計算兩個person是否在同乙個連通分量當中。而queryblocksum計算連通分量的數目。我在iscircle中使用了廣度優先演算法來計算兩個person是否在同乙個連通分量中。由於廣度優先演算法的複雜度是o(n^2),因而如果構造乙個擁有很多person的網路,並且選擇合適的兩個person呼叫iscircle方法,就會使得執行時間過長。而在queryblocksum中也使用了廣度優先演算法,同樣會有上述問題。

事實上廣度優先演算法還會產生占用記憶體過大的問題,因為在該演算法執行的過程中會不斷遞迴產生呼叫棧,一旦遞迴層數過大,就會使得占用記憶體過大。

改進方法是使用並查集演算法,建立特定屬性記錄連通分量。每當網路發生改變時(例如呼叫addperson,addrelation),就對連通分量進行維護。當呼叫iscircle和queryblocksum時,就可以使用該連通分量屬性,大大節省了執行時間(最初超時的資料點從3s降低到了0.2s),也有效減低了占用的記憶體(從170m降低到了30m)。

效能問題主要出現在group類中的getvaluesum方法。該方法用於計算乙個group中所有person之間的value值的和。如果直接使用for迴圈遍歷,那麼就需要兩層,複雜度o(n^2),如果group過大,也會使得執行時間過長。

改進方法是在group中設定乙個valuesum屬性記錄所有person之間的value值的和。每當在group中加入或刪除了乙個perosn(呼叫addperson或delperson),就對valuesum進行維護。如果這些person之間的關係發生了改變(network中的addrelation),也需要進行維護。這樣在呼叫getvaluesum方法時候就直接返回valuesum值就可以了,而不需要重複進行二重遍歷計算。

效能問題體現在network中的sendindirectmessage方法,在這個方法中需要計算兩個perosn結點之間的最短路徑。圖論中一般使用迪傑斯特拉演算法解決這個問題,複雜度o(n^2),可能超時。

改進後使用堆優化的迪傑斯特拉演算法,具體就是使用小根堆儲存各個person到源結點person的距離。這樣每次按照迪傑斯特拉演算法取距離源結點最短的結點時,就只需要花費o(logn)的時間,而優化前需要花費o(n)的時間。這樣最終的複雜度為o(mlogm),m是網路中邊的條數。事實上,這種方法當乙個網路中的邊十分稠密的時候,複雜度會達到n^2logn^2,反而增加了複雜度。但是考慮到測試資料給出了邊的條數的限制(不超過10000條),這樣就使得網路的規模要麼很小,要麼很稀疏。因此堆優化的迪傑斯特拉演算法就能夠擁有較好的效率。

從總體架構上看,這次作業的架構比較清晰。network作為乙個容器和管理者,管理了group,message,person三個類。runner作為與外界互動的類,如果需要進行操作的話只能呼叫network中的方法。然後由network根據自身的方法呼叫其管理的group,message,person物件進行相應計算,然後給出相應的結果。group的作用是將network中的person進行劃分形成子圖。

在本次作業中,perosn就是圖中的結點,而network的作用是對圖進行管理並且給這些person結點提供了乙個互動的平台。

從資料結構的角度看,作業中的關係網路圖採用了鄰接表的方式進行構造,每個person結點儲存了與其鄰接的person結點。

在上述過程中已經建立起了圖最基本也是最重要的資訊——結點和邊,理論上已經能夠求解所有有關圖的問題了,但是在某些具體問題上只依靠這些資訊,就會使得效率很低,因此需要在此基礎上進行一些補充。

補充描述連通分量的資料結構

為了描述連通分量,我在network中加入了numtoblocks屬性進行儲存,採用了hashmap。其中每個連通分量都有唯一確定的序號,而連通分量的具體實現就是乙個包含了多個person的hashset。為了能夠讓每乙個person能夠方便地查詢自己所在的連通分量的序號,我又在person中加入了blocknum屬性進行記錄。

每當圖中的連通分量發生改變、也就是呼叫了addperson和addrelation時,我就會對連通分量進行相應的維護,具體如下:

有了上述操作,就建立起了乙個便於使用並查集演算法的資料結構,使得iscirlce和queryblocksum的實現變得很容易。

在group中補充了記錄其中所有關係的value值之和的屬性

group實際上是network的乙個子圖,其中的getvaluesum需要計算其所有的value值之和。每次都進行遍歷不是乙個很好的方法。因此設定下列屬性進行記錄:

每當子圖發生改變時就需要對其進行維護。可能使子圖發生改變的方法有group中的addperosn,delperson和network中的addrealtion。因此呼叫這三個方法的時候都需要對相關的person進行區域性的遍歷,維護valuesum。

2021 BUAA OO第一單元總結

oo第一單元總結 第一次作業 一 題目要求 簡單多項式求導,僅支援常數與冪函式的乘積作為項,保證輸入無格式錯誤。二 作業實現及分析 由於第一次作業要求實現的功能並不複雜,且我當時對於物件導向的理解並不夠深入,所以我當時只是根據自己的想法構建了4個類來解決這個問題 類圖如下 關鍵類分析如下 strin...

BUAA OO 第三單元總結

一句話概括一下就是 先理解再動手,先實現再優化。首先肯定是要仔細閱讀所提供的jml規格,充分理解規格的作用和其需要完成的任務,在動手之前也先想好要用到什麼內容 選用什麼容器啦,要用什麼資料啦,需不需要修改原本的資料等等 這一點無可厚非。第二步就是先試著動動手,暫不考慮效能以及該規格與原有內容之間的內...

BUAA OO 第三單元總結

本單元主要學習根據課程組提供的介面中的jml規格,實現自己的方法。jml是一種形式化的 面向j a的行為介面規格語言,起初會比較陌生,但是在熟悉後還是比較直白易懂的,而且比較直接也非常詳細地給出了思路,當然了,本次作業中有許多處的容器和資料結構是需要自己設計和優化的。這也是三次作業主要的工作量。不過...