1. 程式人生 > 其它 >BUAA-OO-2022-Unit3

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

  1. 在atg、dfg、ar時維護組內value總和
  2. 遍歷組內點集的連通塊
  3. 遍歷邊集

qgav

均維護了年齡總和,但在最後有所不同

  1. 維護ageSum的同時,維護ageVar
  2. 每次qgav時遍歷組內成員,計算ageVar

qlc

  1. 使用破圈演算法,在ar時動態維護最小生成樹
  2. 使用克魯斯卡爾演算法,臨時計算最小生成樹

由於我在上次作業中,確定了以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 中血淚重構。希望下一單元,可以吸取教訓,更上一層樓。