文/高向敏
(鎮(zhèn)江第一外國語學(xué)校 江蘇省鎮(zhèn)江市 212000)
通俗地來講,算法就是做某件事情或解決某一問題的方法、步驟、過程和程序,而算法優(yōu)化是指對(duì)算法的有關(guān)性能進(jìn)行優(yōu)化。舉個(gè)例子,大家熟知的田忌賽馬的故事,在之前的比賽中,田忌總是以上等馬對(duì)上等馬,中等馬對(duì)中等馬,下等馬對(duì)下等馬,齊威王每一個(gè)等級(jí)的馬都要比田忌的強(qiáng),所以田忌總是輸;后來孫臏給田忌出了個(gè)主意:比賽時(shí),讓他以下等馬對(duì)齊威王的上等馬,再以上等馬對(duì)他的下等馬,最后以中等馬對(duì)他的下等馬。比賽結(jié)束,田忌以三局兩勝的戰(zhàn)績?nèi)〉昧藙倮?。同樣的馬匹,僅僅調(diào)換了比賽順序,就得到了反敗為勝的結(jié)果。從算法角度看,每場(chǎng)比賽的賽馬次序編排都是一種算法,而孫臏的策略就是一種經(jīng)過優(yōu)化的算法。
同一問題可用不同算法解決,而一個(gè)算法的質(zhì)量將影響到算法、程序直至軟件的工作效率。衡量算法性能的主要指標(biāo)有時(shí)間復(fù)雜度、空間復(fù)雜度、正確性、健壯性等。就單純編程競賽來說,競賽要求每題的每個(gè)測(cè)試點(diǎn)數(shù)據(jù)應(yīng)在1s內(nèi)出運(yùn)算結(jié)果,也就是基本語句的運(yùn)算次數(shù)應(yīng)該控制108以內(nèi),因此對(duì)于問題規(guī)模較大的題目,對(duì)算法進(jìn)行合理優(yōu)化是必不可少的;就宏觀應(yīng)用來說,假若將一個(gè)大的程序運(yùn)用到實(shí)際生產(chǎn)生活中時(shí),算法的執(zhí)行效率將直接影響了生產(chǎn)效率,特別是,大數(shù)據(jù)時(shí)代到來,算法要處理的數(shù)據(jù)數(shù)量級(jí)越來越大,處理問題的場(chǎng)景也愈加千變?nèi)f化,因此,為提升算法的效率,對(duì)算法進(jìn)行優(yōu)化也是必不可少的環(huán)節(jié)。
人們使用計(jì)算機(jī)處理問題時(shí),首先要對(duì)實(shí)際問題進(jìn)行抽象,運(yùn)用數(shù)學(xué)思想創(chuàng)建數(shù)學(xué)模型,進(jìn)而設(shè)計(jì)出解決的算法,然后再進(jìn)行編程、測(cè)試、調(diào)整,這是一個(gè)實(shí)際問題運(yùn)用計(jì)算機(jī)得以解決的過程。從這一過程可以看出,創(chuàng)建數(shù)學(xué)模型是前提,數(shù)學(xué)模型能夠有效的把復(fù)雜的問題變簡單,化繁為簡,將一些復(fù)雜程度較高的、難以實(shí)現(xiàn)的編程問題,轉(zhuǎn)化成單一的、便于運(yùn)算的數(shù)學(xué)結(jié)構(gòu)。因此,在進(jìn)行算法優(yōu)化分析時(shí)首要的就是要發(fā)揮數(shù)學(xué)理論知識(shí)的優(yōu)勢(shì),構(gòu)建數(shù)學(xué)模型,在此基礎(chǔ)上設(shè)計(jì)并優(yōu)化算法,求得問題的有效解決方式。數(shù)學(xué)建模不僅密切了數(shù)學(xué)、算法與計(jì)算機(jī)編程的關(guān)系,同時(shí)也直接影響了算法的正確性與性能。
當(dāng)我們建立好數(shù)學(xué)模型后,將實(shí)際問題抽象化為單一簡單的數(shù)學(xué)結(jié)構(gòu)的時(shí)候,數(shù)學(xué)算法的使用對(duì)于計(jì)算機(jī)編程而言是必不可少的,是實(shí)現(xiàn)計(jì)算機(jī)編程算法優(yōu)化的重要途徑。如早期的數(shù)學(xué)算法代表——?dú)W幾里德算法,又名輾轉(zhuǎn)相除法,是為了求得最大公約數(shù)的一種遞回算法,每一步計(jì)算的輸出值就是下一步計(jì)算時(shí)的輸人值,這樣的算法在處理大數(shù)時(shí)效率很高,也是計(jì)算復(fù)雜性理論的開篇。再如秦九韶算法、更相減損術(shù)、中國剩余定理等等,這些數(shù)學(xué)算法對(duì)編程算法優(yōu)化都有著重大意義。除了諸如上述的經(jīng)典數(shù)學(xué)算法之外,重要的數(shù)學(xué)方法如歸納法、演繹法等,或是程序設(shè)計(jì)者自己綜合運(yùn)用數(shù)學(xué)知識(shí),充分挖掘?qū)嶋H問題中蘊(yùn)含的數(shù)量關(guān)系,所尋找的數(shù)學(xué)規(guī)律、推導(dǎo)的數(shù)學(xué)公式、得出的數(shù)學(xué)結(jié)論,都是算法優(yōu)化的重要途徑。在此基礎(chǔ)上設(shè)計(jì)的算法,往往使得原本難以解決的問題得以解決,原本已解決的問題得以高效解決。
圖1:各因子對(duì)應(yīng)關(guān)系
圖2
圖3:行列值與所在圈數(shù)之間的關(guān)系圖
圖4
總之,數(shù)學(xué)思想是連接問題與算法的一座橋梁,有些問題利用這座橋可以更為方便快捷地往返于河兩岸,而還有一些問題,如果不利用這座橋可能根本無法到達(dá)河的對(duì)岸,因此,借助數(shù)學(xué)思想分析問題是進(jìn)行算法優(yōu)化的重要途徑。
下面結(jié)合編程教學(xué)中的幾個(gè)具體實(shí)例,踐析數(shù)學(xué)思想在算法優(yōu)化中的應(yīng)用策略。
求n以內(nèi)的完全數(shù)。
表1:不同方法運(yùn)行時(shí)間比較
表2
表3
完全數(shù)(Perfect number),又稱完美數(shù)或完備數(shù),是一些特殊的自然數(shù)。它所有真因子(即除了自身以外的約數(shù))的和,恰好等于它本身。第一個(gè)完全數(shù)是6(1+2+3=6),第二個(gè)完全數(shù)是28 (1+2+4+7+14=28)。若求n內(nèi)的完全數(shù),需要對(duì)2-n以內(nèi)的每個(gè)數(shù)判斷是否是完全數(shù)(1不是完全數(shù)),因此本題關(guān)鍵在于如何判斷一個(gè)數(shù)i是否是完全數(shù)。對(duì)于這個(gè)問題可以用不同的數(shù)學(xué)方法來解決。
方法一:最直接的方法,從1到i-1每個(gè)數(shù)依次判斷其是否是i的真因子,若是則累加求和至sum,若sum=i,則說明i是完全數(shù)。
方法二:根據(jù)數(shù)學(xué)常識(shí),對(duì)于某一整數(shù)i來說,其最大因子為i/2,在i/2~i-1范圍內(nèi)沒有數(shù)據(jù)可以整除此數(shù)。據(jù)此,我們可以把遍歷范圍縮小至1~i/2,這樣程序效率可提高一倍。
方法三:進(jìn)一步思考,若j是i的一個(gè)因子,則i/j一定也是i的因子,若已知1,2,4,5,10是100因子,相應(yīng)地即可得出100,50,25,20,10也是其因子(如圖1所示),據(jù)此,我們可以把遍歷范圍縮小至1~即可。
方法三改進(jìn):sqrt函數(shù)一個(gè)求平方根的庫函數(shù),函數(shù)調(diào)用有一定的時(shí)間損耗,其次,內(nèi)部是浮點(diǎn)數(shù)運(yùn)算,效率也不高,乘法的效率比除法要高的多,因此我們對(duì)循環(huán)結(jié)束條件又做了一部分改進(jìn)(j*j<=i),程序效率會(huì)大大提高。
為了更加直觀對(duì)比各算法的運(yùn)行效率,我們分別對(duì)其進(jìn)行了n=10000,以及n=1000000的測(cè)試,程序運(yùn)行時(shí)間對(duì)比如表1。
利用簡單的數(shù)學(xué)常識(shí),縮小數(shù)據(jù)范圍,對(duì)程序的局部進(jìn)行優(yōu)化,如對(duì)程序循環(huán)次數(shù)簡化等,從而達(dá)到提高程序工作效率的目的。
一個(gè)n行n列的螺旋矩陣可由如下方法生成:從矩陣的左上角(第1行第1列)出發(fā),初始時(shí)向右移動(dòng);如果前方是未曾經(jīng)過的格子,則繼續(xù)前進(jìn),否則右轉(zhuǎn);重復(fù)上述操作直至經(jīng)過矩陣中所有格子。根據(jù)經(jīng)過順序,在格子中依次填入 1,2,3,...,n2,便構(gòu)成了一個(gè)螺旋矩陣。圖2是一個(gè) n = 4 時(shí)的螺旋矩陣?,F(xiàn)給出矩陣大小 n 以及i和j,請(qǐng)你求出該矩陣中第i行第j列的數(shù)是多少。
方法一:最直接解法,建立一個(gè)二維數(shù)組a依次存放各個(gè)矩陣數(shù)據(jù),然后輸出a[i][j]的值即可。而當(dāng)n=30000時(shí),需要建立30000*30000的二維數(shù)組,結(jié)果會(huì)發(fā)生數(shù)據(jù)溢出且超出內(nèi)存上限。
方法二:我們可采用類似貪吃蛇的方法,讓它在n*n個(gè)方格內(nèi)自外向內(nèi)逐格移動(dòng),控制其右轉(zhuǎn)的方向,直到到達(dá)i行j列位置,整個(gè)過程中移動(dòng)步數(shù)即為求解值。
方法三:在上一個(gè)方法中,若遇到極端情況——當(dāng)n=30000時(shí)候,求第15001行15000列的數(shù)是多少,需要移動(dòng)次數(shù)為900000000,顯然太慢,因此我們需要進(jìn)一步優(yōu)化??紤]到貪吃蛇總是從外圍一圈圈地向目的地前進(jìn),而每一圈剛好是一個(gè)正方形,我們可以先計(jì)算外圍各圈正方形的周長之和,再讓貪吃蛇從目的地所在圈的的左上角出發(fā),計(jì)算其從出發(fā)地到目的地的長度就可以了。鑒于此,我們需要尋找如下幾個(gè)數(shù)學(xué)規(guī)律:
(1)第i行j列位于第幾圈(假設(shè)為第x圈)
(2)前x-1圈,每一圈有多個(gè)數(shù)字;
(3)第i行j列位于第x圈的第幾個(gè)。
由圖3得數(shù)學(xué)規(guī)律:第i行第j列的所在的圈數(shù)x一定是(i,j,n+1-i,n+1-j)這四個(gè)數(shù)中的最小值;第1圈有(n-1)*4個(gè)數(shù)字,第2圈有(n-3)*4個(gè)數(shù)字,……,則第k圈有(n-2*k+1)*4個(gè)數(shù)字;我們只需要從所在x圈的左上角(x,x)位置出發(fā),走到目的地(i,j)即可,最多走一圈,各算法效率比較如表2所示。
建立數(shù)學(xué)模型,尋找各數(shù)據(jù)之間內(nèi)在的數(shù)學(xué)關(guān)系,歸納數(shù)學(xué)規(guī)律,在此次基礎(chǔ)上優(yōu)化算法,往往大大提高算法性能。
如圖4,一條狹長的紙帶被均勻劃分出了n個(gè)格子,格子編號(hào)從1到n。每個(gè)格子上都染了一種顏色colori(用[1,m]當(dāng)中的一個(gè)整數(shù)表示),并且寫了一個(gè)數(shù)字numi。
定義一種特殊的三元組:(x,y,z),其中 x,y,z 都代表紙帶上格子的編號(hào),這里的三元組要求滿足以下兩個(gè)條件:(1) x,y,z都是整數(shù),x 方法一:最直接的解法,根據(jù)x,y,z的關(guān)系 y-x=z-y,雙重循環(huán)枚舉x,y,當(dāng)滿足條件colorx=colorz時(shí)求得該三元組分?jǐn)?shù)并累加起來。 方法二:方法一中如果n數(shù)值較大時(shí),非常慢,因此需要充分挖掘數(shù)據(jù)之間的關(guān)系,本題我們可以借助數(shù)學(xué)公式推導(dǎo):根據(jù)題意可知,三元組的分?jǐn)?shù)僅與x,z有關(guān),與y無關(guān).假設(shè)編號(hào)為x1,x2,x3的元素彼此間都符合題目要求的2個(gè)條件,則它們必定奇偶性相同、顏色相同。我們假設(shè)同顏色元素個(gè)數(shù)k=3,則其三元組分?jǐn)?shù)之和表示為: 因此,我們只需要找出每一種顏色下編號(hào)同為奇數(shù)三元組分?jǐn)?shù)之和、編號(hào)同為偶數(shù)三元組分?jǐn)?shù)之和即可。如表3所示。 充分挖掘問題中的數(shù)據(jù)關(guān)系,借助數(shù)學(xué)公式推導(dǎo),得出結(jié)論,并在結(jié)論基礎(chǔ)上從整體架構(gòu)、設(shè)計(jì)算法,從而提高算法性能,達(dá)到事半功倍的效果。 計(jì)算機(jī)的學(xué)習(xí)離不開數(shù)學(xué)理論與知識(shí)的學(xué)習(xí)和運(yùn)用,數(shù)學(xué)算法是計(jì)算機(jī)編程的基礎(chǔ),數(shù)學(xué)思想作為溝通問題與編程實(shí)現(xiàn)的一座橋梁,有著極其廣泛的應(yīng)用。它不僅可以直接為解題提供思路,如果與其他算法或者思想相結(jié)合,還能夠起到很好的輔助作用。通過數(shù)學(xué)模型的建立,利用數(shù)學(xué)算法對(duì)編程邏輯進(jìn)行分析設(shè)計(jì),不斷優(yōu)化數(shù)學(xué)算法,降低其時(shí)間復(fù)雜度、空間復(fù)雜度。在你覺得“山窮水復(fù)疑無路”時(shí),不妨用數(shù)學(xué)的角度重新審視問題,也許就會(huì)“柳暗花明又一村”。4 結(jié)束語