高斯消元法的时间复杂度计算
算法的时间复杂度通常用来反映程序执行时间随输入规模增长而增长的量级,在很大程度上能很好反映出算法的优劣与否。而度量程序的执行时间通常有两种方法,事后统计方法和事前分析估计方法。前者有两个缺陷:一是要想对设计的算法的运行性能进行评测,必须先依据算法编制相应的程序并实际运行;二是所得时间的统计量依赖于计算机的硬件、软件等环境因素,有时容易掩盖算法本身的优势。故通常都使用后一种方法,在编写程序前,依据统计方法对算法进行估算。一个用高级语言编写的程序在计算机上运行时所消耗的时间取决于下列因素:
- 算法采用的策略、方法;
- 编译产生的代码质量;
- 问题的输入规模;
- 机器执行指令的速度;
1、时间复杂度定义
一个算法中的语句执行次数称为语句频度或时间频度,记为T(n)。当问题的规模n不断变化时,时间频度T(n)也会不断变化。一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。比如 3 n 3 + n = O ( n 3 ) 3n^3+n=O(n^3) 3n3+n=O(n3),因为 当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数。
l i m n − > + ∞ ( 3 n 3 + n ) / ( n 3 ) = l i m n − > + ∞ ( 3 + 1 / ( n 2 ) = 3 lim_{n->+∞}(3n^3+n)/(n^3)=lim_{n->+∞}(3+1/(n^2)=3 limn−>+∞(3n3+n)/(n3)=limn−>+∞(3+1/(n2)=3
算法通常是由控制结构(顺序、分支和循环3种)和原操作(指固有数据类型的操作)构成的,故算法时间取决于两者的综合效果。为了便于比较同一个问题的不同算法,通常的做法是,从算法中选取一种对于所研究的问题(或算法类型)来说是基本操作的原操作,以该基本操作的重复执行的次数作为算法的时间量度。
2、几种常见复杂度执行效率的比较
在各种不同算法中,若算法中语句执行次数为一个常数,则时间复杂度为O(1),另外,在时间频度不相同时,时间复杂度有可能相同,如T(n)=n2+3n+4与T(n)=4n2+2n+1它们的频度不同,但时间复杂度相同,都为O(n2)。 按数量级递增排列,常见的时间复杂度有:常数阶O(1),对数阶O(log2n),线性阶O(n), 线性对数阶O(nlog2n),平方阶O(n2),立方阶O(n3),…, k次方阶O(nk),指数阶O(2n)。随着问题规模n的不断增大,上述时间复杂度不断增大,算法的执行效率越低。
常见的算法时间复杂度由小到大依次为: O ( 1 ) < O ( l o g 2 n ) < O ( n ) < O ( n l o g 2 n ) < O ( n 2 ) < O ( n 3 ) < O ( 2 n ) < O ( n ! ) < O ( n n ) Ο(1)<Ο(log_2^n)<Ο(n)<Ο(nlog_2^n)<Ο(n^2)<Ο(n^3)<Ο(2^n)<Ο(n!)<Ο(n^n) O(1)<O(log2n)<O(n)<O(nlog2n)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn)。故我们应该尽可能选用多项式阶O(nk)的算法,而不希望用指数阶的算法。Ο(1)表示基本语句的执行次数是一个常数,一般来说,只要算法中不存在循环语句,其时间复杂度就是Ο(1)。其中 O ( l o g 2 n ) Ο(log_2^n) O(log2n)、 O ( n ) Ο(n) O(n)、 O ( n l o g 2 n ) Ο(nlog_2^n) O(nlog2n)、 O ( n 2 ) Ο(n^2) O(n2)和 O ( n 3 ) Ο(n^3) O(n3)称为多项式时间,而 O ( 2 n ) Ο(2^n) O(2n)和 O ( n ! ) Ο(n!) O(n!)称为指数时间。计算机科学家普遍认为前者(即多项式时间复杂度的算法)是有效算法,把这类问题称为P(Polynomial,多项式)类问题,而把后者(即指数时间复杂度的算法)称为NP(Non-Deterministic Polynomial, 非确定多项式)问题。一般来说多项式级的复杂度是可以接受的,很多问题都有多项式级的解——也就是说,这样的问题,对于一个规模是n的输入,在n^k的时间内得到结果,称为P问题。有些问题要复杂些,没有多项式时间的解,但是可以在多项式时间里验证某个猜测是不是正确。大数分解、Hamilton回路之类的问题,都是可以多项式时间内验证一个“解”是否正确,这类问题叫做NP问题。
3、时间复杂度的计算
求解算法的时间复杂度的具体步骤是:
⑴ 找出算法中的基本语句;
算法中执行次数最多的那条语句就是基本语句,通常是最内层循环的循环体。
⑵ 计算基本语句的执行次数的数量级;
只需计算基本语句执行次数的数量级,这就意味着只要保证基本语句执行次数的函数中的最高次幂正确即可,可以忽略所有低次幂和最高次幂的系数。这样能够简化算法分析,并且使注意力集中在最重要的一点上:增长率。
⑶ 用大Ο记号表示算法的时间性能。
将基本语句执行次数的数量级放入大Ο记号中。
如果算法中包含嵌套的循环,则基本语句通常是最内层的循环体,如果算法中包含并列的循环,则将并列循环的时间复杂度相加。主体思路是:用常数1替代语句频度中所有的加法项, 且只保留数的最高项,如若最高阶项若系数不为1,则去除系数(或是改为1)。
示例一:
Temp=i; //1、
i=j; //2、
j=temp; //3、
分析:
以上三条单个语句的频度均为1,该程序段的执行时间是一个与问题规模n无关的常数。算法的时间复杂度为常数阶,记作T(n)=O(1)。从这里我们可以看出,如果算法的执行时间不随着问题规模n的增加而增长,即使算法中有上千条语句,其执行时间也不过是一个较大的常数。此类算法的时间复杂度是O(1)。
示例二:
for(int sum=0,i = 0;i < N;i++) //1、
for(int j = 0;j <N;j++) //2、
sum=sum+i*j; //3、
分析:
- 对于第一句,定义两个变量共一次,记为1;中间循环共判断N+1次,当i=N<N不成立时退出循环;循环变量一共是递增N次;故这一句的复杂度为 1 + (N+1)+N。
- 对于第二句,定义一个变量共一次,记为1;中间循环共判断N+1次,当j=N<N不成立时退出循环;循环变量一共是递增N次;故单就这一句的复杂度为 1 + (N+1)+N,但因为外循环的存在,内循环共进来N次,故第二句的时间复杂度为 N( 1 + (N+1)+N)=N+N(N+1)+NN。
- 对于第三句,加法运算记为1,乘法运算一次,因外部有两层N次循环,故需要N*N次,即时间复杂度为 NN。
则这三句的时间复杂度合起来为 ( 1 + (N+1)+N)+(N+N(N+1)+NN)+ (NN),保留最高项并去掉系数为NN,即时间复杂度为 O ( N 2 ) O(N^2) O(N2)。
示例三:
int j = 0;
for(int sum = 0,i = 1;i < N;i = i*2) //1、
sum = sum+i*j; //2、
分析:
- 对于第一句,定义两个变量共一次,记为1;中间循环共判断 l o g 2 N {log_2}^N log2