算法分析
在现在这个信息爆炸的时代,处理数据的量也越来越大。所以人们在用计算机来解决日常生活生产的问题的时候难免会有这样的疑问。
我的程序会运行多长时间?
我的程序会耗多少的内存?
这次我们就来简单讨论一下第一个“我的程序会运行多长时间?”。
运行时间
要知道一个程序的运行时间,最简单的方法就是计时啦,简单粗暴。
但是这自然要问,我不可能每一次都用个秒表去测量吧,而且程序的运行时间会受到多种因素的干扰(操作系统,计算机的性能参数等等),所以就算用秒表测量得出的数值也不具有什么意义。
所以自然我们要用一种相对的测量方法,我们可以通过计算一个对象被操作的次数来计算程序相对的运行时间。
下面我们分析一个简单的例子,看看怎样来估计程序的运行时间
public int sum(int[] a,int[] b){
int sum=0;
for(int i =0; i<a.length; i++){
for(int j=0; j<b.length; j++){
sum=sum+a[i]+b[j];
}
}
return sum;
}
分析上面的求和代码,我们可以看到a中的每一个元素与每一个b中的元素求和,假设数组的长度(a.length)比较大,而且两数组长度近似相等(n=a.length=b.length)。那么明显每个数组被访问的次数为
n2
,两个数组就是
2n2
。现在我们假定运行时间就是输入数据的规模大小(n)的函数:
T(n)=2n2
。而且事实也的确是这样。
也可以用大O记法,记作
O(n2)
(通常忽略低阶项和常数项),叫作时间复杂度。
这样就给出了一个我们估计程序运行时间的相对参考量,虽然并不能非常准确的计算程序的运行时间。但是至少也给出来程序的运行时间和输入数据的规模的函数变化规律。就像上面的例子运行时间跟输入数据的规模成平方增长。
常用算法的时间复杂度
上图取自维基百科,上面列出了常用的算法的时间复杂度列表。
数学定义
在最后给出算法分析的数学基础,数学定义。
大O记号
O(g(n))={ f(n): 存在正常数c和n0,使所有n>n0,有0≦f(n)≦c*g(n)}
上面的定义说明了O(g(n))是一个函数集合,集合里面的函数满足g(n)的常数倍是函数f(n)的一个上界,即f(n)有上界。
Ω记号
Ω(g(n))={ f(n):存在正常数c和n0,使所有n>n0,有0≦ c*g(n)≦f(n)}
上面的定义说明集合里面的函数满足g(n)的常数倍是函数f(n)的一个下界,即f(n)有下界。
Θ记号
Θ(g(n)),当且仅的,f(n)属于O(g(n))和Ω(g(n))。
可见Θ是更强的形式。Θ蕴含大O和Ω。
有了这个数学基础后就可以证明当分析程序的成本的时候,大多时候可以忽略掉低次项和常数相。
总结
当然算法是一门很大的学科,绝不仅只有这些内容。如果各位同学有兴趣可以看一下相关的教材。