估算算法的效率时的数学思想
作为程序员,我们可以凭直觉得出一个算法的效率高低。但是在算法研究中,我们需要较为准确的数学概念来描述算法的效率。
这里介绍一点相关的数学知识,非常非常的简单。
仅考虑相对速度
首先,当进行算法分析时,我们必须用抽象的方法,把问题简化。例如不考虑程序运行所在的机器效率问题等和环境相关的因素。
因此我们在讨论两个算法效率高低时,考虑的是相对速度,而不是绝对速度。
衡量一个算法的效率时,我们考虑的是消耗最大的那类操作
例如有两个长度一样的数组,同时都已经排序完毕了:
A :[1,1,4,5]
B:[2,4,6,8]
现在我们要做的是将这两个数组合并成一个数组,同时是排序好的。
具体做法是:我们分别比较这两个数组的开头1和2,把较小的元素放入输入的数组C,同时把它从原数组删除。于是现在C为[1],A为[1,4,5],B不变。
接着我们继续比较A和B的第一项[1]和[2],同样C变为[1,1],A为[4,5],B仍然不变。
继续比较[4]和[2],此时C为[1,1,2], A为[4,5],B为[4,6,8].
如此反复,当A,B之一的长度变为0时,比较结束,得到完全排序好的C数组。
通常的想法是,这个过程读取了2n次数组的变量(A和B的总元素),写入了2n次变量(C的总元素),删除了变量2n次(将A和B中的元素慢慢的删除到0),比较变量2n-1次(最坏的情况下)。如果这样精确的计算每个算法的效率,问题就会极大的复杂化。
因此在算法分析中,这个算法最大开销的操作是2n
在后面可以看到这个算法的复杂度是O(N).
使用渐进公式,大O表示法
例如:一个算法所花费的时间为:
T(N)=3N^3+2N^2+700 (n为输入的元素个数,例如一个数列包含的元素)
这里由于n的3次方比比n的2次方高阶,因此
1,保留最高阶的项,3n^3,扔掉其他所有项。
2,去掉这个最高阶项前面的常数3。
3,最后得到n^3。此时我们认为,这个算法的时间复杂度为n^3. 记做O(N^3)
这种估算的思想很容易理解,当n趋近于无穷时,最高阶的项对于最终的结果起了决定性作用。相对于n^3,n^2的影响可以忽略不计了。
因此我们去掉所有不那么重要的因素,留下最重要的因素,这个思想就称为渐进算法。同时我们认为时间复杂度为n^2的算法优于时间复杂度为n^3的算法。
一个实际的应用:常见的冒泡排序法完成需要读取数列里的变量n*(n-1)次,从上面的方法我们可以得出,冒泡算法的时间复杂度为:
O(N^2)
在后面我的blog中可以看到插入排序算法的时间复杂度也是O(N^2)。而插入算法的时间复杂度是O(N*logN)。这里需要注意的一点是,log是对数符号,而对数是有底数的。我们在这里看到的都没有底数是因为在大O表示法中,对数的底数也被认为是次要因素而被忽略了。例如,我们知道用二分法查找有序数组的次数是:以2为底,n的对数。但是大O表示法记做O(logN)。这里就将底数2忽略了。
由此我们可以得出这两种算法的效率差不多(记住在这里的数学思想,在特定的情况下,这个判定不一定成立。例如在输入的数组很短时,可能插入算法效率更高,但是在做算法分析时,从数学的角度分析,我们认为它们效率差不多)。
例如merge sort的时间复杂度为O(NlogN)而insert sort的时间复杂度为O(N^2)。我们认为merge sort优于insert sort,而实际情况是,当需要排列的数组很小的时候,insert sort效率很高,但是当数组元素达到几十个时,merge sort就开始领先于insert sort,而这种优势随着数组长度的增加越发明显。