1第一次作業
1.1題目描述
對形如4*x+x^2+x的多項式求導。
1.2類圖
1.3度量分析
在完成第一次作業時,我的寫法沒有特別的“面向對象”。唯一封裝起來的是Node,代表多項式的一個項。PolyDerivation是一個方法龐雜的類,先判斷輸入是否合法,再將多項式拆分成獨立的項,接着求導,同時也包含了程序的入口main。這無疑是一個面向方法的寫法。
寫valid方法判斷合法性的時候,經歷過一次波折。一開始的思路是用一個很長的大正則表示出整個多項式,寫完發現超過了100個字符,很不美觀。而且如果表達式的項數過多,遞歸層數太深會爆棧。然后我換了一種思路,正則每次只匹配一個項,而項又可以根據x有沒有系數和指數分為5類,所以我一共寫了5種很短的正則分別匹配5種項。
但是我有一個地方多此一舉了。就是用parsePoly和parseOp兩個方法分別取得獨立的項和他們之前的符號。其實連接各個項的符號可以歸並到每個項里。
1.4BUG分析
我公測和互測階段都沒有bug。其實提交之前,我改了很多自己的bug。比如優化之后,出現了求導之后如果約掉了所有項就沒有輸出的情況。
互測階段,我hack到別人的bug有爆棧,特殊空白字符,化簡之后格式錯誤這三類問題。
2第二次作業
2.1題目描述
增加sin(x)和cos(x),每個乘積項可以有多個因子。
樣例:cos(x)*sin(x)*5+x^233+sin(x)^+2
2.2類圖
2.3度量分析
多項式的每一個乘積項其實只有四種因子:常數,x,sin(x),cos(x)。延續作業一的思想,我把乘積項封裝在Term類里面,有四個重要屬性,分別為常數項和后三種因子的指數。為了保留一個簡潔的main入口,我設置了一個只包含main方法的Main類。需要對字符串進行三步處理,即合法判斷,拆分乘積項,和求導,這些方法都放在了另一個類DealString里面。Derivation類實現了對x和三角函數的冪求導,他沒有屬性,只有方法,在需要使用他的功能時創建一個引用,然后直接調用他的方法即可。
第二次作業我把精力放在了結果化簡上面。除了合並同類項,sin(x)^2+cos(x)^2也可以化簡。每一次對三角函數化簡之后得到的新項,可能會繼續滿足化簡條件。所以我使用for循環,並設置flag記錄某次遍歷有沒有化簡出新的項,直到不能再化簡為止就停止遍歷。
2.4BUG分析
我通過了所有公測,但是互測被hack了合法性判斷問題,我有一處正則表達式漏掉了一個空白字符。
互測時使用了shell腳本,可以同時輸出小組內所有人的求導結果。結合matlab,可以很快判斷他們的運算結果是否正確。我是這樣使用matlab的:
>>x=2 sin(x)+3*x^2
1*sin(x)^1+3*x^2 ...... 3*x^2+sin(x)
先設置x的值,后面粘貼同組人輸出結果,敲擊回車,比較數值是否一致。
3第三次作業
3.1題目描述
增加多項式因子,sin(...)和cos(...)內部可以嵌套因子。
樣例:sin((2*x))^2*(cos(x)+1)
3.2類圖
3.3度量分析
Factor類代表了因子,它有常數,x的冪,sin的冪,cos的冪,乘類,加類,這6個子類。他們都重寫了合法性判斷和求導的方法。
第三次作業與前兩次最大的不同是,采用了繼承的結構,將合法性判斷和求導交給不同的因子類自行解決。
合法性判斷思路:對於常數,x的冪,sin,cos因子,valid方法的傳入參數是一個長字符串,應該判斷字符串的前綴是否為合法因子。如果包含則返回true,並且捕獲前綴中的相關信息,初始化這個因子類的屬性(指數等)。對於加類,需要從頭至尾地判斷傳入字符串是否為合法的AddClass類,而不僅僅是前綴。具體方法是判斷前綴是否為一個合法的因子,其后如果是*(乘號),把該因子加入到動態創建的MulClass實例的屬性里,如果是+-號,把這個mulClass加入到這個加類的ArrayList<MulClass>里。一邊判斷合法性,一邊存儲合法因子。
求導思路:根據乘積和嵌套的求導法則,分別調用所包含的因子實例的求導參數。結果返回一個字符串。
下表為不同因子之間的包含關系:
類名 | 屬性 |
AddClass | ArrayList<MulClass> |
MulClass | BigInteger coeff BigInteger xexp ArrayList<Sin> ArrayList<Cos> ArrayList<AddClass> |
Sin | BigInteger exp AddClass inside |
Cos | BigInteger exp AddClass inside |
Xexp | BigInteger exp |
Constant | BigInteger val |
其中加類AddClass比較特殊,輸入的多項式就是一個加類,帶括號的多項式因子也是一個加類。
3.4BUG分析
強測多個點超時,受到了毀滅性打擊。以上的類圖是在bug修復環節我重構一遍的架構,不會超時。重構之前的加類里面,我用for循環掃描字符串從start到end的子串是不是合法因子,如果不是就end++,再判斷一次;如果是就start=end+1,判斷后續因子。這個辦法for循環層數很深,處理((((((((x))))))))這種多層括號的數據點時很容易超時。
互測hack別人的數據點大多數是導數計算錯誤,少數優化的很好的同學存在過度化簡問題(比如輸出sin(2*x))。
4問題反思
4.1方法過長
checkstyle要求方法長度不超過60行,我超長的方法內基本都有一個很長的while循環。我的解決方式是將while內部,功能相對獨立的語句合並成一個新的方法。即將大方法的步驟分化成小方法,並在大方法里調用小方法。
4.2大段重復代碼
sin和cos這兩個類極為相像,因此有很多重復的代碼。我目前的思路是給他們一個共同的Tri父類,將原來重復的代碼放在父類里面實現,子類只體現區別。