BUAA-OO-2022-Unit3
面向物件設計與構造2022第三單元總結
零、任務簡介
本次作業,需要完成的任務為實現簡單社交關係的模擬和查詢,學習目標為 入門級JML規格理解與程式碼實現。
本單元作業基本主幹已有模板,故而描述也變得謎語人起來,第一次作業會很摸不到頭腦。
示例輸入:
ap 1 jack 100
ap 2 mark 100
ar 1 2 100
qv 1 2
qbs
qps
qci 1 2
示例輸出:
Ok
Ok
Ok
100
1
2
1
一、Task1
1) 任務說明
設計指令如下
add_person id(int) name(String) age(int) add_relation id(int) id(int) value(int) query_value id(int) id(int) query_people_sum query_circle id(int) id(int) query_block_sum add_group id(int) add_to_group id(int) id(int) del_from_group id(int) id(int)
2) 整體結構
第一次作業的整體架構如下。簡潔考慮,隱去了異常處理類。
想來第一次作業,同學們的架構都很接近吧,主要敘述互動邏輯方面。
根據效能需求,需要最差時間複雜度期望為O(n),故而本人搭建的複雜度列表為
指令 | 最差時間複雜度 |
---|---|
ap | O(1) |
ar | O(n) |
qv | O(1) |
qps | O(1) |
qci | O(1) |
qbs | O(1) |
ag | O(1) |
atg | O(1) |
dfg | O(1) |
qbs
大部分均採用並查集演算法,並使用了路徑壓縮排行了演算法優化。
但是我在此處並沒有選擇這種方式,而是選擇了標記聯通塊,在ar時,視情況合併兩個聯通塊。則可以使用 O(1) 複雜度解決問題。
這樣的代價是ar的最差複雜度為 O(n) 。但是依據本人此前的原則,最差複雜度反而從 O(nlogn) 降低至 O(n)。雖然並查集路徑壓縮會使得後續複雜度遠低於 O(nlogn),但是從單次查詢角度看,確實更慢。
而且,對於所有詢問類指令,都可以實現 O(1) 查詢。從設計理念上,更貼近實際應用。
3) 互測體驗
強測AC
Hack零次。
我房的人都tql,一個都刀不中qwq,全員成功率0%
4) 效能優化
前文已述
5) 額外完成任務
- qgvs
- qgav
二、Task2
1) 任務說明
- 新增最小生成樹。有關查詢指令
- 新增message資訊,以及相關操作
2) 整體結構
與Task1相同部分不再贅述。
自初新建了message類作為資訊的存貯載體。Mypair類是一箇中間類,表示二元組,方便coding而設定。
對於以下幾種方法,有不同的實現策略
qgvs
- 在atg、dfg、ar時維護組內value總和
- 遍歷組內點集的連通塊
- 遍歷邊集
qgav
均維護了年齡總和,但在最後有所不同
- 維護ageSum的同時,維護ageVar
- 每次qgav時遍歷組內成員,計算ageVar
qlc
- 使用破圈演算法,在ar時動態維護最小生成樹
- 使用克魯斯卡爾演算法,臨時計算最小生成樹
由於我在上次作業中,確定了以ar為核心操作,所有詢問操作均為O(1)的基本策略。所以在qgvs和qlc中,均選擇了方法1
注意到最小生成樹為n-1邊,故破圈法的複雜度為O(v) = O(n)
3) 互測體驗
強測AC
Hack兩次,被hack一次。
Hack主要的原因為在qgvs中,有同學選擇的遍歷兩遍點集,複雜度為O(n2)。但實際上只需要遍歷邊集,複雜度為O(v)。故而構造了一組資料,卡了超時,刀中兩人。
被Hack是因為,在維護最小生成樹時,未考慮全部邊權為0的情況,我預設邊權均為正,被一位同學的評測機刀中。
4) 效能優化
前文已述
5) 額外完成任務
無
三、Task3
1) 任務說明
- 對message進行了細分,並加入相關指令
2) 整體結構
與Task2相同部分不再贅述。
第三次作業的難度相比前幾次並沒有很大區別,新增的類比較友好(除了jml比較謎語人)
3) 互測體驗
強測AC
Hack八次,被hack零次。
很好奇,為什麼這次作業這麼多人被刀。可能是jml太謎語人了。
這次互測當天比較忙,就評測機隨緣發刀了,刀中很多也不知道為什麼。似乎是因為這次中測強測都很弱,同房的人從巨佬變成了佬。我甚至刀中一個把id當金額的人,他竟然活過來中測強測,難以置信。
4) 效能優化
本次似乎想過需要迪傑斯特拉+堆優化,但這個演算法並不難,且網上大量模板。除此之外,沒有可以優化的點。
5) 額外完成任務
無額外目標
四、互測
筆者在互測中,使用了測評機和對拍機。
使用對拍的方式進行測試。未使用Junit(真的不好用,又不能造資料)
完全隨機
採用完全隨機的策略,通過擴大測試次數以及單次測試的指令規模發現Bug
單元隨機
可動態調整各指令出現的概率和指令中id生成的概率等,集中測試相應的功能模組
評測機暴力Hack,大概率是Hack不下來的。假如這位同學有評測機,那麼大概率在A房,地毯式轟炸並不一定能保證效果。對於高手互博,往往功能本身不會出錯,只有想怪資料,反常識資料,才可克敵制勝。
既然如此,看程式碼就是必不可少的。和刀我的同學交流,發現他也是通過看程式碼才刀到。
據我不完全觀察,防禦力較低的程式碼遵循以下規律。
- 優化多
- 父子類之間很少呼叫方法
- 重複程式碼過多
五、Network擴充套件
/*@ public normal_behavior
@ requires (\exists int i; 0 <= i && i < people.length; people[i].euqals(getPerson(procuderId)) && people[i] instanceof Producer);
@ requires (\exists int i; 0 <= i && i < people.length; people[i].equals(getPerson(procuderId)) && people[i] instanceof Advertiser);
@ assignable getPerson(producerId).advertisers;
@ assignable getPerson(AdvertiserId).producers;
@ ensures getPerson(producerId).advertisers.length == \old(getPerson(producerId).advertisers.length) + 1;
@ ensures (\exists int i;0 <= i < getPerson(producerId).advertisers.length;getPerson(producerId).advertisers[i].equals(getPerson(AddVertiserId)));
@ ensures (\forall int i; 0 <= i && i < \old(getPerson(producerId).advertisers.length;
@ \exists int j;0 <= j < getPerson(producerId).advertisers.length;
@ \old(getPerson(producerId).advertisers[i]).euqals(getPerson(producerId).advertisers[j]));
@ ensures (\exists int i;0 <= i < getPerson(advertiserId).producers.length;getPerson(advertiserId).producers[i].equals(getPerson(addVertiserId)));
@ ensures (\forall int i; 0 <= i && i < \old(getPerson(advertiserId).producers.length;
@ \exists int j;0 <= j < getPerson(producerId).producers.length;
@ \old(getPerson(advertiserId).producers[i]).euqals(getPerson(advertiserId).producers[k]));
@ also
@ public exceptional_behavior
@ signals (PersonIdNotFoundException e) !(\exists int i; 0 <= i && i < people.length;
@ people[i].equals(getPerson(producerId)));
@ signals (PersonIdNotFoundException e) (\exists int i; 0 <= i && i < people.length;people[i].equals(getPerson(producerId)))
@ && !(\exists int i; 0 <= i && i < people.length; people[i].equals(getPerson(advertiserId)));
@*/
void addAdvertise(int producerId,int advertiserId);
/*@ public normal_behavior
@ requires (\exists int i; 0 <= i && i < people.length; people[i].euqals(getPerson(customerId)) && people[i] instanceof Customer);
@ requires (\exists int i; 0 <= i && i < people.length; people[i].equals(getPerson(AdvertiserId)) && people[i] instanceof Advertiser);
@ assignable getPerson(customerId).attentions;
@ ensures getPerson(customerId).attentions.length == \old(getPerson(customerId).attentions.length) + 1;
@ ensures (\exists int i;0 <= i < getPerson(customerId).attentions.length;getPerson(customerId).attentions[i].equals(getPerson(AdvertiserId)));
@ ensures (\forall int i; 0 <= i && i < \old(getPerson(customerId).attentions.length;
@ \exists int j;0 <= j < getPerson(customerId).attentions.length;
@ also
@ public exceptional_behavior
@ signals (PersonIdNotFoundException e) !(\exists int i; 0 <= i && i < people.length;
@ people[i].equals(getPerson(customerId)));
@ signals (PersonIdNotFoundException e) (\exists int i; 0 <= i && i < people.length;people[i].equals(getPerson(customerId)))
@ && !(\exists int i; 0 <= i && i < people.length; people[i].equals(getPerson(AdvertiserId)));
@*/
void addAttention(int customerId,int AdvertiserId);
/*@ public normal_behavior
@ requires (\exists int i; 0 <= i && i < people.length; people[i].euqals(getPerson(customerId)) && people[i] instanceof Customer);
@ requires (\exists int i; 0 <= i && i < people.length; people[i].equals(getPerson(AdvertiserId)) && people[i] instanceof producerId);
@ requires (\exists int i; 0 <= i && i < getPerson(customerId).attentions.length; (\exists int j;0 <= j && j < getPerson(customerId).attentions.length;getPerson(customerId).attentions[j].equals(getPerson(producerId)));
@ assignable getPerson(customerId).money;
@ assignable getPerson(producerId).saleVolume;
@ ensures getPerson(customerId).money == \old(getPerson(customerId).attentions.length) - getPerson(producerId).getProce();
@ ensures (\exists int i;0 <= i < getPerson(customerId).attentions.length;getPerson(customerId).attentions[i].equals(getPerson(AdvertiserId)));
@ ensures getPerson(customerId).volume == \old(getPerson(customerId).volume) + 1;
@ also
@ public exceptional_behavior
@ signals (PersonIdNotFoundException e) !(\exists int i; 0 <= i && i < people.length;
@ people[i].equals(getPerson(customerId)));
@ signals (PersonIdNotFoundException e) (\exists int i; 0 <= i && i < people.length;people[i].equals(getPerson(customerId)))
@ && !(\exists int i; 0 <= i && i < people.length; people[i].equals(getPerson(producerId)));
@*/
void purchase(int customerId,int producerId);
六、心得感想
(坐大牢)
第三單元的OO課程,比第二單元輕鬆不少,但還是難度不小。我學到了許多面向物件程式設計的方法和思維,更加熟練地掌握了Java語言。
當然,這一單元的訓練也讓我認識到了自己的許多不足,比如思路不清晰,在 Task1 到 Task2 中血淚重構。希望下一單元,可以吸取教訓,更上一層樓。