Object Oriented Homework Summary(1)

OO第一單元總結(表達式求導)java

 



 

1、程序結構分析正則表達式

 

第一次做業(a*x^b)算法


  本次做業須要完成形如多個a*x^b相加(減)構成的多項式求導,並以輸出長度做爲性能指標。數組

(1)UML:架構

(2)功能概述模塊化

  本次做業難度小,整體而言實現思路較爲固定。函數

  輸入部分,本人利用正則表達式首先判斷輸入是否正確,若是正確按照在利用正則表達式的.find(String)方法讀取每一項內容。性能

  存儲部分,將讀到的內容先生成哈希值,再存入數組中。對,你沒看錯,因爲本人當時並不知曉java自帶的HashMap類,所以手動實現了一個哈希表算法,碰撞的處理方法是存入下一個空位置。若是查詢到的項與存入項指數相同那麼調用合併方法。優化

  輸出部分,中規中矩的調用重寫的toString。當時還未想到將正項提早,所以在性能上未作到最優。(PS. 要早知道這是最後一次性能拿滿的機會確定會更認真對待的,sigh....)ui

 

第二次做業(a*x^b*cos(x)^c*sin(x)^d)


   本次做業在前次的基礎上加入了三角函數。同時項的格式也不只限於a*x^b,轉而變成了(x^n|cos(x)^n|sin(x)^n|n)+這樣的形式。

(1)UML:

 

(2)功能概述

  本次做業從本質上和上次做業相似,處理思路基本沿用第一次做業。

  讀入部分。經過正則表達式先讀入一個獨立項,對於項中得每個部分利用正則表達式讀入單獨得因子。因爲項和因子得格式是固定的,所以正則表達式徹底能夠勝任這次做業的需求。另外,在讀入因子時能夠經過equals()方法合併同類項,每一項均可以被四個參數(係數,x指數,cos(x)指數,sin(x)指數)表達。

  計算部分與第一次做業相似,只是此次的單項求導結果會是最多四個項,所以在此次做業中本人沒有采用更復雜的結構,而是用統一的輸出格式返回一個包含四個類的數組。

  除此之外,此次加入了一個allMerge()方法來化簡多項式,化簡採用的範式是(sin(x)^2+cos(x)^2),在每次合併前判斷合併結果的長度是否小於原長度,若是小於則執行操做。總而言之,採用的是貪心策略進行合併化簡,所以在某些特殊狀況(好比sin(x)^4+cos(x)^4+2*sin(x)^2*cos(x)^2)下沒法達成最優化簡得結果。

  輸出部分和第一次相同,調用項的toString()方法造成最終的輸出表達式字符串。爲了保證正項提早採用了雙StringBuilder的方法,若是正項則放入第一個StringBuilder;若是是負項則放入第二個。最後先輸出第一個再輸出第二個。

 

第三次做業(嵌套表達式)


 

(1)UML:

 

(2)功能概述

   本次做業和前兩次從本質上有着比較大的不一樣。儘管單純從功能角度分析是真包含關係,然而實現角度比較難利用前兩次的算法或是總體架構。儘管如此,一些底層實現的思路是相通的,能夠必定程度借鑑以前。

  本次做業的架構採用的是分層,即將全部表達式分紅三層:Polynomial,Term,Factor。其中Polynomial由多個Term加減鏈接而成;Term由多個Factor相乘鏈接而成;Factor分紅五類(sin(F)^n,cos(F)^n,x^n,n,(P))

  輸入部分。本次做業因爲支持嵌套,輸入須要必定程度的封裝。本人在設計上犯了2,先檢查格式錯誤再讀取,這就致使許多格式檢查或是重複或是遺漏(儘管沒有檢測出來,但不排除這種可能)。更合理的設計應當是將格式檢查分離成多層,每層只檢測屬於本層的格式問題,其他的拋給下層判斷。

  計算部分。求導的本質沒有改變,但每層之間的狀態轉換表本質上是一個有限狀態機。Polymonial的求導結果必定是Polymonial;Term的求導結果是多個Term;Factor的求導結果能夠被歸結成(Polymonial)類型的Factor,如此一來即可以規範化求導的過程,使得遞歸求導的過程可預測。

  輸出部分。本次採用的是遞歸輸出,即上層的toString方法調用下層的toString,遇到基本元素的toString則返回其實際內容。

 

2、Bug分析

  一言以蔽之,正則。(沒錯,就這個,沒別的了,真的)

  其實這單元的做業在求導的計算方面很難出現問題。第一二兩次做業較爲簡單,只須要指數減一便可;第三次做業一但造成模塊化處理,若是整個程序可以正常運行,單獨某個模塊出現計算問題的機率能夠忽略不計。

 

3、發現別人Bug所採用的策略

  一言以蔽之,正則。

  除此以外,大量隨機樣例對拍。

  不少人代碼不寫註釋,沒有說明,可讀性幾乎不存在。

  事實上,從debug的結果上來看,大多數碰到的問題都是格式判斷有誤,要不就是少考慮了一些特殊狀況(好比指數爲零,或是乘零乘負一)。一些格式問題來源於優化,好比結果是零時無輸出等。

 

4、重構心得

  本部分分爲兩個主題:架構設計以及細節實現。

 

架構設計


  出於面向對象的思路,綜合本身幾回代碼,所得到的心得以下:

  一、封裝的完整性

  對於每個類必定應該有明確的功能範圍。不只要規範其中構造方法所須要的數據格式,也要規範這個類所應對的問題範圍。好比第三次做業的Polynomial類,其應對的應當是全部形如([+-]Term)+這樣的表達式的識別以及處理。對於功能部分好比格式判斷,應當保證可以識別全部屬於本層的格式問題(好比加減號的數量),而且對於不屬於本層範疇的問題應當將其包裝遞送至下一層處理(Term)。如此設計能夠保證類和類之間的協做實現連貫性與完備性,避免功能的重複實現以及缺漏現象。

  二、數據遞送的規範性

  承接上條。每一個方法的輸入和輸出應當保證其規範性。大多數涉及到類之間交互的方法不該當將基礎數據(好比Int或是BigInteger)做爲遞送的形式,而是將數據封裝成一個獨立的類再傳遞給其餘類。好比derivate()方法,不一樣層的derivate()應當返回的內容時不一樣的,上級調用下級derivate()時收到的返回值應當是一個能夠兼容的類(好比Polynomial類的derivate()須要調用其中每一個Term的derivate(),此時就應當讓Term.derivate()返回一個Polynomial型的對象,再統一集中處理)。

 

細節實現


 

   一、合併同類項優化

  目前設計了一個較爲高效的識別同類項方法。經過判斷兩個對象在x不一樣取值下的值來斷定是否爲同類項,好比x^3*cos(x)^2*cos(x)^-1與x^2*x*cos(x)^+1這兩項在x取值相同時值明顯相同。這種方法一樣適用於更爲複雜的狀況。具體實現上目前有兩種思路。一種是預先準備一組x的取值,而後對於每個待合併的項都計算出一組對應結果。經過比對對應結果來判斷是否能夠合併;一種是在每次比對時隨機生成x的值,而後比對結果。兩種方法前者在效能上更優,能夠在讀入過程當中生成每個對象的特徵值組;後者則更保險,由於隨機生成的值不容易出現不匹配但卻能兼容的錯誤判斷(如x^3與x在x=1時值相等)。

  二、模擬退火合併sin(x)^2+cos(x)^2

  針對目前優化方案中貪心所致使的缺陷,能夠引入模擬退火的方式來尋找最優解。具體實現方案以下:

    (1)隨機選擇兩項,判斷是否能夠合併

    (2)若是能夠合併,但長度增加,則按照當前的溫度選擇接受機率;不然進行合併。

    (3)若是進行屢次上述操做均無發生合併,則將結果返回

  這樣能夠在必定程度上避免局部最優的狀況發生。實現細節可能還須要進一步雕琢,但大體思路如上,但願可以拋磚引玉。

相關文章
相關標籤/搜索