算法的优化
算法的优化分为全局优化和局部优化两个层次。全局优化也称为结构优化,主要是从基本控制结构优化、算法、数据结构的选择上考虑;局部优化即为代码优化,包括使用尽量小的数据类型、优化表达式、优化赋值语句、优化函数参数、全局变量及宏的使用等内容。
一、全局优化
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.如果将程序的执行效率纳入软件的整个生命周期来考虑,为提高单个程序的效率而花费大量的开发时间往往得不偿失,在下列情况下,程序的优化才有意义。
①首先保证程序的正确性和健壮性,然后才考虑优化。
②严重影响效率的程序才值得优化。例如系统反复调用的核心模块,无关大局的模块没有优化的价值。
摘自《算法设计方法与优化》