算法分析基础

数学基础

定义

如果存在正常数c与n0,使得当N >= n0时T(N) <= cf(N),则记为$T(N)= O(f(N))$。

如果存在正常数c与n0, 使得当N >= n0时T(N) >= cg(N),则记为$T(N) = \Omega(g(N))$。

当且仅当T(N) = O(h(N))且T(N) = Ω(h(N))时,则$T(N) = \Theta(h(N))$。

如果T(N) = O(p(N))且T(N) != Θ(p(N)),则$T(N) = o(p(N))$。

 

法则

如果T1(N) = $O(f(N))$且T2(N) = $O(g(N))$,那么:

(a)T1(N) + T2(N) = $max(O(f(N)), O(g(N)))$

(b)T1(N) * T2(N) = $O(f(N) * g(N))$

 

如果T(N)是一个k次多项式,则$T(N) = \Theta(N^k)$

 

对于任意常数k,$\log^k{N} = O(N)$

 

函数增长率

典型增长率

 

函数 名称
c 常数
$\log{N}$ 对数级
$\log^2{N}$ 对数平方根
N 线性级
$N\log{N}$  
$N^2$ 平方级
$N^3$ 立方级
$2^N$ 指数级

 

 

判断两个函数的增长率

可以通过比较两个函数的极限:$\lim\limits_{n \to \infty}\frac{f(N)}{g(N)}$,来确定两个函数的增长率。必要的时候,还可以使用洛必达法则。

该极限有4种可能:

1 极限是0,这意味着$f(N) = o(g(N))$;

2 极限是常数c != 0,这意味着$f(N) = \Theta(g(N))$;

3 极限是$\infty$,这意味着$g(N) = o(f(N))$;

4 极限摆动,二者无关

 

运行时间计算的一般法则

for循环

一次for循环的运行时间至多是for循环内语句(包括测试)的运行时间乘以迭代得次数

 

for (i = 0; i < N; i++) {
    for (j = 0; j < N; j++) {
        k++;
    }
}

 上面的例子当中,时间复杂度是$N^2$。

 

 

顺序语句

将各个语句的运行时间求和即可(这意味着,其中的最大值就是所得得运行时间)

for (i = 0; i < N; i++) {
    A[i] = 0; 
}   

for (i = 0; i < N; i++) {
    for (j = 0; j < N; j++) {
        A[i] += A[j] + i + j;
    }
} 

 上面例子当中,第一个循环时间复杂度是$O(N)$,第二个循环时间复杂度是$O(N^2)$,两个循环的总体时间复杂度为$O(N) + O(N^2) = max(O(N), O(N^2))$,也就是还是$O(N^2)$。

 

 if/else语句

对于程序片段:

if (condition) {
    S1;
} else {
    S2;
}

一个if/else语句得运行时间从不超过判断再加上S1和S2中运行时间长的运行时间。

 

运行时间中的对数

如果一个算法用常数时间O(1)将问题的大小削减为其一部分(通常是1/2),那么该算法得时间复杂度就是$O(\log{N})$。举个例子,下面是用迭代的方法写的二分查找:

int BinarySearch(const ElementType A[], ElementType X, int N) {
    int Low, Mid, High;

    Low = 0;
    High = N - 1;

    while (Low <= High) {
        Mid = (Low + High) / 2;
        
        if (A[Mid] < X) {
            Low = Mid + 1;
        } else {
            if (A[Mid] > X) {
                High = Mid - 1;
            } else {
                return Mid;
            }
        }

     }
      return NotFound;
}

在上面的二分法中,每次迭代的时间复杂度是$O(1)$,并且每次迭代之后,问题复杂度就减为原来的一半,因此,这个程序的时间复杂度为$O(\log{N})$。

另一方面,如果使用常数时间只是把问题减少一个常数(如将问题减少1),那么这种算法就是$O(N)$。举个例子,下面使用递归的方式求解N!:

long int Factorial(int N) {
    if (N <= 1) {
        return 1;
    } else {
        return N * Factorial(N - 1);
    }
}

在上面的递归中,每次递归的时间复杂度是$O(1)$,并且递归之后,问题复杂度只比原来少1,因此,这个程序的时间复杂度就是$O(N)$。

 

递归时间复杂度总结

对于一个问题复杂度为T(N)的问题,可以分解为如下的表达式:

$T(N) = aT(\frac{N}{b}) + f(N)$,其中a是常数,且a >= 1, b也是常数,b > 1。

1 如果存在正常数$\varepsilon > 0$令$f(N) = O(N^{\log_{b}{a} - \varepsilon})$,那么$T(N) = \Theta(N^{\log_{b}{a}})$;

2 如果$f(N) = \Theta(N^{\log_{b}^{a}})$,那么$T(N) = \Theta(N^{\log_{b}{a}}\log{N})$;

3 如果存在正常数$\varepsilon > 0$使得$f(n) = \Omega(N^{{\log_{b}{a}} + \epsilon})$且存在常数c < 1使得对于足够大的N满足$af(\frac{N}{b}) \leq cf(N)$,那么$T(N) = \Theta(f(N))$.

 

这三种情形如何记忆呢?我们可以这样记忆,三种情况的结果取决于函数f(N)与$N^{\log_{b}^{a}}$的大小。如果$N^{\log_{b}^{a}}$大,那么结果就是情形1;如果是f(N)大,那么结果就是情形3;如果两个同级别,那么就是情形2。

还需要注意的事情是,这三种情形没有覆盖所有的情形。情形1与情形2之间有Gap,如果f(N)比$N^{\log_{b}{a}}$小,但是如果不是同时小一个$N^\epsilon$因子,就落到情形1与情形2之间;同时,情形2与情形3之间也有Gap,如果f(N)比$N^{\log_{b}{a}}$大,但是如果不同时大一个$N^\epsilon$因子,就落到情形2与情形3之间。如果发生这两种情形,就无法应用上面的三个公式。

 

下面我们看一下实际的例子。

1 $T(N) = 9T(\frac{N}{3}) + N$

其中a = 9, b = 3, f(N) = N,$N^{\log_{b}{a} } = N^{\log_{3}{9}} = \Theta(N^2)$,并且$f(N) = O(N^{\log_{3}{9 - \varepsilon}})$,其中$\varepsilon = 1$,那么这个满足情形1,即$T(N) = \Theta(N^{\log_{3}{9}}) = \Theta(N^2)$。

 

2 $T(N) = T(\frac{2}{3}N) + 1$

其中a = 1,b = $\frac{3}{2}$,f(N) = 1,同时$N^{\log_\frac{3}{2}{1}} = N^0 = 1$,那么这就满足情形2,即$T(N) = \Theta(N^{\log_\frac{3}{2}{1}}\log{N}) = \Theta(\log{N})$。这其实就是上面运行时间中的对数当中得情形1。

 

3 $T(N) = 3T(\frac{N}{4}) + N\log{N}$

其中a = 3,b = 4,f(N) = $N\log{N}$,$N^{\log_{b}{a}} = N^{\log_{4}{3}} = \Omega(N^{0.793})$。因为$f(N) = \Omega(N^{\log_{4}{3} + \varepsilon})$  $\varepsilon \cong 0.2$,同时对于足够大的N,满足$af(\frac{N}{b}) = 3(\frac{N}{4})\log\frac{N}{4} \leq \frac{3}{4}N\log{N} = cf(N)$,其中$c = \frac{3}{4}$。这就满足了情形3,即$T(N) = \Theta(N\log{N})$。

 

4 $T(N) = 2T(\frac{N}{2}) + N\log{N}$

其中a = 2,b = 2,$f(N) = N\log{N}$, $N^{\log_{b}{a}} = N^{\log_{2}{2}} = N$,表面上看,这属于情形3,但是,却不存在一个正常数$\varepsilon$,使得$\frac{f(N)}{N^{\log_{b}{a} + \varepsilon}} = \frac{N\log{N}}{N^{\log_{2}{2} + \varepsilon}} = \frac{N\log{N}}{N * N^\varepsilon} = \frac{\log{N}}{N^\varepsilon} \ge 1$。所以无法运用上面3中情形的公式。

 

posted @ 2020-06-28 00:18  chaoguo1234  阅读(207)  评论(0编辑  收藏  举报