在我们先前关于算法分析的文章中,我们已经简要讨论了渐近符号,它们的最坏和最佳情况性能等。在本文中,我们将详细讨论使用Big-O渐近符号表示的算法分析。
算法的大O分析
我们可以使用big-O表示法来表达算法复杂性。对于大小为N的问题:
- 恒定时间函数/方法为“阶数1”:O(1)
- 线性时间函数/方法为“阶N”:O(N)
- 二次函数/方法为“ N阶平方”:O(N 2)
定义:令g和f是从自然数到其本身的函数。如果存在一个常数c> 0和一个自然数n 0 ,使得对于所有n> = f(n)≤cg(n),则函数f被称为O(g)(g的big-oh)。 n 0 。
注意:O(g)是一个集合!
滥用表示法:f = O(g)并不意味着f∈O(g)。
Big-O渐近记号为我们提供了上限概念,其数学描述如下:
f(n) = O(g(n)) if there exists a positive integer n0 and a positive constant c, such that f(n)≤c.g(n) ∀ n≥n0
Big-O运行时分析的一般分步过程如下:
- 弄清楚输入是什么,n代表什么。
- 表示最大操作数,该算法以n表示。
- 消除所有不包括最高订购条款的信息。
- 删除所有恒定因素。
Big-O表示法分析的一些有用属性如下:
▪ Constant Multiplication:
If f(n) = c.g(n), then O(f(n)) = O(g(n)) ; where c is a nonzero constant.
▪ Polynomial Function:
If f(n) = a0 + a1.n + a2.n2 + —- + am.nm, then O(f(n)) = O(nm).
▪ Summation Function:
If f(n) = f1(n) + f2(n) + —- + fm(n) and fi(n)≤fi+1(n) ∀ i=1, 2, —-, m,
then O(f(n)) = O(max(f1(n), f2(n), —-, fm(n))).
▪ Logarithmic Function:
If f(n) = logan and g(n)=logbn, then O(f(n))=O(g(n))
; all log functions grow in the same manner in terms of Big-O.
基本上,这种渐近符号用于从理论上测量和比较算法的最坏情况。对于任何算法,只要我们正确地识别依赖于输入大小n的运算,Big-O分析就应该很简单。
算法的运行时分析
在一般情况下,我们主要用于测量和比较算法的最坏情况理论运行时间复杂度,以进行性能分析。
对于任何算法,最快的运行时间是O(1),通常称为“恒定运行时间” 。在这种情况下,无论输入大小如何,算法总是需要花费相同的时间来执行。对于算法来说,这是理想的运行时,但是很少能实现。
在实际情况下,算法的性能(运行时)取决于n,即输入的大小或每个输入项需要的操作数。
从最佳性能到最差性能(运行时间复杂度),可以将算法分类如下:
▪ A logarithmic algorithm – O(logn)
Runtime grows logarithmically in proportion to n.
▪ A linear algorithm – O(n)
Runtime grows directly in proportion to n.
▪ A superlinear algorithm – O(nlogn)
Runtime grows in proportion to n.
▪ A polynomial algorithm – O(nc)
Runtime grows quicker than previous all based on n.
▪ A exponential algorithm – O(cn)
Runtime grows even faster than polynomial algorithm based on n.
▪ A factorial algorithm – O(n!)
Runtime grows the fastest and becomes quickly unusable for even
small values of n.
其中,n是输入大小,c是一个正常数。
运行时分析的算法示例:
下面提到所有这些类型的算法的一些示例(在最坏的情况下):
▪ Logarithmic algorithm – O(logn) – Binary Search.
▪ Linear algorithm – O(n) – Linear Search.
▪ Superlinear algorithm – O(nlogn) – Heap Sort, Merge Sort.
▪ Polynomial algorithm – O(n^c) – Strassen’s Matrix Multiplication, Bubble Sort, Selection Sort, Insertion Sort, Bucket Sort.
▪ Exponential algorithm – O(c^n) – Tower of Hanoi.
▪ Factorial algorithm – O(n!) – Determinant Expansion by Minors, Brute force Search algorithm for Traveling Salesman Problem.
运行时分析的数学示例:
随着n(输入大小)变大,不同顺序算法的性能(运行时)迅速分离。让我们考虑一下数学示例:
If n = 10, If n=20,
log(10) = 1; log(20) = 2.996;
10 = 10; 20 = 20;
10log(10)=10; 20log(20)=59.9;
102=100; 202=400;
210=1024; 220=1048576;
10!=3628800; 20!=2.432902e+1818;
算法的内存占用量分析
对于算法的性能分析,运行时测量不仅是相关的指标,而且我们还需要考虑程序的内存使用量。这被称为算法的内存占用量,简称为空间复杂度。
同样,在这里,我们需要测量和比较算法的最坏情况理论空间复杂度,以进行性能分析。
它基本上取决于以下两个主要方面:
- 首先,程序的执行负责内存的使用。例如,我们可以假定递归实现始终比特定问题的相应迭代实现保留更多的内存。
- 另一个是n,即每个项目的输入大小或所需的存储量。例如,与输入量少的复杂算法相比,输入量大的简单算法会消耗更多的内存。
内存足迹分析的算法示例:以下是根据最坏情况,从最佳性能到最差性能(空间复杂度)对示例进行分类的算法:
▪ Ideal algorithm - O(1) - Linear Search, Binary Search,
Bubble Sort, Selection Sort, Insertion Sort, Heap Sort, Shell Sort.
▪ Logarithmic algorithm - O(log n) - Merge Sort.
▪ Linear algorithm - O(n) - Quick Sort.
▪ Sub-linear algorithm - O(n+k) - Radix Sort.
时空权衡与效率
通常,在最佳内存使用和运行时性能之间需要权衡取舍。
通常,对于一种算法,空间效率和时间效率在两个相对的末端达到,并且它们之间的每个点都具有一定的时间和空间效率。因此,时间效率越高,空间效率就越低,反之亦然。
例如,Mergesort算法非常快,但是需要大量空间才能进行操作。另一方面,气泡排序非常慢,但需要的空间最小。
在本主题的最后,我们可以得出结论,找到一种算法,该算法运行时间较短,并且对内存空间的需求也较小,可以对算法的性能产生巨大的影响。