算法的優化
算法的優化分為全局優化和局部優化兩個層次。全局優化也稱為結構優化,主要是從基本控制結構優化、算法、數據結構的選擇上考慮;局部優化即為代碼優化,包括使用盡量小的數據類型、優化表達式、優化賦值語句、優化函數參數、全局變量及宏的使用等內容。
一、全局優化
1.優化算法設計
例如,在排序中用快速排序或者堆排序代替插入排序或冒泡排序;用較快的折半查找代替順序查找法等,都可以極大地提高程序的執行效率。
2.優化數據結構
例如在一堆隨機存放的數中使用了大量的插入和刪除指令,那么使用鏈表要快得多。數組和指針具有十分密切的關系,一般來說,指針比較靈活簡潔,而數組比較直觀,容易理解。在許多情況下,可以用指針運算代替數組,這樣做常常能產生又快又短的代碼,並且運行速度快,占用空間少。
3.優化選擇結構
①嵌套if語句的使用。當if結構中要判斷的並列條件較多的時候,最好將它們拆分成多個if語句結構,然后嵌套在一起,就可以減少不必要的判斷。
②嵌套switch語句的使用。switch語句中的case很多時,為了減少比較次數,可以把大switch語句轉化為嵌套switch語句。把頻率較高的case標號放在一個switch語句中,而發生頻率較低的case標號則放到另一個switch語句當中。
③給switch語句中的case排序。根據發生的可能性對case的值排序,最有可能的放在第一位就可以使選擇過程更合理,從而提高了效率。
4.優化循環結構
提高程序效率的核心是對影響代碼執行速度的關鍵程序段進行優化。在任何程序中,最影響代碼速度的往往是循環結構,特別是多層嵌套的循環結構。掌握循環優化的的各種實用技術是提高程序效率的關鍵。
常用的循環優化技術如下:
①降價策略。通過算法分析我們知道算法的事件復雜度主要是由循環嵌套的層數決定的,所以,算法中如果能夠減少循環嵌套的層數,如將雙重循環改寫成單循環,則從事件復雜度上可達到降價的目的。
②加速原理。是指將循環體內的選擇結構去掉,則加速循環結構的執行效率。
③代碼外提。是指將循環體中與循環變量無關的運算提出,並將其放到循環之外,以避免每次循環過程中的重復操作。
④變換循環控制條件。當某循環變量在循環體中除自身引用外,已不再控制循環過程時,可以將其從循環中刪去。
⑤合並循環。把兩個或兩個以上的循環合並放到一個循環里,這樣會加快速度。使用循環雖然簡單,但是使用不當,往往可能帶來很大的性能影響。原則是將問題充分分解為小的循環,不在循環內的多余的工作(如賦值、常量計算等),避免死循環。還可以考慮將循環改為非循環來提高效率。
二、局部優化
1.使用盡量小的數據類型
能夠使用字符型(char)定義的變量,就不要使用整型(int)變量來定義;能夠使用整型變量定義的變量就不要使用長整型(long int),能不使用浮點型(float)就不要使用浮點型變量。當然,在定義變量后不要超過變量的作用范圍,如果超過變量的范圍賦值,C編譯器並不報錯,但程序運行結果卻錯了,而且這樣的錯誤很難發現。
2.優化表達式
對於一個表達式中各種運算執行的優先順序不太明確或容易混淆的地方,應當采用圓括號明確指定他們的優先順序。一個表達式通常不能寫的太復雜,如果表達式太復雜,時間久了,自己也不容易看得懂,不利於以后的維護。
3.使用自增、自減和復合賦值運算符
通常使用自增、自減運算符和復合賦值表達式(如a-=1及a+=1等)都能夠生成高質量的程序代碼,編譯器通常都能夠生成inc和dec之類的指令,而使用a=a+1或a=a-1之類的指令,有很多C編譯器都會生成二到三個字節的指令。
4.使用運算量較小(但功能相同)的表達式代替復雜的表達式可以減少運算的強度。例如平方運算:a=pow(b,2.0)優化為:a=b*b。
5.避免浮點運算。C語言中的浮點型float和雙精度型double運算比短整型、整型、長整型運算要慢得多,因此避免浮點運算就非常有必要。
6.優化賦值語句。在代碼中,若一個變量經賦值后在后面語句的執行過程中不再引用,則這一賦值語句就稱為無用賦值,可以不用。當賦值語句中出現多個已知量的運算時,可以將其合並成一個值,減少程序執行過程中重復計算的工作量。
7.優化函數參數。在C語言中,調用函數的第一步是傳遞參數給寄存器或堆棧。當函數的參數很多時,就要調用大量的堆棧空間,開銷會很大。當結構作為函數參數傳遞的內容時,編譯器的第一步操作是把整個結構復制到堆棧,這種情況下堆棧空間的使用會非常大。此外,如果結構作為函數返回值,調用程序會把堆棧空間保留,把結構地址傳遞給函數同時調用函數,接着把函數返回。最后,調用程序需要把堆棧空間清除,並把返回的結構拷貝到第二個結構當中。這樣代碼和堆棧的開銷就會非常驚人。因此應禁止傳遞結構,一般用結構指針作為函數的參數,來避免這種開銷。
8.宏的使用
在程序設計過程中,對於經常使用的一些常數,如果將它直接寫到程序中去,一旦常數的數值發生變化,就必須逐個找出程序中所有的常數,並逐一進行修改,這樣必然會降低程序的可維護性。因此,應該采用預處理命令中的宏定義,而且還可以避免輸入錯誤。
宏定義除了一些大家所熟知的好處外,如可以提高程序的清晰性、可讀性,便於修改移植等,還有一個很妙的地方:利用宏定義來代替函數可以提高程序設計的效率。
算法優化中的注意事項:
1.程序的優化以不破壞程序的可讀性、可理解性為原則。
2.如果將程序的執行效率納入軟件的整個生命周期來考慮,為提高單個程序的效率而花費大量的開發時間往往得不償失,在下列情況下,程序的優化才有意義。
①首先保證程序的正確性和健壯性,然后才考慮優化。
②嚴重影響效率的程序才值得優化。例如系統反復調用的核心模塊,無關大局的模塊沒有優化的價值。
摘自《算法設計方法與優化》