如何评估算法的性能?

155 阅读3分钟

复杂度分析是用来分析算法在时间和空间维度的消耗趋势。

1. 为什么要做复杂度分析

因为现实中我们所拥有的设备资源和时间都是有限的,程序的运行亦是如此,我们希望消耗更小的硬件和时间去完成某项功能,我们也需要为运行一段程序去提供其所需要的资源,以及评估运行的时间是否可以接受。

仅通过跑一下程序来统计时间、空间的消耗不可以吗?

确实,跑一下程序能够得到一些实际的消耗数据,但是:

(1) 这需要先把程序实现出来,以及可执行环境,成本较高。而复杂度分析基于伪代码即可。

(2) 实际运行受测试数据样本、规模,设备性能,以及当时CPU、内存运行等诸多情况影响,得到的结果可能比较片面。

2. 复杂度的分类

2.1 渐进时间复杂度

简称"时间复杂度",用来表示随着数据规模的增加,算法执行所消耗的时间的变化趋势。

设数据规模为n, 代码执行时间为T(n), 代码行执行次数为f(n), 每行代码执行时间为unit_time,

则T(n)=f(n)*unit_time.

T(n)与代码行执行的次数成正比,记作T(n)=O(f(n))。

这就是大O表示法,f(n)中的低阶、常量、系数三部分并不左右增长趋势,所以都可以忽略。我们只需要记录一个最大量级就可以了。

2.1.1 分析时间复杂度的基本规则

只关注受数据规模影响的代码块。

只关注循环执行次数最多的一段代码,分析其执行次数和n的关系。

加法规则

如果T1(n)=O(f(n)), T2(n)=O(g(n)), T(n)=T1(n)+T2(n).

则T(n)=MAX(T1(n), T2(n))=MAX(O(f(n)), O(g(n)))=O(MAX(f(n), MAX(g(n)))

乘法规则

设T1(n)=O(f(n)), T2(n)=O(g(n)), T(n)=T1(n)*T2(n).

则T(n)=O(f(n))*O(g(n))=O(f(n)*g(n)).

忽略常数项,只关注最高阶项

2.1.2 常见时间复杂度

O(1), O(logn), O(n), O(nlogn), O(n²)

常见时间复杂度.webp 图1. 常见时间复杂度

常见时间复杂度趋势图.webp 图2. 常见时间复杂度趋势图

分析方法

最好情况时间复杂度

最坏情况复杂度

平均情况时间复杂度

均摊时间复杂度

2.2 渐进空间复杂度

简称“空间复杂度”,表示算法所需存储空间随着数据规模增长的关系。

常见的空间复杂度

有O(1)、O(n)、O(n²),而O(logn)、O(nlogn) 这样的对数阶复杂度平时都用不到。

3. 问题

那么我们是不是可以简单粗暴地说:在任何情况下,我们都应该选时间复杂度更低的算法呢?