<font size=6>2019面向對象程序設計第一單元總結</font>java
<font color="red" face="楷體" size="4">(1)思路一:WF判斷與分項結合,避免大正則爆棧問題</font>正則表達式
第一次做業中,多項式的結構具備極強的規律性,因此很多同窗想到用正則表達式匹配整串,但忽略了大正則存在回溯次數過多可能會致使爆棧。所以正確的處理策略之一是,單獨處理輸入中的空格,正負號以及非法字符,而後用正則匹配表達式中的每一項,獲得的全部項相加,若與原表達式不相同,則判爲「WRONG FORMAT!」,不然,則輸出求導結果。express
正則匹配處理空格與符號後的每一項:架構
Pattern pattern = Pattern.compile("([-+]+(([-+]?(\\d+\\*)?x(\\^[-+]?\\d+)?)|([-+]?\\d+)))");
而後用<font color="blue">Matcher</font>中的<font color="blue">find()</font>函數實現分離每一項,用<font color="blue">Matcher</font>中的<font color="blue">group()</font>函數實現項處理。函數
<font color="red" face="楷體" size="4">(2)思路二:用ArrayList、LinkedList或HashMap實現動態插入新項</font>測試
筆者在第一次做業中採用的是利用泛型的LinkedList實現優化
public class PolyHandler { //利用鏈表構造求導後的新多項式 private LinkedList<String[]> polynomial; //構造函數 PolyHandler(LinkedList<String[]> polynomial) { this.polynomial = polynomial; }
重點:在動態添加元素的過程當中,實現同類項的合併,可以大大減小優化複雜度。this
筆者利用PolyHandler類中的merge函數實現求導後多項式合併新項spa
void merge(String[] item)
下圖爲第一次做業個人類圖設計
StringMatcher用於初步判斷輸入是否合法
ItemHandler爲項處理類
PolyHandler爲多項式處理類
MainHandler爲主類
<font color="red" face="楷體" size="4">(1)思路一:本次做業仍可利用正則進行分項</font>
筆者認爲,第二次做業相比第一次多項式求導做業而言,在處理非法輸入與求導方面對總體架構的改變不大,所以筆者沿用了第一次做業的設計,複用了較多的類。
正則分項:
Pattern pattern = Pattern.compile("[-+](\\d+|(x(\\^[-+]?\\d+)?)|" + "(sin\\(x\\)(\\^[-+]?\\d+)?)|(cos\\(x\\)(\\^[-+]?\\d+)?))" + "(\\*[-+]?(\\d+|(x(\\^[-+]?\\d+)?)" + "|(sin\\(x\\)(\\^[-+]?\\d+)?)|(cos\\(x\\)(\\^[-+]?\\d+)?)))*");
emmm......看上去是否是很複雜呢,在第二次做業咱們已經能夠上遞歸降低分析了!
<font color="red" face="楷體" size="4">(2)思路二:用HashMap處理多個Key,化簡時有巨大優點</font>
自定義一個類,包含多個Key,將這個類做爲HashMap的Key類型,並實現這個類的哈希函數和相等性斷定函數。
1.哈希函數
int hashcode()
2.相等性斷定函數
boolean equals(object obj)
下圖是筆者本身設計的HashKey類中重寫的核心方法
注:在第二次做業中,利用HashMap實現同類項的合併相比ArrayList與LinkedList有更大優點。
下圖爲我第二次做業的類圖
StringMatcher類用於判斷輸入是否合法
HashKey類爲本身設計構造的HashMap的Key類型
Expression類爲表達式處理類
Item類爲項處理類
Derivation類爲求導類
<font color="red" face="楷體" size="4">核心思想——遞歸降低分析法:表達式由項構成,項由因子構成,因子的種類能夠是表達式,冪函數,常數項,三角函數類。</font>
1.表達式:讀取項,用LinkedList存儲項:
LinkedList<Term> parseExpression()
2.項:讀取因子,用LinkedList存儲因子:
Term parseTerm()
3.因子
//外包函數,分析因子種類,而後分種類爬取因子 Factor parseFactor() //獲取常數項因子 Factor getConstantFactor() //獲取冪函數因子 Factor getXFactor() //獲取正弦函數因子 Factor getSinxFactor() //獲取餘弦函數因子 Factor getCosxFactor() //獲取表達式因子 Factor getExpressionFactor()
爲使邏輯清晰,可把因子種類定義爲枚舉類型。
public enum FactorType { constant,x,sin,cos,expression }
下圖爲第三次做業個人類圖
StringMatcher類用於判斷輸入格式是否爲正確格式
Expression類爲表達式處理類
Term類爲項處理類
Factor類爲因子處理類
FactorType枚舉類型定義了因子類型
<font size=5>二.優化策略簡介</font>
筆者會在另外一篇博文中對優化策略進行詳解,在此僅說下總體思路。</u>
題外話,筆者在三次做業的優化中均取得了還不錯的成績,第一次做業滿分,第二次做業第11名,第三次做業前10名
<font color ="lightgreen">1).第一次做業優化要點:</font>
①合併同類項
②正項前移
<font color ="lightgreen">2).第二次做業優化要點:</font>
①合併同類項
②利用三角函數公式簡化(主要採用瞭如下四類公式):
1° sin(x)^2 + cos(x)^2 = 1 2° 1-sin(x)^2 = cos(x)^2 3° 1-cos(x)^2 = sin(x)^2 4° sin(x)^4 - cos(x)^4 = sin(x)^2 - cos(x)^2
③正項前移
<font color="lightgreen">3).第三次做業優化要點:</font>
筆者認爲,第三次做業的優化重點不在於利用三角函數公式簡化,而在於在可控時間複雜度下利用遞歸降低實現同類項的合併,這便須要重寫Expression類、Term類、Factor類的hashcode()和equals()函數
<font size=5>三.綜合三次做業的方法複雜度的分析</font>
第一次做業:
第二次做業:
第三次做業:
能夠看出,隨着三次做業的層層遞進,工程的方法數目與方法複雜度均在上升。因爲2、三次做業的優化相對複雜,故我在相應的類中實現了較多複雜度較高的方法來進行相應的優化,具體方法邏輯可參考類圖。
<font size=5>四.Bug分析</font>
三次做業個人強測所有經過,均沒有出現bug,加上優化分,筆者在三次做業中的強測得分分別爲100分、99.2839分、96.7647分。
但在第三次做業中,我被別的玩家hack了一個點,系我在化簡過程當中一個if語句判斷失誤,去除了三角函數中表達式因子的括號,致使輸出格式有問題。這也告誡我,在優化過程當中,必定要考慮全面。
<font size=5>五.高效Debug和Hack策略分析</font>
筆者認爲,其實要想成爲OO互測的頂級玩家,必需要擁有一臺本身編寫的評測機,千萬不要偷懶。
<font color="red" face="楷體" size="4">(1)構建本身的「強測」評測機(全面覆蓋):</font>
① 自動生成隨機表達式串
② 讓本組成員的程序互拍,尋找不一樣結果。
③ 判斷錯誤結果的來源、錯誤結果的種類(即Wrong Format Error、Wrong Value Error仍是Wrong Derivative Error),並輸出到指定文件
下圖是用Python實現的第三次做業的評測機。
<font color="red" face="楷體" size="4">(1)精心構造測試樣例(重點針對):</font>
評測機生成的數據太隨機,所以每每有些bug會疏漏掉,須要本身讀懂評測屋內的代碼,並分析其架構和邏輯上的失誤,並構造相應樣例。
好比第二次做業的一個針對優化的典型hack樣例:
x*sin(x)^4*cos(x)^-4+x*sin(x)^5*cos(x)^-5+x*sin(x)^2*cos(x)^-2+x*cos(x)^-3+x*sin(x)^6*cos(x)^-6
第三次做業的一個典型hack樣例
cos((((-+((((sin(x)))))))))
<font size=5>六.Applying Creational Pattern</font>
工廠模式與抽象工廠
對於本次做業可使用工廠模式來建立表達式,項,因子,咱們只需定義一個建立對象的接口,讓實現了該接口的子類本身決定實例化哪個工廠類。在咱們明確地計劃不一樣條件下建立不一樣實例時,工廠模式將十分管用。
抽象工廠模式是圍繞一個超級工廠建立其餘工廠。在抽象工廠模式中,接口是負責建立一個相關對象的工廠,不須要顯式指定它們的類。每一個生成的工廠都能按照工廠模式提供對象,這樣咱們就不用花費時間在選擇接口上了。
<font size=5>七.總結與展望</font>
北航的OO課正在不斷變好,感謝老師、助教和爲其餘爲這門課默默付出的人。我會再接再礪,不斷提高本身的分析能力和代碼能力。