定义:
算法是为了求解一个问题需要遵循的、被清楚指定的简单指令的集合,对于每一个问题,一旦某种算法给定并且(以某种方式)被确定是正确的,那么重要的一步就是确定该算法将需要多少诸如时间或空间资源的问题,如果一个问题的求解算法竟然需要长达一年时间,那么这种算法 很难有什么用处,同样要是一个算法需要若干个GB的内存,那么在当前大多数机器上也是无法使用的
算法分析 一般说来,估计算法资源消耗所需是一个理论问题,因此需要一套正式的系统架构,先从几个数学定义开始学起
下面的定义的目的是在函数间建立起一种相对的级别。给定的两个函数。通常存在一些点,在这些点上一个函数的值小于另一个函数的值,因此一般的宣称:
比如说 f(N)< g(N),是没有意义的,于是 我们比较他们的相对增长率,当将相对增长率应用到算法分析时,我们将明白为什么 它是重要的度量
定义1:如果存在正常数c和n0 使得当N>=n0时,T(N)<= cf(N),则记为T(N)= O(f(N)).
定义2:如果存在正常数c和n0使得当N>=n0时,T(N)>=cf(N),则记为T(N) = Ω(g(N)),
定义3:T(N) = Θ(h(N))当且仅当T(N) = O(h(N))和T(N) = Ω(h(N))。
定义4:如果对每一个正常数c都存在常数n0使得当N>n0时T(N)<cp(N),则T(N)=o(p(N)),有时也可以说如果T(N)= O(p(N))且 T(N) != Θ(p(N))
则 则T(N)=o(p(N));
解释:虽然对于较小的N值 1000N 要比N*N(N的平方)要大,但是N*N以更快的速度增长,因此N*N 最终一定是更大的函数,在这种情况下N =1000 是转折点,
定义1的意思是说:最后总会存在某个点n0从它以后的cf(N)总是至少与T(N)一样大。在我们的例子中 T(N) = 1000N,f(N)=N*N,n0=1000,而c=1,也可以让n0 = 10,c = 100,因此可以说 1000N = O(f(N))(N的平方)(N平方级),这种计法称之为大O 标记法,人们常常不说....级的,而是说大O...
如果用传统的不等式来计算增长率,那么定义1 说的是T(N)的增长率小于或等于f(N)的增长率 则记为 T(N) = O(f(N));
定义2 的意思是说 在一定范围以后 T(N)的增长率大于或者等于 g(N)也就俗称的T(N) = T(N) = Ω(g(N));
定义三的意思就是T(N)的增长率等于f(N)的增长率;
定义四的意思是 T(N)的增长率小于p(N)的增长率 它不同于大O 大O是有相等的可能性;
通常证明 T(N)= O(f(N));通常不是形式的使用这些定义,而是使用一些已知的结果;这就意味着证明或者确定假设不成立是非常简单的计算而不应涉及微积分,除非遇到特殊的情况(不可能在算法分析中发生);
当T(N)= O(f(N))时,我们是在保证函数T(N)是在以不快于f(N)的速度增长,因此f(N)是T(N)的一个上界;这也就意味着T(N)=Ω(f(N)),T(N)是f(N)的一个下界,
作为一个例子
f(N) = N*N*N g(N) =N*N ;f(N)比g(N) 增长速度快,因此我们可以说 f(N) = Ω(g(N));g(N) = O(f(N));
f(N) = N*N 和 f(N) = 2*N*N 的增长速度是相同的,从而 我们说 f(N) = O(g(N)) 并且f(N) = Ω(g(N)),当两个函数的增长速度相同时,是否需要使用记号 Θ来表示可能依赖于上下文,直观的说 g(N) = 2*N*N,那么g(N) = O(N*N*N*N),g(N) = O(N*N*N),和g(N) = O(N*N) 从技术上看都是成立的,但最后一个是最佳选择,写法g(N) = Θ(N*N) 不仅表示g(N) = O(N*N) 而且还可以表示g (N) = Ω(N*N) 结果尽可能的好(严肃一些)
法则1:
如果T1(N) = O(f(N))且 T2(N) = O(g(N)),那么
T1(N) +T2(N) = O(f(N)+g(N)) 直观的非正式的可以写成max(O(f(N)) , O(g(N)) );
T1(N)*T2(N) = O(f(N) * g(N) );
法则2:
如果T(N)是一个k次多项式 那么T(N) = Θ (N*N*N*...)
法则3:
对于任意常数k log k N = O(N) 它告诉我们 对数增长的非常缓慢,
有几点需要注意的是:
首先 将常数和低阶项 放进大O是非常坏的习惯,例如 不要写成 T(N) = O(2*N*N) 或 T(N) = O(N*N+N),在这两种情况下。正确的形式是T(N)=O(N*N),这就是说在需要大O表示的任何分析中,各种简化都是可能发生的,低阶项一般可以被忽略。而常数也可以弃掉,此时要求的精度非常粗糙,
函数 名称 c 常数 logN 对数 logN*logN 对数平方的 N 线性的 NlogN N*N 二次的 N*N*N 三次的 2N(2的n次方) 指数的 典型的函数增长率 越来越快
通常 两个函数f(N)和g(N)见得关系用简单的代数方法就可以得到,例如 f(N) = Nlog(N) 和 g(N) = N1.5(N的1.5次方),那么为了比较 他们两个那个增长率更快 实际就是比较 logN 和 N0.5 的增长速度快,这于确定 logN*logN N0.5*N0.5 那个增长速度更快是一样的 后者增长速度是明显比前者快的
另外要注意的是 不要写成 f(N)<=O(g(N)),因为定义已经隐含不等式了,
作为所执行的典型分析的例子,考虑在互联网上下载文件的问题,设有初始3s的延迟(来建立连接),伺候下载的速度以1.5Kb/s的速度进行,可以推出,如果文件为N个KB,那么下载时间由公式T(N)= N/1.5+3 来表示,这是一个线性函数,注意下载一个文件大小为1500Kb大小的文件所用时间大约是1003s(近似但不精确)为下载文件大小为750kb所用时间503s的两倍,这是典型的线性函数,还要注意的是如果连接的速度快了两倍,那么两种时间都要减少,但是1500
kb的文件仍然要花费大约下载750kb文件的两倍的时间,这是线性时间算法的典型特点,这也就是为什么我们写T(N) = O(N)而忽略常数因子的原因,