一、衡量算法好坏的因素
1、主要因素
衡量一个算法好坏的因素主要有两个:时间复杂度和空间复杂度。
注意,这里是好坏,而不是执行时间上的快慢。
本文,我们主要讲时间复杂度。
2、影响时间运行的因素
高级程序语言(java、c++等语言)在计算机运行所消耗的时间取决于以下因素。
a、算法采用的策略、方法。(指具体的算法)
b、编译产生的代码质量。
c、问题的输入规模
d、机器执行指令的速度
上述 b 由软件支持,d由硬件性能支持。也就是说,一个程序的运行时间,主要依赖 a和c.
二、算法时间计算的方式和涉及到的相关概念
1、输入规模
输入规模的最佳的概念,依赖于研究的问题。
如:对于一个待排序的的数组,其输入规模为其数组的长度n.
如果过说算法的输入是一个图,则输入规模可以使用该图的顶点数和边数。
最终结论,也就是说,输入规模没有固定度量方式,而是需要根据具体的算法,来分析出具体的输入规模。
2、运行时间
通常,不同算法的运行时间,描述成其输入规模的函数。
算法在特定输入的运行时间,是指执行的所有的基本操作数的总时间,执行一个基本操作所耗费的时间,可以理解为执行一行代码所耗费的时间。
算法运行时间的计算示例:
/**
* 下面这只是个示例代码,并不是一个解决实际问题的算法。
* 输入规模 n
* 总时间: t0*n+t1*n+t2*n=(t0+t1+t2)*n
* 所以,最终执行时间可以看成一个跟输入规模有关的函数。
* f(n)=(t0+t1+t2)*n
* 由于t0,t1,t2均为常数,这里简化成
* f(n)=a*n //a为t0,t1,t2的和。
*/
public void sample(){
int n =10;
int x=0;
for (int i = 0; i < n; i++) { //for循环表达式单次执行时间为 t2,总共会执行n次
x++;//执行时间为 t0
x=x+1+2+3;//执行时间为t1.
}
}
通过上面的例子,我们可以得出一个算法最终执行时间可以看成一个跟输入规模有关的函数。
上面只是个简单的例子,其它算法的时间计算思路和上面的类似。
3、函数运行时间的增长率和增长量级
- 对于输入规模较大的问题,我们要计算并比较不同算法之间的具体时间的长短,无疑是灾难性的且无法实现的。
- 那么我们还有没有办法比较两个算法时间上的优劣性?答案是,当然有。这时候,我们引入函数的运行时间的增长率和增长量级的概念。
- 增长率基于解决同一问题的两个算法之间的比较。如果解决同一问题的两个算法 A1和A2的运行时间分别是 c*n和 n^2(^2为平方),c为常数。随着输入规模n的越来越大,A2的时间增长的速度肯定会越来比A1块。这时,我们就能用增长率来衡量两个算法的效率(除了输入规模非常小的时候),增长率可以理解为随着输入规模的增大,函数运行时间的增长速度。
- 如果一个两个算法在最坏运行时间的情况下,比另一个具有更低的增长量级,我们就认为前者比后者更有效率。
- 增长量级,是指在某个具体输入规模的时候,基于输入规模的时间函数,省略掉了其中一些不重要的项,只保留了主要项目的的值。这个时候,并不是真正的算法的函数时间,我们就叫他函数的增长量级。(如 n 、n^2、logN 等常用的增长量级)
三、时间复杂度
1、到底什么是时间复杂度
网上大多数文章,提到时间复杂度,往往都直接提到的是用大O标记法得出的一个等式,如f(n)=O(n^2)。很少有人能给出一个准确的让人一看就懂的定义。下面,我们一步步分析,到底什么是时间复杂度。
2、渐进记号
渐进记号用来修饰函数,被修饰的函数是一个算法执行时间保留最重要的项得出的。被渐近记号修饰的函数,和渐进记号一起代表了一个函数的集合
Θ记号
O记号
Ω记号
我们根据一个算法具体的时间,只保留高阶项,同时省略高阶项的系数,得到的一个函数,这个函数被不同的渐进记号修饰下的含义,可以理解为算法的时间复杂度。
如:
一个算法,基于数据规模n得到的时间的计算公示为 an^2+bn+c,其中a、b、c均为正常数,则只保留重要项后为n^2.这个时候,用不同的记号修饰,将得到不同的时间复杂度。
Θ(n^2) 包括了处于最好与最坏情况之间的函数。
O(n^2) 包括时间的上界,也就是说这个集合的函数里面的函数,最坏不会超过c*n^2,c是一个常数
Ω(n^2) 包括了时间的下界,也就是说这个集合的函数里面的函数,都要比超过c*n^2用的时间长,c是一个常数。
所以,一般我们用大O标记法来衡量算法的效率,因为大O标记法,代表了某个算法最坏情况下的时间。
四、关于时间复杂度极容易混淆的一些思路和概念
1、对比基准:
时间复杂度的好坏,一定是基于解决同一问题的不同算法,在相同的输入规模的情况下比较得出的结果。
2、最坏时间和最优时间的参照点
在同一问题的不同算法之间,我们一般用O表示运行时间的一个上界,用Ω来表示运行时间的一个下界。也就是说,参照点是解决同一问题的不同算法。
在同一个算法里面,根据不同的输入(如:插入排序输入的数组,有的已经排序好了,有的完全是逆序),在相同的输入规模下,得到时间量级的表达式有可能是n,也有可能是n^2。在同一个算法同一个输入规模,不同输入得到的时间增长量级的函数,不能用渐进记号来修饰,因为这是同一算法的不同情况,并不是两个算法的比较。
3、算法的时间复杂度,只是理论上用于多个算法执行效率比较的.
五、大O复杂度的情况下,一些常用函数增长量级间的比较
参考资料:《算法导论》