高斯消元法的时间复杂度计算


算法的时间复杂度通常用来反映程序执行时间随输入规模增长而增长的量级,在很大程度上能很好反映出算法的优劣与否。而度量程序的执行时间通常有两种方法,事后统计方法和事前分析估计方法。前者有两个缺陷:一是要想对设计的算法的运行性能进行评测,必须先依据算法编制相应的程序并实际运行;二是所得时间的统计量依赖于计算机的硬件、软件等环境因素,有时容易掩盖算法本身的优势。故通常都使用后一种方法,在编写程序前,依据统计方法对算法进行估算。一个用高级语言编写的程序在计算机上运行时所消耗的时间取决于下列因素:

  • 算法采用的策略、方法;
  • 编译产生的代码质量;
  • 问题的输入规模;
  • 机器执行指令的速度;

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)=3limn>+(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}^Nlog2N+1次,当i 2 i^2i2<N不成立时退出循环;循环变量一共是递增l o g 2 N {log_2}^Nlog2N次;故这一句的复杂度为 1 + (l o g 2 N {log_2}^Nlog2N+1)+l o g 2 N {log_2}^Nlog2N
  • 对于第二句,加法运算记为1,乘法运算一次,因外部有一层l o g 2 N {log_2}^Nlog2N次循环,故需要l o g 2 N {log_2}^Nlog2N次,即时间复杂度为 l o g 2 N {log_2}^Nlog2N

则这三句的时间复杂度合起来为 (1 + (l o g 2 N {log_2}^Nlog2N+1)+l o g 2 N {log_2}^Nlog2N)+ (l o g 2 N {log_2}^Nlog2N),保留最高项并去掉系数为l o g 2 N {log_2}^Nlog2N,即时间复杂度为l o g 2 N {log_2}^Nlog2N

示例四:

for(int sum=0,i = 0;i<N;i++)        //1、
    for(int j = 1;j < N;j = j*2)     //2、
        sum=sum+i*j;                //3、

分析:

  • 对于第一句,定义两个变量共一次,记为1;中间循环共判断N+1次,当i=N<N不成立时退出循环;循环变量一共是递增N次;故这一句的复杂度为 1 + (N+1)+N。
  • 对于第二句,定义一个变量共一次,记为1;中间循环共判断l o g 2 N {log_2}^Nlog2N+1次,当i 2 i^2i2<N不成立时退出循环;循环变量一共是递增l o g 2 N {log_2}^Nlog2N次;故这一句的复杂度为 1 + (l o g 2 N {log_2}^Nlog2N+1)+l o g 2 N {log_2}^Nlog2N。但因为外循环的存在,内循环共进来N次,故第二句的时间复杂度为N*(1 + (l o g 2 N {log_2}^Nlog2N+1)+l o g 2 N {log_2}^Nlog2N)。
  • 对于第三句,加法运算记为1,乘法运算一次,因外部有两层循环,故需要N*l o g 2 N {log_2}^Nlog2N次,即时间复杂度为 N*l o g 2 N {log_2}^Nlog2N

则这三句的时间复杂度合起来为 (1 + (N+1)+N)+(N*(1 + (l o g 2 N {log_2}^Nlog2N+1)+l o g 2 N {log_2}^Nlog2N))+ (N*l o g 2 N {log_2}^Nlog2N),保留最高项并去掉系数为N*l o g 2 N {log_2}^Nlog2N,即时间复杂度为N*l o g 2 N {log_2}^Nlog2N

示例五:


 for(i=0;i<n;i++)           //1、
    {                     
       for(j=0;j<i;j++)     //2、
       {
          for(k=0;k<j;k++)  //3、
             x=x+2;         //4、
       }
    }

分析:

  • 对于第一句,定义一个变量共一次,记为1;中间循环共判断N+1次,当i=N<N不成立时退出循环;循环变量一共是递增N次;故这一句的复杂度为 1 + (N+1)+N。

  • 对于第二句,定义一个变量共一次,记为1;中间循环共判断i+1次,当j=i<i不成立时退出循环;循环变量一共是递增i次;故这一句的复杂度为 1 + (i+1)+i。但因为外循环的存在,外循环共进来N次,故第二句的时间复杂度为 ( 1 + 1 + 0 ) + ( 1 + 2 + 1 ) + ( 1 + 3 + 2 ) + ( 1 + 4 + 3 ) + ( 1 + 5 + 4 ) + . . . + ( 1 + N + 1 + N ) = 1 ∗ N + ( 1 + ( N + 1 ) ) ∗ N 2 + ( 0 + N ) ∗ N 2 (1+1+0)+(1+2+1)+(1+3+2)+(1+4+3)+(1+5+4)+...+(1+N+1+N)=1*N+\frac{(1+(N+1))*N}{2}+\frac{(0+N)*N}{2}(1+1+0)+(1+2+1)+(1+3+2)+(1+4+3)+(1+5+4)+...+(1+N+1+N)=1N+2(1+(N+1))N+2(0+N)N ,第二句的复杂度为N ∗ N N*NNN

  • 对于第三句,定义一个变量共一次,记为1;中间循环共判断j+1次,当k=j<j不成立时退出循环;循环变量一共是递增j次;故这一句的复杂度为 1 + (k+1)+k。。因外部有两层循环,当第一外循环N次进入i=N-1时,有第二外循环j=i-1+1=N次j=i-1=N-1-1=N-2,此时内循环最大循环次数为k=j-1=N-2-1=N-3。此时有 ( 1 + 1 + 0 ) + ( 1 + 2 + 1 ) + ( 1 + 3 + 2 ) + ( 1 + 4 + 3 ) + ( 1 + 5 + 4 ) + . . . + ( 1 + ( N − 2 + 1 ) + ( N − 2 ) ) = 1 ∗ N + ( 1 + ( N − 1 ) ) ∗ ( N − 2 ) 2 + ( 0 + N − 2 ) ∗ ( N − 2 ) 2 (1+1+0)+(1+2+1)+(1+3+2)+(1+4+3)+(1+5+4)+...+(1+(N-2+1)+(N-2))=1*N+\frac{(1+(N-1))*(N-2)}{2}+\frac{(0+N-2)*(N-2)}{2}(1+1+0)+(1+2+1)+(1+3+2)+(1+4+3)+(1+5+4)+...+(1+(N2+1)+(N2))=1N+2(1+(N1))(N2)+2(0+N2)(N2), 再乘以外循环的N次得到第三句的复杂度为N ∗ ( 1 ∗ N + ( N ) ∗ ( N − 2 ) 2 + ( N − 2 ) ∗ ( N − 2 ) 2 ) N*(1*N+\frac{(N)*(N-2)}{2}+\frac{(N-2)*(N-2)}{2})N(1N+2(N)(N2)+2(N2)(N2))

  • 对于第四句,加法运算记为1,因外部有三层循环 N*(N-1)*(N-2),即时间复杂度为 N 3 2 \frac{N^3}{2}2N3

则这三句的时间复杂度合起来为 (1 + (N+1)+N)+(1 + (l o g 2 N {log_2}^Nlog2N+1)+l o g 2 N {log_2}^Nlog2N)+ (N*l o g 2 N {log_2}^Nlog2N),保留最高项并去掉系数为N*l o g 2 N {log_2}^Nlog2N,即时间复杂度为N*l o g 2 N {log_2}^Nlog2N

4、高斯消元法的时间复杂度

高斯消元法的步骤,假设等待进行消元的矩阵为一个 N×N 的方阵:

  • 第一行不变,不进行行交换
  • 用第一行乘以某个数加到第二行,以使得第一个主元(first pivot)下的数字为零,重复以上工作使得第三行在主元下的数字为零。最终目的使得第一列,除了第一行外,所有数字为零。
  • 类似第二步,只是保持第二行不变,通过第二行乘以一个数加到第三行,第四行 等等。最终目的使得第二主元以下数字为零。
  • 依次到第三行,第四行,最终达到高斯消元的目的。

算法复杂度分析:
首先看上面的第二步,因为是 N×N 的矩阵,所以每一行有 N个元素,N个元素乘以某个数加到第二行,我们认为产生了N次乘法运算,N次加法运算。因为一共有N-1行需要进行运算,所以完成第一主元以下消零的工作,一共要产生的乘法运算次数是 N×(N-1),加法运算同理。
再看上面第三步,我们保持第二行不变,来消除第二个主元下面所有的元素为零。由于第一行不参与运算,且第一主元下都是零了。我们可以认为矩阵被压缩成了 N-1的方阵。每处理一行,有N-1次乘法,N-1次加法。可是需要处理的行由上面的N-1行变成了N-2行。所以实现第二主元下面的数字为零的工作我们需要(N-1)×(N-2)乘法,加法同理。依次下来,我需要进行的运算次数是多少呢?只考虑乘法运算的情况下
S = N ( N − 1 ) + ( N − 1 ) ( N − 2 ) + . . . + 2 × 1 + 1 × 0 = ( N 2 − N ) + ( ( N − 1 ) 2 − ( N − 1 ) ) + . . . + ( 2 2 − 2 ) + ( 1 2 − 2 ) S=N(N-1)+(N-1)(N-2)+...+2×1+1×0=(N^2-N)+((N-1)^2-(N-1))+...+(2^2-2)+(1^2-2)S=N(N1)+(N1)(N2)+...+2×1+1×0=(N2N)+((N1)2(N1))+...+(222)+(122)
因为
N 2 + ( N − 1 ) 2 + . . . + 2 2 + 1 2 = N ( N + 1 2 ) ( N + 1 ) 3 N^2+(N-1)^2+...+2^2+1^2=\frac{N(N+\frac{1}{2})(N+1)}{3}N2+(N1)2+...+22+12=3N(N+21)(N+1)
N + ( N − 1 ) + ( N − 2 ) + . . . + 2 + 1 = ( 1 + N ) ∗ N 2 N+(N-1)+(N-2)+...+2+1=\frac{(1+N)*N}{2}N+(N1)+(N2)+...+2+1=2(1+N)N
因此 当N 很大的时候,乘法的个数为 N 3 3 \frac{N^3}{3}3N3 次,加法一样。

参考:
高斯-约旦消元法及其程序实现
高斯消元法复杂度计算
高斯消元和高斯约旦消元 Gauss(-Jordan) Elimination
Gaussian Elimination和Gauss-Jordan method
高斯消元解方程组
算法的时间复杂度和空间复杂度-总结
复杂度简单计算


版权声明:本文为weixin_50912862原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。