文章目录
0.复杂度度量
Mathematics is more in need of good notations than of new theorems.
–Alan Turing
与新定理相比,数学更需要好的符号。
–艾伦 ⋅ \cdot ⋅图灵
算法的复杂度存在于时间与空间两个维度,分别体现在随输入规模增加,消耗的时间的增加与占用计算机物理内存空间的增加。
0.1 时间复杂度
执行时间随规模增长的变化趋势可以表示为输入规模的一个函数,称为该算法的时间复杂度(Time complexity),记位
T
(
n
)
T(n)
T(n)。
由于大规模的问题更能放大效率差异带来的效果,所以我们忽略解决小规模问题时的能力差异,着眼于处理更大规模问题的时候的处理时间。
这种着眼长远、更为注重时间复杂度的总体变化趋势和增长速度的策略与方法,即“渐进分析(asymptotic analysis)”。
0.1.1 大O记号
大O记号是时间复杂度
T
(
n
)
T(n)
T(n)的渐进上界,一般用于描述时间复杂度。其定义为对于任何
n
>
>
2
n>>2
n>>2都有
T
(
n
)
≤
c
⋅
f
(
n
)
T(n)\leq c\cdot f(n)
T(n)≤c⋅f(n)
可认为n足够大时,
f
(
n
)
f(n)
f(n)给出
T
(
n
)
T(n)
T(n)的增长上界。大O记号可以记为
T
(
n
)
=
O
(
f
(
n
)
)
T(n)= O\big(f(n)\big)
T(n)=O(f(n))
大O记号有以下性质:
1.对于任一常数
c
>
0
c>0
c>0,有
O
(
f
(
n
)
)
=
O
(
c
⋅
f
(
n
)
)
O\big(f(n)\big)=O\big(c\cdot f(n)\big)
O(f(n))=O(c⋅f(n))
2.对任意常数
a
>
b
>
0
a>b>0
a>b>0,有
O
(
n
a
+
n
b
)
=
O
(
n
a
)
O\big(n^a+n^b \big)=O\big(n^a\big)
O(na+nb)=O(na)
0.1.2 大Ω ,大Θ记号
类似的,大Ω ,大Θ记号也可描述时间复杂度。大Ω 一般描述时间复杂度
T
(
n
)
T(n)
T(n)的渐进下界,大Θ一般描述时间复杂度
T
(
n
)
T(n)
T(n)的平均水平。
图
1
大
O
,
大
Ω
,
大
Θ
记
号
图
示
图1 \quad 大O,大Ω ,大Θ记号图示
图1大O,大Ω,大Θ记号图示
0.2 空间复杂度
空间复杂度是指算法所需存储空间的多少,是衡量算法性能的另一个方面。也由大O,大Ω ,大Θ记号表示。但是,就渐进复杂度而言,在任一算法的任何一次运行过程中所消耗的存储空间,都不会多于其间所执行基本操作的时间。
1.递归函数复杂度分析–以fib()为例
斐波拉契数列的二分递归实现**fib_I(int n)**程序如下,这是一种典型的多递归基的二分递归。
/*版本I:__int64 fib_I(int n)*/
//递推
__int64 fib_I(int n)//O(2^n)
{
return (2 > n) ? (__int64)n : fib_I(n - 1) + fib_I(n - 2);
}
1.1递归跟踪
递归跟踪分析法是通过跟踪每个递归实例的创建、执行和销毁,来确定整个算法的计算时间。 其中,递归实例的创建与销毁全都由操作系统负责完成,对应的时间成本近似为常数,不会超过递归实例实际计算中所需的时间成本,往往予忽略。
使用递归分析法对**fib_I(int n)**进行分析:
- 1.对于形如fib(k)的递归实例,算法的执行过程中会先后重复出现fib(n-k+1)次,由数学归纳法可容易得出;
- 2.该算法的每个递归实例自身消耗常数时间,故整体运行时间正比于递归实例的总数
∑ k = 0 n f i b ( n − k + 1 ) = ∑ k = 1 n + 1 f i b ( k ) = f i b ( n + 3 ) − 1 = O ( ϕ n ) = O ( 2 n ) \sum^{n}_{k= 0}fib(n-k+1)=\sum^{n+1}_{k= 1}fib(k)=fib(n+3)-1=O(\phi^n)=O( 2^n) k=0∑nfib(n−k+1)=k=1∑n+1fib(k)=fib(n+3)−1=O(ϕn)=O(2n)
其中, ϕ = ( 1 + 5 ) / 2 = 1.618 \phi=(1+\sqrt 5)/2=1.618 ϕ=(1+5)/2=1.618
所以,以上算法使用递归跟踪得出的时间复杂度为 O ( 2 n ) O(2^n) O(2n)。由于该算法的递归深度不超过n,所以该算法需要空间不超过 O ( n ) O(n) O(n)。
1.2递归方程
递归方程法通过对递归模式进行数学归纳,导出复杂度定界函数得到递推方程(组)及边界条件,将复杂度的分析转化为递推方程(组)的求解。
使用递归方程法对**fib_I(int n)**进行分析:
- 1
T
(
n
)
=
{
n
,
n
≤
1
T
(
n
)
=
T
(
n
−
1
)
+
T
(
n
−
2
)
+
1
,
e
l
s
e
T(n)=\left\{ \begin{matrix} n,n\leq 1\\ T(n)= T(n-1)+T(n-2)+1 ,else \end{matrix} \right.
T(n)={n,n≤1T(n)=T(n−1)+T(n−2)+1,else
令 S ( n ) = [ T ( n ) + 1 ] / 2 S(n)=[T(n)+1]/2 S(n)=[T(n)+1]/2,可得
S ( n ) = { n , n ≤ 1 S ( n ) = S ( n − 1 ) + S ( n − 2 ) , e l s e S(n)=\left\{ \begin{matrix} n,n\leq 1\\ S(n)= S(n-1)+S(n-2) ,else \end{matrix} \right. S(n)={n,n≤1S(n)=S(n−1)+S(n−2),else - 2. S ( n ) S(n) S(n)与 f i b ( n ) fib(n) fib(n)形式一模一样,只是起始项不同,S(n)相对于fib(n)提前了一个单元。 S ( n ) = f i b ( n + 1 ) S(n)=fib(n+1) S(n)=fib(n+1)。
- 3.
T
(
n
)
=
2
⋅
S
(
n
)
−
1
=
2
∗
f
i
b
(
n
+
1
)
−
1
=
O
(
ϕ
n
)
=
O
(
2
n
)
T(n)=2\cdot S(n)-1=2*fib(n+1)-1=O(\phi^n) =O( 2^n)
T(n)=2⋅S(n)−1=2∗fib(n+1)−1=O(ϕn)=O(2n)
所以,以上算法使用递归方程得出的时间复杂度为 O ( 2 n ) O(2^n) O(2n)。由于该算法的递归深度不超过n,所以该算法需要空间不超过 O ( n ) O(n) O(n)。时间复杂度与空间复杂度的计算结果与递归跟踪法得出的一样。
2.fib函数的改进
2.1 线性递归法
第一种关于
f
i
b
(
)
fib()
fib()的改进是使用线性递归,使用 “记忆” 的方式进行递归,避免如此多递归实例的创建。prev初值为
f
i
b
(
−
1
)
=
1
fib(-1)=1
fib(−1)=1,prevPrev初值为
f
i
b
(
0
)
=
0
fib(0)=0
fib(0)=0,由此可实现自上而下的记忆递归,不必多次调用相同的实例。
算法的时间复杂度正比于创建的递归实例数,即为
O
(
n
)
O(n)
O(n);由于递归深度仍然为
n
n
n,空间复杂度为
O
(
n
)
O(n)
O(n)。
/*版本II:__int64 fib_II(int n)*/
//记忆递归
__int64 fib_II(int n,__int64& prev)//O(n)
{
if(0==n)
{
prev = 1;
return 0; //fib_II(-1,prev)=1;fib_II(0,prev)=0;
}
else
{
__int64 prevPrev;
prev=fib_II( n-1,prevPrev);//fib_II(1,prev)->fib_II(0,prevPrev)->[prevPrev=1],fib_(0,prevPrev)返回值0->fib_II(1,prev)返回值0+1
return prev + prevPrev;
}
}
2.2 迭代法
第二种关于
f
i
b
(
)
fib()
fib()的改进为迭代的方法,套用动态规划的策略,对f和g进行初始化,之后逐个迭代。
该算法时间复杂度为
O
(
n
)
O(n)
O(n)。由于除了g、f,不占用额外的空间,空间复杂度为
O
(
1
)
O(1)
O(1)。
/*版本III:__int64 fib_III(int n)*/
//迭代
__int64 fib_III(int n)//O(n)
{
int f = 0;
int g = 1;
while(n>0)
{
g = g + f;
f = g - f;
n--;
}
return f;
}
3.总结
本文首先介绍了衡量算法性能的尺度——时间复杂度与空间复杂度,及其表示方式。
以计算斐波那契数列的函数fib()为例,分别使用递归跟踪与递归方程法计算其复杂度,时间复杂度为
O
(
2
n
)
O(2^n)
O(2n),空间复杂度为
O
(
n
)
O(n)
O(n)。
最后对fib()进行改进,使用线性递归的改进实例时间复杂度降为
O
(
n
)
O(n)
O(n),空间复杂度为
O
(
n
)
O(n)
O(n);使用迭代的改进实例空间复杂度为
O
(
n
)
O(n)
O(n),空间复杂度降为
O
(
1
)
O(1)
O(1)。